Forum Menu




 


Log in Problems?
New User? Sign Up!
AVR Freaks Forum Index

Post new topic   Reply to topic
View previous topic Printable version Log in to check your private messages View next topic
Author Message
danni
PostPosted: Jan 16, 2011 - 06:48 PM
Raving lunatic


Joined: Sep 05, 2001
Posts: 2612


Tutorial for HD44780-LCD and similar.
Following an easy, universal and code saving example.

1.
There are different ways to control the LCD. You can use the 8bit mode with busy testing or the 4bit mode with busy waiting.

On busy waiting a character need about 50µs to be transmitted. But nobody was able to read a character within 50µs.
E.g. a digital meter display only up to 5 measurements per second, which was good readable. So a 200ms update rate was also an ergonomic value for our display.
Then e.g. for a 2*16 LCD this result in a CPU load of:
50µs * 32 / 200ms = 0.8%.
So the 4bit mode with busy waiting may cause no remarkable CPU load.
But it need only 6 IO-pins, so there is no reason to waste 5 IO-pins more on using the 8bit mode with busy testing.

Thus we need the following 6 LCD-lines connected to the AVR:
DB7, DB6, DB5, DB4, RS and E.
The RW-line must be connected to GND. Forget not the contrast input connected according to the datasheet of your LCD. On some LCD it can be connected to GND, but some others may need a negative voltage on it!
The DB3, DB2, DB1 and DB0 lines may be connected to GND.


2.
The next point was, that the LCD should use any 6 IO-pins, even if not on the same 8bit port. Since it make the pcb easier and allow still to use special functions (UART, I2C, timer, ...) of certain IO-pins.
To do so, we need a simple way to define single IO-pins.
This can be done easily with the following macro:
Code:

struct bits {
  uint8_t b0:1, b1:1, b2:1, b3:1, b4:1, b5:1, b6:1, b7:1;
} __attribute__((__packed__));
#define SBIT_(port,pin) ((*(volatile struct bits*)&port).b##pin)
#define   SBIT(x,y)       SBIT_(x,y)

The macro was described on another tutorial:
http://www.avrfreaks.net/index.php?name ... ;start=all

Now we can define the 6 needed IO-pins. We must also define the corresponding direction bits, since a macro can not generate a second macro inside:
Code:

#define LCD_D4          SBIT( PORTB, 0 )
#define LCD_DDR_D4      SBIT( DDRB, 0 )

#define LCD_D5          SBIT( PORTB, 4 )
#define LCD_DDR_D5      SBIT( DDRB, 4 )

#define LCD_D6          SBIT( PORTB, 3 )
#define LCD_DDR_D6      SBIT( DDRB, 3 )

#define LCD_D7          SBIT( PORTB, 5 )
#define LCD_DDR_D7      SBIT( DDRB, 5 )

#define LCD_RS          SBIT( PORTB, 2 )
#define LCD_DDR_RS      SBIT( DDRB, 2 )

#define LCD_E0          SBIT( PORTB, 1 )
#define LCD_DDR_E0      SBIT( DDRB, 1 )


3.
Now we can start to do the code.
On looking on the HD44780 datasheet, we can see, that we must send a nibble (4bit) and then pulse the E-line high and low. This was the basic step on all sending and thus we write our first function for it.
We input a byte, where are only the upper 4 bits are used and set the 4 IO-lines to the LCD accordingly. Then the E-line was pulsed for about 1µs:
Code:

