/*Flexible LCD HD44780 Driver Module*/
/*     Written by Sergey Kozlov     */
/*          uk8amk@mail.ru          */

/// LCD ports configuration ///
/// Output pins definitions ///
#define RS PORTA.4
#define RW PORTA.5
#define E  PORTA.6

#define DB4 PORTC.4
#define DB5 PORTC.5
#define DB6 PORTC.6
#define DB7 PORTC.7
/// Input pins definitions///
/// (bits locations must be the same as outputs) ///
#define DB4I PINC.4
#define DB5I PINC.5
#define DB6I PINC.6
#define DB7I PINC.7
/// Data directions ///
#define RS_DR DDRA.4
#define RW_DR DDRA.5
#define E_DR  DDRA.6

#define DB4_DR DDRC.4
#define DB5_DR DDRC.5
#define DB6_DR DDRC.6
#define DB7_DR DDRC.7
///////////////////////

/// LCD parameters ///
#define lcd_max_x 16//number of chars in one row
#define lcd_max_y 2 //number of rows
//////////////////////

void lcd_init(void);
void lcd_init_write(char cmd);
void lcd_write_command(char cmd);
void lcd_write_data(char data);
void lcd_clear(void); 
void lcd_gotoxy(unsigned char x, unsigned char y); 
void lcd_send_data(char dta);
char lcd_read_byte(void);
char lcd_read_BF(void);
void define_char(flash char *pc,unsigned char char_code);

#include <delay.h>

unsigned char lcd_x_cnt=0;
unsigned char lcd_y_cnt=0;
static unsigned char _base_y[4]={0x80,0xc0};


void lcd_init(void)
{
E=0;
RW=0;
RS=0;

E_DR=1;//set outputs
RW_DR=1;
RS_DR=1;

DB4=0;//Set high impendance inputs
DB5=0;
DB6=0;
DB7=0;
DB4_DR=0;
DB5_DR=0;
DB6_DR=0;
DB7_DR=0;

delay_ms(50);//wait for LCD internal initialization

_base_y[2]=lcd_max_x+0x80;
_base_y[3]=lcd_max_y+0xc0;
lcd_init_write(0x30);
lcd_init_write(0x30);
lcd_init_write(0x30);
lcd_init_write(0x20);//set 4-bit mode
lcd_write_command(0x28);//4 bit mode, 2 strings
lcd_write_command(0x04);//AC=decrement, S=no shift screen
lcd_write_command(0x85);

lcd_clear();
}

/// Clear LCD ///
void lcd_clear(void)
{
        lcd_write_command(0x02);
        lcd_write_command(0x0C); 
        lcd_write_command(0x01);
}

/// Print single char ///
void lcd_putchar(char chr)
{
lcd_write_data(chr);
if(++lcd_x_cnt==lcd_max_x)
        { 
        lcd_x_cnt=0;
        if(++lcd_y_cnt==lcd_max_y) lcd_y_cnt=0;
        lcd_gotoxy(0,lcd_y_cnt);
        }
}

/// Set cursor position x y ///
void lcd_gotoxy(unsigned char x, unsigned char y)
{ 
        lcd_write_command(_base_y[y]+x);
        lcd_x_cnt=x;
        lcd_y_cnt=y;
}

// write the string str located in SRAM to the LCD
void lcd_puts(char *str)
{
char k;
while (k=*str++) lcd_putchar(k);
}

// write the string str located in FLASH to the LCD
void lcd_putsf(char flash *str)
{
char k;
while (k=*str++) lcd_putchar(k);
}

/// Write higher nibble in 8-bit mode on init stage ///
void lcd_init_write(char cmd)
{ 
E=0;
RW=0;//write mode
RS=0;//command mode

DB4_DR=1;//set outputs
DB5_DR=1;
DB6_DR=1;
DB7_DR=1;
delay_us(1);

DB4=cmd&16;//out higher nibble
DB5=cmd&32;
DB6=cmd&64;
DB7=cmd&128;
E=1;
delay_us(1);
E=0;
delay_ms(10);
}                                                   

/// Send to command register ///
void lcd_write_command(char cmd)
{    
RS=0;//command mode
lcd_send_data(cmd);            
} 

/// Send to data register ///
void lcd_write_data(char data)
{    
RS=1;//data mode
lcd_send_data(data);          
}

/// Send prepared data ///
void lcd_send_data(char dta)
{
E=0;
RW=0;//write mode

DB4_DR=1;//set outputs
DB5_DR=1;
DB6_DR=1;
DB7_DR=1;             

delay_us(1);

DB4=dta&16;//out higher nibble
DB5=dta&32;
DB6=dta&64;
DB7=dta&128;
E=1;//latch-up
delay_us(1);
E=0;

DB4=dta&1;//out lower nibble
DB5=dta&2;
DB6=dta&4;
DB7=dta&8;
E=1;
delay_us(1);
E=0;

DB4_DR=0;//set high impendance/deactivate
DB5_DR=0;
DB6_DR=0;
DB7_DR=0;

//delay_ms(10);   
while(lcd_read_BF());//wait when LCD will be ready
}

/// Read busy flag ///
char lcd_read_BF(void)
{                     
RS=0;
return (lcd_read_byte() & 128);//return BF
}
 
/// Read single byte from LCD port ///
char lcd_read_byte(void)
{
char data=0;
E=0;
RW=1;//read LCD sygnal

DB4_DR=0;//all inputs
DB5_DR=0;
DB6_DR=0;
DB7_DR=0;
DB4=0;//high impendance
DB5=0;
DB6=0;
DB7=0;                 
delay_us(1);

E=1;//read high nibble
delay_us(1);
if(DB4I) data=16;
if(DB5I) data|=32;
if(DB6I) data|=64;
if(DB7I) data|=128;
E=0;
delay_us(1);

E=1;//read low nibble
delay_us(1);
if(DB4I) data|=1;
if(DB5I) data|=2;
if(DB6I) data|=4;
if(DB7I) data|=8;
E=0;

RW=0;
return data; 
}

/* function used to define user characters */
/// You should execute lcd_gotoxy() command ///
/// after defining all your chars           ///        
void define_char(flash unsigned char *pc, unsigned char char_code)
{
unsigned char i,a;
a=(char_code<<3) | 0x40;
for (i=0; i<8; i++) 
        {
        //RS=1; 
        lcd_write_command(a++);
        lcd_write_data(*pc++);
        };
}

