// hd44780.h - HD44780 Instructions and Parameter definitions
//
// Copyright (C) 1997, Ian Harries & Imperial College, London, UK
//

/*
$Id: hd44780.h,v 1.3 2006/05/20 15:54:04 nemo Exp $
antics@nervousbot.us

ported for use with sdcc/gcc and various microcontrollers

this header is intended mainly for use with AVR and PIC 
(ATmega32 and p16f628a used in development) micros but 
should be easy to port to any system in any configuration.  
note that init() and wait_for_lcd() need to be modified 
for your micro as well to set the data direction register

Wiring Guide:
A7 = R/W 0->Write, 1->Read
A1 = E : 0->Not, 1->Enable
A0 = RS: 0->instruction, 1->data
B0-B7 = Data

20x4 Details:
http://ouwehand.net/~peter/lcd/lcd0.shtml

Changelog:
 + 18 May 2006 christopher pepe
  - initial updates for AVR use
  - retained PIC related code
  - added comments to show where to (un)comment for use with PIC/AVR
 
*/

//this block is specific to the micorcontroller setup
//change the masks to match your pinout
#define Data    PORTB		//8 bit data port
#define Data_read PINB 	//AVR input port b
#define Data_ddr DDRB	//data direction register	
//#define Control PORTA		// Control register
#define Control PORTD		// Control register for ATmega32 (porta is the ADC which is also used)
#define Control_ddr	DDRD	//date direction register


//Common Combos
#define RS_E_Low 0xfc
#define RW_RS_E_Low 0x7c

//             R/W            7   6   5   4   3   2   1   0 
//      ===============      === === === === === === === ===
#define Read 0x80		   // 1   .   .   .   .   .   .   .
#define Write 0x00		   // 0   .   .   .   .   .   .   .

//             RS             7   6   5   4   3   2   1   0 
//      ===============      === === === === === === === ===
#define Data_Register 0x01 // .   .   .   .   .   .   .   1
#define Ins_Register  0x00 // .   .   .   .   .   .   .   0

//      Enable                7   6   5   4   3   2   1   0 
//      ======               === === === === === === === ===
#define E_Low	 0xfd      // 1   1   1   1   1   1   0   1
#define E_High   0x02      // .   .   .   .   .   .   1   .
//this block is specific to the micorcontroller setup



//      HD44780 Instruction Set             DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0
//      =======================             === === === === === === === ===
#define Clear_Display                0x01 // 0   0   0   0   0   0   0   1
#define Return_Home                  0x02 // 0   0   0   0   0   0   1   *
#define Set_Entry_Mode               0x04 // 0   0   0   0   0   1  I/D  S
#define Set_Display                  0x08 // 0   0   0   0   1   D   C   B
#define Set_Cursor_and_Display_Shift 0x10 // 0   0   0   1  S/C R/L  *   *
#define Set_Function                 0x20 // 0   0   1   DL  N   F   *   *
#define Set_CGRAM_Address            0x40 // 0   1   A   A   A   A   A   A
#define Set_DDRAM_Address            0x80 // 1   A   A   A   A   A   A   A

//      HD44780 Parameters 
//      ==================
//      N.B. explicit values for EVERY corresponding parameter
//      ==== MUST be passed each time any instruction is used

//      Set_Entry_Mode
//      ==============
#define Decrement_Address            0x00 // .   .   .   .   .   .   0   .
#define Increment_Address            0x02 // .   .   .   .   .   .   1   .

#define Shift_Display_Off            0x00 // .   .   .   .   .   .   .   0
#define Shift_Display_On             0x01 // .   .   .   .   .   .   .   1

//      Set_Display
//      ===========
#define Display_Off                  0x00 // .   .   .   .   .   0   .   .
#define Display_On                   0x04 // .   .   .   .   .   1   .   .

#define Cursor_Off                   0x00 // .   .   .   .   .   .   0   .
#define Cursor_On                    0x02 // .   .   .   .   .   .   1   .

