/*Flexible LCD HD44780 Driver Module*/
/*     Written by Sergey Kozlov     */
/*          uk8amk@mail.ru          */

/// LCD ports configuration ///
/// Output pins definitions ///
#define RS PORTC.4
#define RW PORTB.2
#define E  PORTC.5

#define DB4 PORTC.0
#define DB5 PORTC.1
#define DB6 PORTC.2
#define DB7 PORTC.3
/// Input pins definitions///
/// (bits locations must be the same as outputs) ///
#define DB4I PINC.0
#define DB5I PINC.1
#define DB6I PINC.2
#define DB7I PINC.3
/// Data directions ///
#define RS_DR DDRC.4
#define RW_DR DDRB.2
#define E_DR  DDRC.5

#define DB4_DR DDRC.0
#define DB5_DR DDRC.1
#define DB6_DR DDRC.2
#define DB7_DR DDRC.3
///////////////////////

/// LCD parameters ///
#define lcd_max_x 16//number of chars in one row
#define lcd_max_y 2 //number of rows
//////////////////////

// win1251 - LCD translation table ///////////
const unsigned char TransTable[] =
{0x41,0xA0,0x42,0xA1,0xE0,0x45,0xA3,0xA4,				//C0-C7		À,Á,Â,Ã,Ä,Å,Æ,Ç
0xA5,0xA6,0x4B,0xA7,0x4D,0x48,0x4F,0xA8,				//C8-CF		È,É,Ê,Ë,Ì,Í,Î,Ï
0x50,0x43,0x54,0xA9,0xAA,0x58,0xE1,0xAB,				//D0-D7		Ð,Ñ,Ò,Ó,Ô,Õ,Ö,×
0xAC,0xE2,0xAD,0xAE,0x62,0xAF,0xB0,0xB1,				//D8-DF		Ø,Ù,Ú,Û,Ü,Ý,Þ,ß
0x61,0xB2,0xB3,0xB4,0xE3,0x65,0xB6,0xB7,				//E0-E7		à,á,â,ã,ä,å,æ,ç
0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0x6F,0xBE,				//E8-EF		è,é,ê,ë,ì,í,î,ï
0x70,0x63,0xBF,0x79,0xE4,0x78,0xE5,0xC0,				//F0-F7		ð,ñ,ò,ó,ô,õ,ö,÷
0xC1,0xE6,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7};				//F8-FF		ø,ù,ú,û,ü,ý,þ,ÿ
///////////////////////////////////////////////

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)
{
if (chr==0xA8) chr=0xA2;							//¨
if (chr==0xB8) chr=0xB5;							//¸
if (chr > 0xBF) chr = TransTable [(chr-0xC0)];		//âñå îñòàëüíûå áóêâû
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++);
        };
}

