//////////////////////////////////////////////////////////////////////////////////////////////
//	LCD library for 4 bit communication with LCD
//  supported LCD controller: NT3881 and compatible ones
//  Created May 5th, 2009 by Thomas Baier DG8SAQ, all rights reserved
//  Revision date 19.1.2013
//////////////////////////////////////////////////////////////////////////////////////////////
//	old ------------------
//	hardcoded LCD ports:
//	PortB LCD_RS	=>	RS
//	PortB LCD_E		=>	E
//	PortB5...7		=>	D4...D6
//	PortB0			=>	D7
//////////////////////////////////////////////////////////////////////////////////////////////
//  new ------------------
//	PortB6			=>	D4
//	PortD4...2		=>	D5...D7
//  PortD5			=>	RW
//	PortD6			=>	RS
//	PortB7			=>	E
//
//////////////////////////////////////////////////////////////////////////////////////////////

////////user settings
#define DDR_E	DDRB
#define PORT_E 	PORTB
#define BIT_E	7
#define LCD_E 	(1<<BIT_E)

#define DDR_RS	DDRD
#define PORT_RS PORTD
#define BIT_RS 	6
#define LCD_RS 	(1<<BIT_RS)

#define DDR_RW	DDRD
#define PORT_RW PORTD
#define BIT_RW 	5
#define LCD_RW 	(1<<BIT_RW)

#define	DDR_D4	DDRB
#define PORT_D4 PORTB
#define	BIT_D4	6
#define	LCD_D4	(1<<BIT_D4)

#define	DDR_D5	DDRD
#define PORT_D5 PORTD
#define	BIT_D5	4
#define	LCD_D5	(1<<BIT_D5)

#define	DDR_D6	DDRD
#define PORT_D6 PORTD
#define	BIT_D6	3
#define	LCD_D6	(1<<BIT_D6)

#define	DDR_D7	DDRD
#define PORT_D7 PORTD
#define	BIT_D7	2
#define	LCD_D7	(1<<BIT_D7)
////////end user settings

#define LCD_CLEAR 		1
#define LCD_CHOME   	2
#define LCD_ENTRY_MODE	4
#define LCD_COFF    	0x0c
#define LCD_CBLINK  	0x0f
#define LCD_CON      	0x0e
#define LCD_DDRAM   	0x80	//addresses: line 1: 0-3Fh, line 2:40h-7Fh
// Set CG RAM Address --------- 0b01xxxxxx  (Character Generator RAM)
#define LCD_SET_CGADR           0x40
 
#define LCD_GC_CHAR0            0
#define LCD_GC_CHAR1            1
#define LCD_GC_CHAR2            2
#define LCD_GC_CHAR3            3
#define LCD_GC_CHAR4            4
#define LCD_GC_CHAR5            5
#define LCD_GC_CHAR6            6
#define LCD_GC_CHAR7            7



#define LCD_SHORT_DELAY 60		//us, min 40us
#define LCD_LONG_DELAY 3		//ms, min 2ms


#define LCD_shortdelay() _delay_us(LCD_SHORT_DELAY)
#define LCD_longdelay() _delay_ms(LCD_LONG_DELAY)

#define LCD_DISPLAY_ON 		4
#define LCD_CURSOR_ON 		2
#define LCD_CURSOR_BLINK 	1

char LCD_bar[17];

						

void LCD_Strobe(void){
PORT_E=PORT_E & ~LCD_E;
_delay_us(1);
PORT_E=PORT_E | LCD_E;
}


void LCD_SendByte(unsigned char i){
unsigned char c;
c=((i >> 4) & 1) << BIT_D4;
PORT_D4=(PORT_D4 & ~LCD_D4) | c;
c=((i >> 5) & 1) << BIT_D5;
PORT_D5=(PORT_D5 & ~LCD_D5) | c;
c=((i >> 6) & 1) << BIT_D6;
PORT_D6=(PORT_D6 & ~LCD_D6) | c;
c=((i >> 7) & 1) << BIT_D7;
PORT_D7=(PORT_D7 & ~LCD_D7) | c;
LCD_Strobe();
LCD_shortdelay();
c=( i       & 1) << BIT_D4;
PORT_D4=(PORT_D4 & ~LCD_D4) | c;
c=((i >> 1) & 1) << BIT_D5;
PORT_D5=(PORT_D5 & ~LCD_D5) | c;
c=((i >> 2) & 1) << BIT_D6;
PORT_D6=(PORT_D6 & ~LCD_D6) | c;
c=((i >> 3) & 1) << BIT_D7;
PORT_D7=(PORT_D7 & ~LCD_D7) | c;
LCD_Strobe();
LCD_shortdelay();
}


void LCD_SendNibble(unsigned char i){
char c;
c= (i       & 1) << BIT_D4;
PORT_D4=(PORT_D4 & ~LCD_D4) | c;
c=((i >> 1) & 1) << BIT_D5;
PORT_D5=(PORT_D5 & ~LCD_D5) | c;
c=((i >> 2) & 1) << BIT_D6;
PORT_D6=(PORT_D6 & ~LCD_D6) | c;
c=((i >> 3) & 1) << BIT_D7;
PORT_D7=(PORT_D7 & ~LCD_D7) | c;
LCD_Strobe();
LCD_shortdelay();
}


void LCD_InitPort(void){
DDR_E  = DDR_E  | LCD_E;
DDR_RW = DDR_RW | LCD_RW;
DDR_RS = DDR_RS | LCD_RS;
DDR_D4 = DDR_D4 | LCD_D4;
DDR_D5 = DDR_D5 | LCD_D5;
DDR_D6 = DDR_D6 | LCD_D6;
DDR_D7 = DDR_D7 | LCD_D7;
PORT_RW = PORT_RW & ~LCD_RW;
}


