The LCD still seems to be a sticking point for many. I spent some time studying the documentation for the display actually used on the Orangutan – available at
uoxray.uoregon.edu/orangutan/Oran_LCD.pdf
and began to wonder why any of the posted routines ever worked! I haven’t seen an example yet where the initialization is done according to instructions in the data sheet.
I’ve posted a revised set of routines that have been tested on the older Orangutan using the ATMega8 (clock speeds 1 MHz and 8MHz) as well as the newer one using the ATMega168.
Download version with tabs from
uoxray.uoregon.edu/orangutan/lcd_or.c
also posted here for convenience.
/*
lcd_or.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: <util/delay.h>, <avr/io.h>
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 <avr/io.h>
#include <util/delay.h>
// useful defines (LCD commands)
#define LCD_Clear 0x01
#define LCD_Line0 0x80
#define LCD_Line1 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 2nd line, 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
}
int main(void)
{
unsigned char i,j;
LCDInit();
LCDPrintString("Hello,");
LCDSendCommand(LCD_Line1);
LCDPrintString("World!");
for(j=1;j<5;j++) _delay_ms(250); //wait 1 second
for(j=1;j<5;j++) _delay_ms(250); //wait 1 second
//wipe out the message just printed…
for (i=0;i<8;i++) {
LCDGotoXY(i,0); //target a character for destruction on line 0
for(j=1;j<5;j++) _delay_ms(250); //wait 1 second
LCDPrintString("."); //overwrite with a period
}
for (i=0;i<8;i++) {
LCDGotoXY(7-i,1); //same on line 1
for(j=1;j<5;j++) _delay_ms(250);
LCDPrintString(".");
}
LCDSendCommand(LCD_Clear);
LCDSendCommand(LCD_Line0);
LCDPrintString("Done!");
return 0;
}