static void lcd_nibble( uint8_t d )
{
  LCD_D4 = 0; if( d & 1<<4 ) LCD_D4 = 1;
  LCD_D5 = 0; if( d & 1<<5 ) LCD_D5 = 1;
  LCD_D6 = 0; if( d & 1<<6 ) LCD_D6 = 1;
  LCD_D7 = 0; if( d & 1<<7 ) LCD_D7 = 1;

  LCD_E0 = 1;
  _delay_us( 1 );                       // 1us
  LCD_E0 = 0;



But after initialization we need always to send a whole byte. And after every byte we need a busy waiting of about 50µs. So we need the next function:
Code:

static void lcd_byte( uint8_t d )
{
  lcd_nibble( d );
  lcd_nibble( d<<4 );
  _delay_us( 50 );                      // 50us
}



Also we must send instructions and data to the LCD. The only difference was the state of the RS line. And some instructions (Clear Dispay, Return Home) need a longer busy time of about 2ms:
Code:

void lcd_command( uint8_t d )
{
  LCD_RS = 0;
  lcd_byte( d );
  if( d <= 3 )
    _delay_ms( 2 );                     // wait 2ms
}

void lcd_putchar( uint8_t d )
{
  LCD_RS = 1;
  lcd_byte( d );
}



Now we are ready to intialize the LCD. At first we must set the 6 IO-pins as outputs. Then we should follow the order for initialization of the 4bit mode according to the data sheet. Why should we try to set the 8bit mode first?
After a clean power on reset the LCD should be in the 8bit mode. But after slowly rising power or a reset by another source (manual knob, watchdog), the state was unknown.
So the only chance to turn the LCD into a known state, was to set the 8bit mode first. Since this may fail, if the LCD was in the 4bit mode and awaiting the second nibble, it must be tried two times.
Code:

void lcd_init( void )
{
  LCD_DDR_D4 = 1;                               // enable output pins
  LCD_DDR_D5 = 1;
  LCD_DDR_D6 = 1;
  LCD_DDR_D7 = 1;
  LCD_DDR_RS = 1;
  LCD_DDR_E0 = 1;
  LCD_E0 = 0;
  LCD_RS = 0;                                   // send commands

  _delay_ms( 15 );
  lcd_nibble( 0x30 );
  _delay_ms( 4.1 );
  lcd_nibble( 0x30 );
  _delay_us( 100 );
  lcd_nibble( 0x30 );
  _delay_us( LCD_TIME_DAT );
  lcd_nibble( 0x20 );                           // 4 bit mode
  _delay_us( LCD_TIME_DAT );
  lcd_command( 0x28 );                          // 2 lines 5*7
  lcd_command( 0x08 );                          // display off
  lcd_command( 0x01 );                          // display clear
  lcd_command( 0x06 );                          // cursor increment
  lcd_command( 0x0C );                          // on, no cursor, no blink



Some additional functions:
lcd_puts(): display a string until zero byte
ldc_xy(): set the cursor to x (column), y (line)
ldc_blank: overwrite a piece of the LCD with blanks

Why lcd_xy was implemented as macro?
Since typically the position was known at compile time, it's code saving, if the command was already calculated at compile time also.

Why no clear command?
On using the clear command you see always an annoying flicker until the text was written again. Thus it looks many more professional, if you omit this command and simple overwrite the old text with the new text.


4.
Finally we are ready to write our first LCD program:
Code:

int main( void )
{
  lcd_init();
  lcd_xy( 2, 0 );
  lcd_puts( "Hello Peter" );

  for(;;){
  }
}


I have now added a driver for different formats.

To see the attachments, you must be logged in Exclamation

Peter Dannegger


Last edited by danni on Jan 31, 2011 - 07:59 PM; edited 2 times in total
 
 View user's profile Send private message  
Reply with quote Back to top
floresta1212
PostPosted: Jan 16, 2011 - 09:55 PM
Resident


Joined: Mar 29, 2006
Posts: 667
Location: Western New York, USA

Ah ha - someone who can read and correctly follow the data sheet.

In the data sheet there is no delay specified after the third 0x30 of the initialization sequence or for the following 0x20. I believe that this omission is an editorial error in the flowchart since it would be unusual for a processor to be able to deal with these two instructions in zero time while all the others have some specified minimum time requirement. I generally play it safe and use 100uS as specified for the preceding 0x30.

Have you tried your cursor positioning technique on any of the 4 line displays?

Don
 
 View user's profile Send private message  
Reply with quote Back to top
danni
PostPosted: Jan 17, 2011 - 10:21 PM
Raving lunatic


Joined: Sep 05, 2001
Posts: 2612


Yes, the data sheet was misleading in this sequence.
Without the delays, the sequence works only randomly.

floresta1212 wrote:
Have you tried your cursor positioning technique on any of the 4 line displays?


I have added this function now.
Maybe you can test it and tell me, if it works.


Peter
 
 View user's profile Send private message  
Reply with quote Back to top
nta_xyz
PostPosted: May 21, 2011 - 12:08 AM
Hangaround


Joined: Dec 30, 2010
Posts: 188


hi i use this lib for atmega128rfa1...when i program exactly your file and program atmega128rfa1 it is work fine:)...
but! when i used it in the wsndemo application(bitcloud) avr studio give me this error:
./include/main.h:12:1: warning: "F_CPU" redefined
please guide me what can i do to solve this problem.thanks
 
 View user's profile Send private message Send e-mail  
Reply with quote Back to top
Koshchi
PostPosted: May 21, 2011 - 05:52 AM
10k+ Postman


Joined: Nov 17, 2004
Posts: 15015
Location: Vancouver, BC

Quote:
please guide me what can i do to solve this problem.thanks
Don't define F_CPU twice. Which one you should move depends on where it is being defined.

_________________
Regards,
Steve A.

The Board helps those that help themselves.
 
 View user's profile Send private message  
Reply with quote Back to top
nta_xyz
PostPosted: May 21, 2011 - 10:14 AM
Hangaround


Joined: Dec 30, 2010
Posts: 188


Quote:

Don't define F_CPU twice. Which one you should move depends on where it is being defined.

but when i comment this line in main.h:
Code:

#define   XTAL   8e6         // 8MHz

#define F_CPU   XTAL

my lcd did not initial fine:( and i can not see any thing on it!!
 
 View user's profile Send private message Send e-mail  
Reply with quote Back to top
Koshchi
PostPosted: May 21, 2011 - 05:35 PM
10k+ Postman


Joined: Nov 17, 2004
Posts: 15015
Location: Vancouver, BC

Then obviously the other definition defines it as something other that 8MHz. You need to define F_CPU as the actual speed your AVR is running at.

_________________
Regards,
Steve A.

The Board helps those that help themselves.
 
 View user's profile Send private message  
Reply with quote Back to top
nta_xyz
PostPosted: May 21, 2011 - 10:15 PM
Hangaround


Joined: Dec 30, 2010
Posts: 188


it is work with 16 mhz but when i write this:

#define XTAL 16e6

#define F_CPU XTAL

lcd dont initial!!Sad(
 
 View user's profile Send private message Send e-mail  
Reply with quote Back to top
clawson
PostPosted: May 22, 2011 - 09:51 AM
10k+ Postman


Joined: Jul 18, 2005
Posts: 71208
Location: (using avr-gcc in) Finchingfield, Essex, England

Please don't use tutorial threads to ask general questions - the thread is only to make suggestions as to how the tutorial can be improved - for general questions start a thread in AVR Forum.

Moderator

_________________
 
 View user's profile Send private message  
Reply with quote Back to top
smileymicros
PostPosted: Oct 19, 2011 - 11:54 PM
Raving lunatic


Joined: Nov 17, 2004
Posts: 6496
Location: Great Smokey Mountains.

danni,

THANK YOU!

This worked right out of the box. You da man...

Smiley

_________________
FREE TUTORIAL: 'Quick Start Guide for Using the WinAVR C Compiler with ATMEL's AVR Butterfly' AVAILABLE AT: http://www.smileymicros.com
 
 View user's profile Send private message Visit poster's website 
Reply with quote Back to top
cest73
PostPosted: Dec 01, 2011 - 08:14 AM
Newbie


Joined: Jul 20, 2010
Posts: 1
Location: Serbia, Republic of

just to say thanks a zillion!
Surprised)
 
 View user's profile Send private message  
Reply with quote Back to top
indianajones11
PostPosted: Jan 20, 2012 - 12:42 AM
Raving lunatic


Joined: Nov 28, 2004
Posts: 3893
Location: San Diego, Ca

I would tweak this code a bit by :
Code:
#define   LCD_DDR    DDRB // Or whatever... 
#define   LCD_DATA   PORTB //... names desired .
That way, if someone wanted to change to another port, or port it to another AVR and can't use PORTB, it'll be just 2 changes instead of 12 .

_________________
1) Studio 4.18 build 716 (SP3)
2) WinAvr 20100110
3) PN, all on Doze XP... For Now
A) Avr Dragon ver. 1
B) Avr MKII ISP, 2009 model
C) MKII JTAGICE ver. 1
 
 View user's profile Send private message  
