/* lcd.c LCD Routines for Orangutan/WinAVR. Runs on either the ATMega8 or ATmega168 versions. Substantially modified from a post on the Pololu forum. The required delays included here are generously in accord with the LCD data sheet, and should be independent of the clock speed as long as F_CPU is set correctly (>= 1 MHz) Tested with compiler optimization -O0 and -O3, Added a routine -- LCDGotoXY (X,Y) -- which allows printing to any position on the display. X = character position (0-39), Y=line (0 or 1). Needed includes: , Jim Remington, sjames_remington at yahoo dot com */ //Function prototypes void LCDInit(void); void LCDSendData(unsigned char data); void LCDSendCommand(unsigned char command); void LCDPrintString(const unsigned char *string); void LCDGotoXY(unsigned char x,unsigned char y); #define F_CPU 8000000UL #include #include // useful defines (LCD commands) #define LCD_Clear 0x01 #define LCD_Line1 0x80 #define LCD_Line2 0xC0 /* Send lower 4 bits of data byte to display */ void LCDSendNibble( unsigned char data ) { data &= 0x0F; //lower 4 bits of data data <<= 3; PORTB &= ~0x38; //clear LCD bus bits 456 PORTB |= (data & 0x38); //or in data data <<= 1; PORTD &= ~0x80; //same for top bit on PORTD.7 PORTD |= (data & 0x80); _delay_us(1); PORTD |= (1<<4); //E = 1 _delay_us(2); //required minimum 1 us delay PORTD &= ~(1<<4); //E = 0 _delay_us(1); } /* Send a character to the LCD display */ void LCDSendData( unsigned char data ) { PORTD &= ~(1<<3); //R/W =0 PORTD |= (1<<2); //RS = 1; LCDSendNibble( data >> 4 ); LCDSendNibble( data ); _delay_us(100); //38 us typically needed to complete this action } /* Send a command to display. Required delay time depends on the command and the LCD controller clock frequency -- here assumed to be the minimum 190 kHz */ void LCDSendCommand( unsigned char command ) { PORTD &= ~(3<<2); // R/W=0, RS = 0; LCDSendNibble(command>> 4 ); LCDSendNibble(command); _delay_ms(3); //maximum required is 2.1 ms for "clear display" } // print a string constant void LCDPrintString( const unsigned char *str ) { while (*str != 0) LCDSendData( *str++ ); } // set print position to (x,y) where y=line number (0 or 1), x = character position 0, 1, etc. void LCDGotoXY(unsigned char x, unsigned char y) { volatile unsigned char ddram_addr; ddram_addr=0x80; //initialize data ram address to 0 (default) if (y==1) ddram_addr=0xC0; //start print at line 2, DDRAM address 0x40 LCDSendCommand(ddram_addr+ (x&0x7F) ); } /* Initialize the LCD Display, timing requirements taken from datasheet Set PORTB.3,4,5 to DB 4,5,6 Set PORTD.2,3,4,7 to RS, R/W, E and DB7 Send required start-up sequence to set 4 bit interface, 2 lines, 5x8 characters and clear display */ void LCDInit( void ) { DDRB |= (7<<3); DDRD |= (1<<7) | (7<<2); PORTD &= ~(7<<2); // E=0,R/W=0, RS = 0; _delay_ms(20); //required startup sequence from power-on (see datasheet) LCDSendNibble(0x03); //set interface=8 bits _delay_ms(10); //wait at least 5 ms LCDSendNibble(0x03); //set interface=8 bits _delay_ms(1); //wait at least 100 us LCDSendNibble(0x03); //set interface=8 bits _delay_ms(1); //wait at least 100 us LCDSendNibble(0x02); //set interface=4 bits _delay_ms(1); //delays after this are built into SendCommand LCDSendCommand(0x28); //set interface=4 bits, 2 lines, 5x8 characters LCDSendCommand(0x08); //display off, cursor off, blink off LCDSendCommand(0x01); //clear display LCDSendCommand(0x06); //entry mode set, cursor shifts right after character rcvd. LCDSendCommand(0x0D); //0b01DCB D=1:Display on, C=1:cursor on, B=1:Blink on }