// filename: lcd.c
// RealSYS KCO
#include "common.h"
#include "lcd.h"

#define H_LCD_RS	(GPIOA->BSRR = GPIO_Pin_0)
#define H_LCD_E	    (GPIOA->BSRR = GPIO_Pin_1)
/*
#define H_LCD_D4	(GPIOA->BSRR = GPIO_Pin_4)
#define H_LCD_D5	(GPIOA->BSRR = GPIO_Pin_5)
#define H_LCD_D6	(GPIOA->BSRR = GPIO_Pin_6)
#define H_LCD_D7	(GPIOA->BSRR = GPIO_Pin_7)
*/
#define H_LCD_D4	(GPIOC->BSRR = GPIO_Pin_6)
#define H_LCD_D5	(GPIOC->BSRR = GPIO_Pin_7)
#define H_LCD_D6	(GPIOC->BSRR = GPIO_Pin_8)
#define H_LCD_D7	(GPIOC->BSRR = GPIO_Pin_9)

#define L_LCD_RS	(GPIOA->BRR  = GPIO_Pin_0)
#define L_LCD_E	    (GPIOA->BRR  = GPIO_Pin_1)
/*
#define L_LCD_D4	(GPIOA->BRR  = GPIO_Pin_4)
#define L_LCD_D5	(GPIOA->BRR  = GPIO_Pin_5)
#define L_LCD_D6	(GPIOA->BRR  = GPIO_Pin_6)
#define L_LCD_D7	(GPIOA->BRR  = GPIO_Pin_7)
*/
#define L_LCD_D4	(GPIOC->BRR  = GPIO_Pin_6)
#define L_LCD_D5	(GPIOC->BRR  = GPIO_Pin_7)
#define L_LCD_D6	(GPIOC->BRR  = GPIO_Pin_8)
#define L_LCD_D7	(GPIOC->BRR  = GPIO_Pin_9)

// commands
#define LCD_CLEARDISPLAY 	0x01
#define LCD_RETURNHOME		0x02
#define LCD_ENTRYMODESET 	0x04
#define LCD_DISPLAYCONTROL  0x08
#define LCD_CURSORSHIFT 	0x10
#define LCD_FUNCTIONSET 	0x20
#define LCD_SETCGRAMADDR 	0x40
#define LCD_SETDDRAMADDR 	0x80

// flags for display entry mode
#define LCD_ENTRYRIGHT 	        0x00
#define LCD_ENTRYLEFT 		    0x02
#define LCD_ENTRYSHIFTINCREMENT 0x01
#define LCD_ENTRYSHIFTDECREMENT 0x00

// flags for display on/off control
#define LCD_DISPLAYON 		0x04
#define LCD_DISPLAYOFF 	    0x00
#define LCD_CURSORON 		0x02
#define LCD_CURSOROFF 		0x00
#define LCD_BLINKON 		0x01
#define LCD_BLINKOFF 		0x00

// flags for display/cursor shift
#define LCD_DISPLAYMOVE 	0x08
#define LCD_CURSORMOVE 	    0x00
#define LCD_MOVERIGHT 		0x04
#define LCD_MOVELEFT 		0x00

// flags for function set
#define LCD_8BITMODE 		0x10
#define LCD_4BITMODE 		0x00
#define LCD_2LINE 			0x08
#define LCD_1LINE 			0x00
#define LCD_5x10DOTS 		0x04
#define LCD_5x8DOTS 		0x00

BYTE _displayfunction = LCD_4BITMODE | LCD_2LINE | LCD_5x8DOTS;
BYTE _displaymode = LCD_ENTRYLEFT | LCD_ENTRYSHIFTDECREMENT;
BYTE _displaycontrol = LCD_DISPLAYON | LCD_CURSOROFF | LCD_BLINKOFF;  
BYTE _lcd_x=0,_lcd_y=0,_lcd_xlen=16,_lcd_ylen=2;

void lcd_pulseEnable(void) {
	L_LCD_E;
  	delay_us(2);    
	H_LCD_E;
  	delay_us(2);    // enable pulse must be >450ns
	L_LCD_E;
 	delay_us(40);   // commands need > 37us to settle
}

void lcd_write4bits(BYTE value) {
	if(value & 0x01) H_LCD_D4;
	else L_LCD_D4;
	if(value & 0x02) H_LCD_D5;
	else L_LCD_D5;
	if(value & 0x04) H_LCD_D6;
	else L_LCD_D6;
	if(value & 0x08) H_LCD_D7;
	else L_LCD_D7;
  	lcd_pulseEnable();
}

// write either command or data, with automatic 4/8-bit selection
void lcd_send(uint8_t value, uint8_t mode){
	if(mode) H_LCD_RS;
	else L_LCD_RS;
  	lcd_write4bits(value>>4);
  	lcd_write4bits(value);
}
void lcd_command(BYTE value){
	lcd_send(value,LOW);
}
void lcd_write(BYTE value){
	lcd_send(value,HIGH);
}
void lcd_setCursor(uint8_t col, uint8_t row){
  int row_offsets[] = { 0x00, 0x40, 0x14, 0x54 };
  if ( row > _lcd_ylen ) {
    row = _lcd_ylen-1;    // we count rows starting w/0
  }
  lcd_command(LCD_SETDDRAMADDR | (col + row_offsets[row]));
}