Reply with quote Back to top
sternst
PostPosted: Jan 20, 2012 - 01:30 AM
Raving lunatic


Joined: Jul 23, 2001
Posts: 2728
Location: Osnabrueck, Germany

indianajones11 wrote:
I would tweak this code a bit by :
Code:
#define   LCD_DDR    DDRB // Or whatever... 
#define   LCD_DATA   PORTB //... names desired .
That way, if someone wanted to change to another port, or port it to another AVR and can't use PORTB, it'll be just 2 changes instead of 12 .
"tweak"? Obviously you missed an important feature of Peter's code. All signals can be on different ports. Even the data lines don't need to be on the same port.

_________________
Stefan Ernst
 
 View user's profile Send private message  
Reply with quote Back to top
indianajones11
PostPosted: Jan 20, 2012 - 01:54 AM
Raving lunatic


Joined: Nov 28, 2004
Posts: 3893
Location: San Diego, Ca

Nope didn't miss that, but it would be good IF most or ALL are on the same port .

_________________
1) Studio 4.18 build 716 (SP3)
2) WinAvr 20100110
3) PN, all on Doze XP... For Now
A) Avr Dragon ver. 1
B) Avr MKII ISP, 2009 model
C) MKII JTAGICE ver. 1
 
 View user's profile Send private message  