void LCD_SetCommand(void){
PORT_RS=PORT_RS & ~LCD_RS;
}


void LCD_SetData(void){
PORT_RS=PORT_RS | LCD_RS;
}


void LCD_DisplayClear(void){
LCD_SendByte(0x01);
LCD_longdelay();					// wait 2ms
}


void LCD_Cur(char Position){//Position=Pos+(40h*(Lin-1))
LCD_SendByte(Position | LCD_DDRAM);
}


void LCD_HexByte(char b, char pos){//write b in hex to LCD Pos
LCD_SetCommand();
LCD_Cur(pos);
LCD_SetData();
char c=(b >> 4);
if (c>9) c=c+7; 
LCD_SendByte(c+'0');
c=(b & 0xf);
if (c>9) c=c+7; 
LCD_SendByte(c+'0');
}


void LCD_SetDisplay(char displaymode){
LCD_SendByte(8|(displaymode & 7));  // Init Display - Command: Display on/off
LCD_shortdelay();					// wait 40us 
}	


void LCD_Init(char displaymode){
LCD_SetCommand();
LCD_SendNibble( 0x3);				// set 8 bit mode	
LCD_shortdelay();                   // wait 40us
LCD_SendNibble( 0x3);				// set 8 bit mode again to be sure, for well defined entry
LCD_shortdelay();					// wait 40us 
LCD_SendNibble( 0x2);				// set 4 bit mode
LCD_shortdelay();					// wait 40us 
LCD_SetDisplay(displaymode);		// Init Display - Command: Display on/off
LCD_DisplayClear();					// Display Clear					
LCD_SetData();
}



void LCD_Goto(char pos, char displaymode){
LCD_SetCommand();
LCD_Cur(pos);
LCD_SetDisplay(displaymode);
LCD_SetData();
}


void LCD_OutNumber(unsigned char bcd[1], unsigned char len, unsigned char dec_place, unsigned char unit){
unsigned char i;
char b;
char dp=0;
char leadingzero=1;
if (dec_place==1) leadingzero=0;
len=len>>1;
for (i=0;i<len;i++)
  {
	if (dp==dec_place) LCD_SendByte('.');
	dp++;
	b=bcd[i]>>4;
	if (b>0) leadingzero=0;
	b=b+0x30;
	if (leadingzero==1) b=' ';
	LCD_SendByte(b);
	if (dp==dec_place) LCD_SendByte('.');
	dp++;
	b=bcd[i] & 0xF;
	if (b>0) leadingzero=0;
	b=b+0x30;
	if (leadingzero==1) b=' ';
	LCD_SendByte(b);
  }
if (dp==dec_place) LCD_SendByte('.');
LCD_SendByte(' ');
if (unit>0) LCD_SendByte(unit);
}


void LCD_OutStrP(const char *data, char pos){
LCD_SetCommand();
LCD_Cur(pos);
LCD_SetData();
while (pgm_read_byte(data) != 0x00) LCD_SendByte(pgm_read_byte(data++));
}


void LCD_OutStr(const char *data, char pos){
LCD_SetCommand();
LCD_Cur(pos);
LCD_SetData();
while ((*data != 0x00) && (*data != 0x0d) && (*data != 0x0a)) LCD_SendByte(*data++);
}


void LCD_OutChar(char c, char pos){
LCD_SetCommand();
LCD_Cur(pos);
LCD_SetData();
LCD_SendByte(c);
}

////////////////////////////////////////////////////////////////////////////////
// Schreibt ein Zeichen in den Character Generator RAM
 

void LCD_GenerateChar(uint8_t code, const uint8_t *data){
// Startposition des Zeichens einstellen
unsigned char i;
LCD_SetCommand();
LCD_SendByte(LCD_SET_CGADR|(code<<3));
LCD_SetData();
// Bitmuster übertragen
for (i=0; i<8; i++){
    LCD_SendByte(data[i]);
	}
}


void LCD_InitBar(void){
unsigned char i;
unsigned char x[8];
for (i=0;i<8;i++) x[i]=0b10000;
LCD_GenerateChar(1,x);
for (i=0;i<8;i++) x[i]=0b11000;
LCD_GenerateChar(2,x);
for (i=0;i<8;i++) x[i]=0b11100;
LCD_GenerateChar(3,x);
for (i=0;i<8;i++) x[i]=0b11110;
LCD_GenerateChar(4,x);
for (i=0;i<8;i++) x[i]=0b11111;
LCD_GenerateChar(5,x);
}


void LCD_BarStr(unsigned char x){
signed char i;
unsigned char n;
unsigned char r;
if (x>80) x=80;
n=x/5;
r=x-n*5;
LCD_bar[16]=0;
for (i=0;i<16;i++) LCD_bar[i]=' ';
if (x==0) return;
for (i=0;i<n;i++) LCD_bar[i]=5;
if (r==0) return;
LCD_bar[n]=r;
}


void LCD_Bar(unsigned char x, unsigned char line){
line=(line & 1)<<6;
LCD_BarStr(x);
LCD_OutStr(LCD_bar,line);
}


void LCD_Test(void){
LCD_InitPort();
LCD_Init(LCD_DISPLAY_ON | LCD_CURSOR_ON | LCD_CURSOR_BLINK);
LCD_SendByte('H');
LCD_SendByte('e');
LCD_SendByte('l');
LCD_SendByte('l');
LCD_SendByte('o');
LCD_SendByte(' ');
LCD_SendByte('W');
LCD_SendByte('o');
LCD_SendByte('r');
LCD_SendByte('l');
LCD_SendByte('d');
LCD_SendByte('!');
}



