/* ===========================================================================

    lcd.c  - LCD routines for Axiom CML12SDP256 and 912x boards

    Version:        1.0
    Author:         Dusty Nidey, Axiom Manufacturing (www.axman.com)
    Compiler:       GNU for 68HC11 & 68HC12 (www.gnu-m68hc11.org)

    This is freeware - use as you like

==============================================================================
------------------------------------------------------------------------------
*/

#include "..\ports_d256.h"
#include "lcd.h"

// RAM Variables
// -------------
unsigned char LCDBuf;   // holds data and status bits sent to LCD
unsigned char LCDStat;  // holds LCD status

#define LCD_DELAYTIME   0x200   // adjust this value Lower for quicker LCD performance, Higher if you're seeing garbage on the display

// simple delay loop, waits the specified number of counts
void LCD_delayu(unsigned int ucount){
    while(ucount > 0){
        --ucount;
        // look at your compiler output and count the number of cycles used.
        // add more of these if needed to fine tune the exact delay you want
        //        asm("nop");

    }
}
// simple delay loop, LCD_delayu() * LCD_DELAYTIME
void LCD_delaym(unsigned int mcount){
    while(mcount > 0){
        --mcount;
        LCD_delayu(LCD_DELAYTIME);
    }
}

// Simple Serial Driver (SPI) send byte
// sends data byte in global LCDBuf
// return received back value in global LCDStat
void LCDSend(){
    LCD_delaym(1);
    LCDStat = SPI0SR;   // clear status of SPI by reading it
    SP0DR = LCDBuf; // send byte

    do{ // wait for status flag to go high
        LCDStat = SPI0SR;
    }while(LCDStat < 0x80);

    LCDStat = SP0DR;    // receive value back
}

// writes 4 bit data to lcd port
void lcd_wr_4(char LCDdata){
    // merge lower 4 bits of LCDdata with upper 4 bits of LCDBuf (control bits)
    LCDBuf &= 0xF0;
    LCDBuf |= LCDdata;

    LCDBuf &= ~EN;  // enable low
    LCDSend();  // send the data
    LCDBuf |= EN;  // enable high
    LCDSend();  // send the data
    LCDBuf &= ~EN;  // enable low
    LCDSend();  // send the data
}

// same as above but with delay
void lcd_wr_4d(char LCDdata){
    lcd_wr_4(LCDdata);
    LCD_delaym(50);
}
// Lcd Write 8 bit Data , upper 4 bits first, then lower
void LCD_Write(unsigned char lcdval){
    lcd_wr_4(lcdval >> 4);      // send upper 4 bits
    lcd_wr_4(lcdval & 0x0F);    // send lower 4 bits
}
// write a COMMAND byte to the LCD
void lcd_cmd(unsigned char cmdval){
    LCDBuf &= ~RS; // clear RS to select LCD Command mode
    LCD_Write(cmdval);
    LCD_delaym(10);
}

/*----------------------------------------------------------------------------
LCDputch
--------
 Send a character to the LCD for display.

 INPUT:     datval      character to display
----------------------------------------------------------------------------*/
void LCDputch(unsigned char datval){
    LCDBuf |= RS; // set RS to select LCD Data mode
    LCD_Write(datval); // write a DATA byte to the LCD
}

/*----------------------------------------------------------------------------
LCDputs
-------
 Send a string of characters to the LCD.  The string must end in a 0x00

 INPUT:		sptr		pointer to the string
----------------------------------------------------------------------------*/
void LCDputs(char *sptr){
	while(*sptr){
        LCDputch(*sptr);
		++sptr;
	}
}

/*----------------------------------------------------------------------------
LCDInit
-------
 Initialize LCD port

 ----------------------------------------------------------------------------*/
void LCDInit(){

    //  Turn on Spi
    SPI0CR1 = 0x52;
    SPI0CR2 = 0x10; // enable /SS
    SPI0BR = 0x00;  // set up Spi baud clock rate
    LCDBuf = (WR + EN); // set WR and EN bits
    LCDSend();  // send status to LCD

    // Initialize LCD
    LCDBuf &= ~(RS + EN); // clear RS and EN bits (select lcd Command)
    LCDSend();  // send status to LCD

    // delay's are used because this lcd interface does not provide status
    LCD_delaym(50);

    // set to 4 bit wide mode
    lcd_wr_4d(3);   // send 3
    lcd_wr_4d(3);   // send 3
    lcd_wr_4d(3);   // send 3
    lcd_wr_4d(2);   // send 2

    lcd_cmd(0x2c);  // 2x40 display
    lcd_cmd(0x06);  // display and cursor on
    lcd_cmd(0x0e);  // shift cursor right
    lcd_cmd(0x01);  // clear display and home cursor
    lcd_cmd(0x80);

    LCDBuf = 0; // Reset Lcd states to rest
    LCDSend();  // send status to LCD
}