Reply with quote Back to top
JohanEkdahl
PostPosted: Jan 20, 2012 - 06:03 AM
10k+ Postman


Joined: Mar 27, 2002
Posts: 22029
Location: Lund, Sweden

Quote:

but it would be good IF most or ALL are on the same port

Huh?!? In general, or in the specific case of using dannis code? He states as an explicit requirement that the case of pins being on different ports should be handled.
 
 View user's profile Send private message Visit poster's website 
Reply with quote Back to top
indianajones11
PostPosted: Jan 20, 2012 - 08:10 AM
Raving lunatic


Joined: Nov 28, 2004
Posts: 3893
Location: San Diego, Ca

In my 1st post I should have wrote, "If one were to pick the same port for the 6 pins, ..." . Sorry about that .

_________________
1) Studio 4.18 build 716 (SP3)
2) WinAvr 20100110
3) PN, all on Doze XP... For Now
A) Avr Dragon ver. 1
B) Avr MKII ISP, 2009 model
C) MKII JTAGICE ver. 1
 
 View user's profile Send private message  
Reply with quote Back to top
danrulz01
PostPosted: Jul 16, 2012 - 10:52 AM
Hangaround


Joined: Feb 24, 2006
Posts: 232
Location: Australia (BRISBANE)

As smiley said, works right out of the box Wink / Nailed it imo.

_________________
The love of something so annoying and complex is not love...Its a PASSION!
 
 View user's profile Send private message  
Reply with quote Back to top
Dondody
PostPosted: Feb 26, 2013 - 06:08 PM
Rookie


Joined: Nov 22, 2012
Posts: 33


Hi, what is busy testing means in this case ?
 
 View user's profile Send private message  
Reply with quote Back to top
Koshchi
PostPosted: Feb 27, 2013 - 12:50 AM
10k+ Postman


Joined: Nov 17, 2004
Posts: 15015
Location: Vancouver, BC

There are two methods for determining whether or not the LCD is ready for the next command. One is to delay long enough to know that the LCD has had time to process whatever it was doing. The other is to poll the LCDs busy flag. Busy testing refers to the second method.

_________________
Regards,
Steve A.

The Board helps those that help themselves.
 
 View user's profile Send private message  
Reply with quote Back to top
rainblag
PostPosted: Mar 25, 2014 - 07:56 AM
Rookie


Joined: Oct 25, 2013
Posts: 46


hello avr experts,

May I know how can I in anyway, modify danni's code above, to include the following :

Code:

double count =0, dist;
   delay_us(0.1);
   count=count+0.0000001;
   dist=count*34029/2;
        lcd_puts(dist); //i wish to display value of dist


dist= distance. I wish to display value of distance. but i know lcd_puts, puts out only character, in this case probably the character of distance's value. Instead of the chracter, I want to display the actual value in numbers of dist. May someone enlighten me on how to do this?
thank you very much.
 
 View user's profile Send private message  
Reply with quote Back to top
david.prentice
PostPosted: Mar 25, 2014 - 08:45 AM
10k+ Postman


Joined: Feb 12, 2005
Posts: 20528
Location: Wormshill, England

lcd_putchar() takes a single character.
lcd_puts() takes a "string".

If you want to print a human readable number, you have to convert it to a string. Them you can output the string. Note that your string buffer must have enough room to hold the biggest number and its terminating NUL.

e.g. a 16-bit integer: "-32768" needs 7 bytes.
e.g. a 32-bit integer: "-2147483648" needs 12 bytes
e.g. a float or double: "1234.5678" or "-1.2345678e45" depend on the format.

You can use itoa(), utoa(), ftoa(), dtostrf(), ... and other non-standard functions.
There should always be sprintf() but many AVR compilers default to a 'small' sprintf() that may not handle floats or precision.

David.
 
 View user's profile Send private message Send e-mail  
Reply with quote Back to top
Display posts from previous:     
Jump to:  
All times are GMT + 1 Hour
Post new topic   Reply to topic
View previous topic Printable version Log in to check your private messages View next topic
Powered by PNphpBB2 © 2003-2006 The PNphpBB Group
Credits