void lcd_clear(void){
  lcd_command(LCD_CLEARDISPLAY);  // clear display, set cursor position to zero
  delay_ms(2);  // this command takes a long time!
}

void	lcd_clear_line(char line){	/* 0=1st, 1=2nd, 2=3rd, 3=4th line */
int	i;
	lcd_gotoxy(0,line);
	for(i=0;i<_lcd_xlen;i++){
		lcd_write(' ');
		delay_us(40);
	}
}             

void lcd_home(void){
  lcd_command(LCD_RETURNHOME);  // set cursor position to zero
  delay_ms(2);  // this command takes a long time!
}

// Turn the display on/off (quickly)
void lcd_disp_off() {
  _displaycontrol &= ~LCD_DISPLAYON;
  lcd_command(LCD_DISPLAYCONTROL | _displaycontrol);
}

void lcd_disp_on() {
  _displaycontrol |= LCD_DISPLAYON;
  lcd_command(LCD_DISPLAYCONTROL | _displaycontrol);
}

void lcd_cursor_all_on(void){                                           
								/*        0 0 0 0 1    D            C             B        */
	lcd_command(0x0f);	/* display on_off : Disp=ON(1), Cursor=ON(1), Blink=ON(1) */
	delay_us(40);
}
void lcd_cursor_bar_on(void){                                           
								/*        0 0 0 0 1    D            C             B        */
	lcd_command(0x0e);	/* display on_off : Disp=ON(1), Cursor=ON(1), Blink=OFF(0) */
	delay_us(40);
}
void lcd_cursor_blk_on(void){                                           
								/*        0 0 0 0 1    D            C             B        */
	lcd_command(0x0d);	/* display on_off : Disp=ON(1), Cursor=ON(0), Blink=OFF(1) */
	delay_us(40);
}
void lcd_cursor_off(void){
								/*        0 0 0 0 1    D            C             B        */
	lcd_command(0x0c);	/* display on_off : Disp=ON(1), Cursor=OFF(0), Blink=OFF(0) */
	delay_us(40);
}   


void lcd_putc(char c){
	lcd_write(c);
	delay_us(1);
}

void lcd_puts(char *str){
char	c;
	while(TRUE){
		c = *str++;
		if(!c) break;
		lcd_write(c);
	}
}

void lcd_printf(const char *fmt,...){
char str[30];
va_list ap;
	va_start(ap,fmt);
	vsprintf(str,fmt,ap);
	va_end(ap);
	lcd_puts(str);
}

void lcd_hex2(BYTE b){
	lcd_putc(hex2asc(b>>4));
	lcd_putc(hex2asc(b));
}
void lcd_hex3(WORD w){
	lcd_putc(hex2asc(w>>8));
	lcd_putc(hex2asc(w>>4));
	lcd_putc(hex2asc(w));
}
void lcd_hex4(WORD w){
	lcd_putc(hex2asc(w>>12));
	lcd_putc(hex2asc(w>>8));
	lcd_putc(hex2asc(w>>4));
	lcd_putc(hex2asc(w));
}

void lcd_init(BYTE xlen, BYTE ylen){
GPIO_InitTypeDef GPIO_InitStructure;

	_lcd_xlen =	xlen;
	_lcd_ylen =	ylen;
	_lcd_x=0;
	_lcd_y=0;

    // PA0=LCD_RS, PA1=LCD_E
    // PA4=LCD_D4, PA5=LCD_D5, PA6=LCD_D6, PA7=LCD_D7
    
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
/*
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
*/
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7 | GPIO_Pin_8 | GPIO_Pin_9;
    GPIO_Init(GPIOC, &GPIO_InitStructure);

	L_LCD_RS;		// 0 out, LCD_RS
	L_LCD_E;		// 0 out, LCD_E
	L_LCD_D4;		// 0 out, LCD_D4
	L_LCD_D5;		// 0 out, LCD_D5
	L_LCD_D6;		// 0 out, LCD_D6
	L_LCD_D7;		// 0 out, LCD_D7

	delay_ms(50);

   lcd_write4bits(0x03);
   delay_ms(5); // wait min 4.1ms
   lcd_write4bits(0x03);
   delay_ms(5); // wait min 4.1ms
   lcd_write4bits(0x03);
   delay_ms(5); // wait min 4.1ms
   lcd_write4bits(0x02);
   delay_ms(5); // wait min 4.1ms

	_displayfunction = LCD_4BITMODE | LCD_2LINE | LCD_5x8DOTS;
	lcd_command(LCD_FUNCTIONSET | _displayfunction);
	delay_us(40);

  	_displaycontrol = LCD_DISPLAYON | LCD_CURSOROFF | LCD_BLINKOFF;  
  	lcd_disp_on();
	delay_us(40);

  	_displaymode = LCD_ENTRYLEFT | LCD_ENTRYSHIFTDECREMENT;
  	lcd_command(LCD_ENTRYMODESET | _displaymode);
	delay_us(40);

	lcd_clear();
}