#define Blink_Off                    0x00 // .   .   .   .   .   .   .   0
#define Blink_On                     0x01 // .   .   .   .   .   .   .   1

//      Set_Cursor_and_Display_Shift
//      ============================
#define Cursor                       0x00 // .   .   .   .   0   .   .   .
#define Display_and_Cursor           0x08 // .   .   .   .   1   .   .   .

#define Left                         0x00 // .   .   .   .   .   0   .   .
#define Right                        0x04 // .   .   .   .   .   1   .   .

//      Set_Function
//      ============
#define Data_Length_4                0x00 // .   .   .   0   .   .   .   .
#define Data_Length_8                0x10 // .   .   .   1   .   .   .   .

#define One_Display_Line             0x00 // .   .   .   .   0   .   .   .
#define Two_Display_Lines            0x08 // .   .   .   .   1   .   .   .

#define Font_5x7                     0x00 // .   .   .   .   .   0   .   .
#define Font_5x10                    0x04 // .   .   .   .   .   1   .   .

#define Line2_Offset 0x40

#define putc Put_Data // same thing
#define lcd Put_Ins   // same thing

static void init(){
	
	//CMCON=7;	//PIC: turn comparators off
    
    // all are outputs to send commands to lcd
    //TRISB=0x00; //PIC
    Data_ddr=0xff; //AVR    

	//PORTA is all outputs although only A0 and A1 are used  
	//TRISA=0x00; //PIC
	Control_ddr=0xff; //AVR
	
	//clear ports
	Data=Control=0;
}

static void pause(void) { // dilute to taste
	int i,j;
    for (i=0; i<100;i++){
    	for(j=0;j<254;j++);
    }
}

static void wait_for_lcd(void){
	unsigned char busy=0x80;
	unsigned char cnt=0;
	
	//portb = inputs
	//TRISB = 0xff; //PIC	
	Data_ddr=0x00; //AVR

	Control &= RS_E_Low;	
	Control |= Read;
	
	//read Data until busy flag goes low
	//or cnt overflows
	while((busy & 0x80) != 0){//is lcd busy?	
		Control |= E_High;
		Control &= E_Low;
		
		/* PIC */
		//busy = Data;	//read Data for lcd status
		
		/* AVR */	
		busy = Data_read;	//read Data for lcd status		
		if(cnt++ > 254){busy = 0;} //or timeout
	}
		
	Control &= RW_RS_E_Low; //R/W, RS, E low	
	
	
	//portb = outputs
	//TRISB = 0x00; //PIC
	Data_ddr = 0xff; //AVR
}


static void Put_Ins(int Ins) {
    Data = Ins;
	Control &= RS_E_Low;
    Control |= E_High; //RS=0, E=1
	Control &= RS_E_Low;
	wait_for_lcd();
}

static void Put_Data(unsigned char Ch) {
    Data = Ch;
	Control &= RS_E_Low; //RS=E=0
	Control |= (Data_Register + E_High); //RS=E=1
	Control &= E_Low; //Data_Register stays high
	wait_for_lcd();
}

//doesn't seem to work with sdcc
static void puts(unsigned char *Str, unsigned char len) {
    unsigned int i;
    for (i = 0; i < len; i++){
        Put_Data(*Str++);        
        //pause(); //optional pause
    }
}


static void initlcd(void) {
    Put_Ins(Set_Function | Data_Length_8);
    Put_Ins(Set_Function | Data_Length_8);
    Put_Ins(Set_Function | Data_Length_8);
    Put_Ins(Set_Function | Data_Length_8 | Font_5x7 | Two_Display_Lines);
    Put_Ins(Set_Entry_Mode | Increment_Address | Shift_Display_Off);
    Put_Ins(Set_Display | Display_On | Cursor_Off | Blink_On);
    Put_Ins(Set_Function | Data_Length_8 | Font_5x7 | Two_Display_Lines);    
    Put_Ins(Clear_Display);

}
