AtMega328P interface with LCD for 4bit mode

Go To Last Post
74 posts / 0 new

Pages

Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello!

 

I'm starting to know a bit better the AtMega328P and for that I'm trying to interface it with an HD44780 compatible 20x4 LCD.

My problem so far is that I'm not sure about the correct timing sequence that I need to implement on my code to make things work.

 

From the time diagram from datasheet on page 58, I need:

 

RS = 0  //instruction mode

RW = 0 //write mode

E = 1

send 4 MSB of the byte

delay(1us)

 

But at this point I'm not sure what comes next. Do I need to set E = 0 and another delay of 1uS and then set E = 1 again to send the 4 LSB of the bye and finally another delay of 1 uS?

 

I mean:

RS = 0;
RW = 0;

E = 1;
sendByte(0x28);//the 0x28 is just for ilustration purposes
_delay_us(1);
E = 0;
_delay_us(1);

E = 1;
sendByte(0x28 << 4);
_delay(1);
E = 0;
_delay_us(1);

_delay_ms(2);//time needed to process the command

or :

 

RS = 0;
RW = 0;

E = 1;
sendByte(0x28);//the 0x28 is just for ilustration purposes
_delay_us(1);
sendByte(0x28 << 4);
_delay(1);
E = 0;
_delay_ms(2);//time needed to process the command

 

Or are both version wrong? I'm struggling to understand this procedure!

This topic has a solution.
Last Edited: Sun. Oct 30, 2016 - 09:41 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Unless you are really intent on reinventing the wheel just look here:

 

http://www.johanekdahl.se/rwiki/...

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I would just use a respected library.   Verify that your hardware is working 100%.

 

Then set about the wheel re-invention together with the Timing diagrams on page 58.

 

The diagrams are for 8-bit mode.    In 4-bit mode you just do everything twice.   i.e.  wobble E for high nibble,  wobble E for low nibble.

The total cycle time constraint is the same.    i.e. typical 37us per regular command/data.    (1520us for CLR/HOME command)

 

Note that the 37us is typical.    If the HD44780 internal RC clock is slow,   I would regard 50us as a safe time if you don't check BUSY.

 

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks for replying! Of course I'm not trying to reinvent anything!

Just trying to make my own code and understand all the mechanisms!

 

Anyway, I gave up to try to understand the mechanism! Going to use the code provided and check if it works. Bu I'm using 16MHz XTAL... Does this changes anything?

 

I see a 5ms delay in that code. What is that 5mS for and why is there any other delays for instructions and doe data? The datasheet is not being respected, though, things work??? How come??

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You need to usage delay because LCD Driver need time for Executing Command.

In following photo Execution time is given.

 

 

 

শূন্য  - The ZeRo

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yes, but in the code provided there is only one delay of 5ms... Why is that delay and why there are no other delays for the instructions of that table?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

If you really want to understand the mechanism of LCD.

I will say you that first drive it manually.

Then you should understand it better :)

 

শূন্য  - The ZeRo

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You specify the XTAL frequency and the library does the rest.  I am not familiar with johans library, but I know johan so I would say you should have no problems with it.

 

I use Peter Fleury's library:

http://homepage.hispeed.ch/peter...

 

In Peters library you specify in teh LCD.h file the XTAL frequency, the port you are connecting to, and the size of the display

 

And recently I moved to using I2C and Davide Gironi's modified Fleury Library for use with a PCF8574:

http://davidegironi.blogspot.com...

 

Jim

If you want a career with a known path - become an undertaker. Dead people don't sue! - Kartman

Please Read: Code-of-Conduct

Atmel Studio6.2/AS7, DipTrace, Quartus, MPLAB user

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Actually you need a delay that given in image.

And this is the minimum delay time for execution.

 

Look the image maximum delay 1.64ms right?

If your code's delay time is greater than or equal to 1.64ms.

its work.

 

But if you usage less than 1.64ms its dose not work.

 

http://www.johanekdahl.se/rwiki/...

here usage "5ms" delay that is greater than 1.64ms .

so its ok.

 

 

Hope now you clear it.

শূন্য  - The ZeRo

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

eRony wrote:

If you really want to understand the mechanism of LCD.

I will say you that first drive it manually.

Then you should understand it better :)

 

 

 

 

That was what I was trying to accomplish!

But I think I'm struggling to understand the sequence of E = 1 and E = 0 and the required delays...

I've seen quite some examples but I can't find out a common pattern where I can say: "Ok, Let's compare these to example codes to see if they match up!" and then go through the code step by step to understand what is going on!

 

If I try to follow the datasheet exactly I have the following:

 

 

First I set RS and RW to LOW and wait t{AS} before I can rise E to HIGH:

 

 

 

After t{AS} as passed, I can set E to HIGH, send the byte (or nibble) and wait t{DSW}.

 

 

After t{DSW} has passed, the LCD already "took a shot" of the byte (or nibble) and I can set E to LOW and wait t{H}.

 

And after this, I still have a period of time that is not even named in the diagram that is probably the time required by the LCD to process the byte itself.

 

But I don't think that if I code this sequence, things will work. Am I wrong?

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

eRony wrote:

Actually you need a delay that given in image.

And this is the minimum delay time for execution.

 

Look the image maximum delay 1.64ms right?

If your code's delay time is greater than or equal to 1.64ms.

its work.

 

But if you usage less than 1.64ms its dose not work.

 

http://www.johanekdahl.se/rwiki/...

here usage "5ms" delay that is greater than 1.64ms .

so its ok.

 

 

Hope now you clear it.

 

I understand that about the minimum delay. But use 5mS as a predefined global delay? There are a lot of instructions that only requires like 1/2000 of that time! Aren't we being kinda slack using such amount of time as a predefined delay? Aren't we turning our code into a relatively slow code only because of those 5mS??? 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You also need to make sure you do not violate other timings shown (e.g. PWEH (450ns) and/or tcycE (1000ns)).

There are also some caveats to the timing in post #5, at the bottom it says "Execution times are typical..."

In the link in your post #1  it says, "Execution time changes when frequency changes" (page 25).

So do you have control over fOSC?

5ms may be excessive, YMMV.

David (aka frog_jr)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

If you are worried about delays and such, then I suggest you get a working library running...such as one of the two listed above, and play with the delays to see what happens.

 

Jim

 

EDIT:

What speed is your AVR running at, and are you using Studio, or CodeVision for your IDE?

 

 

If you want a career with a known path - become an undertaker. Dead people don't sue! - Kartman

Please Read: Code-of-Conduct

Atmel Studio6.2/AS7, DipTrace, Quartus, MPLAB user

Last Edited: Wed. Oct 26, 2016 - 08:11 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

So, what does the sendByte() function do, exactly?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

frog_jr wrote:

You also need to make sure you do not violate other timings shown (e.g. PWEH (450ns) and/or tcycE (1000ns)).

There are also some caveats to the timing in post #5, at the bottom it says "Execution times are typical..."

In the link in your post #1  it says, "Execution time changes when frequency changes" (page 25).

So do you have control over fOSC?

5ms may be excessive, YMMV.

Ok, yes, I'm also aware that timings varies with quite some factors as voltage, frequency, temperature and probably even with the manufacturing process. But I don't want to get that way at all. I want to make things as simple as possible, but not as simple as just using someone's code just because.

I have my AtMega328 running with a 16MHz XTAL.

Last Edited: Thu. Oct 27, 2016 - 12:15 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

jgmdesign wrote:

If you are worried about delays and such, then I suggest you get a working library running...such as one of the two listed above, and play with the delays to see what happens.

 

Jim

 

EDIT:

What speed is your AVR running at, and are you using Studio, or CodeVision for your IDE?

 

 

Well, I would need some help to do that. And I think I don't need that level of knowledge at this time. I just would like to understand what others have already done and maybe after that, try to make my own code from scratch...

I'm a Linux user, so I'm using Atmel 3.4 (or so) tool chain to compile and avrdude to upload the firmware.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

El Tangas wrote:

So, what does the sendByte() function do, exactly?


Ok, I have done a small game project in an academic context but I used 8051 based uC.

Back then I also struggled to make this to work on the hardware. I was using MCU 8051 IDE that allowed us to ignore all these timings. And we used the 8 bit mode.
And I memorised the idea (not sure if correct) that send an instruction (that I call command sometimes) was different of sending data which means send alpha numeric chars to the LCD and that the code for both actions was different. But looks like that the only difference is the RS bit.

But I think I already understood how to do it using 4 bit mode. My problem is the timings for instructions and for data. Is to line them up correctly.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello,  

 

  I suggest that you back up and reconsider using any HD44780-based LCD screen on your new designs.   Consider instead a TFT-based display such as this eBay 1.8" 128x160 pixel:

 

http://www.ebay.com/itm/1-8-inch...

 

This kind of TFT and the 2.2" 240x320 pixel screens are -roughly- the same price as the HD44780 LCD screens,  but they look much better.  You can display 25 chars per line/16 lines on the screen, along with graphics and colors. The Adafruit library for the ST7735R used in the 1.8" TFT works well.

 

Since you're already using a mega328p at 16MHz, then you may as well use the Arduino standardized-design version of this chip/crystal combination.  You get access to all the working libraries for all the peripheral devices that people have connected to the mega328p/16MHz over the years.

Last Edited: Thu. Oct 27, 2016 - 01:08 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

PsySc0rpi0n,

Take a look at teh attached project.  It was done in Studio 6_2 but will work in version 7 as well.  There is an ELF and HEX file you can load into your Mega328 using AVRdude if need be.  It has been configured for a 16megahertz clock, and the LCD is connected to PORTD.

 

LCD Pinouts are:

RS = 0

R/W = 1

E = 2

Data0 = 3

Data1 = 4

Data2 = 5

Data3 = 6

 

PORTD-7 is not used.

 

I tested the code on a Mega328 in my STK600 and it does work.

 

You can see in LCD.h how the port, the number of lines and characters, and XTAL for the library is set up.

 

Jim

Attachment(s): 

If you want a career with a known path - become an undertaker. Dead people don't sue! - Kartman

Please Read: Code-of-Conduct

Atmel Studio6.2/AS7, DipTrace, Quartus, MPLAB user

Last Edited: Thu. Oct 27, 2016 - 02:23 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ok, guys... That's why I'm about to quit trying to understand the mechanisms. I have already did this in the past using an 8051 based uC. But this was almost 2 years ago! So I was trying to write my own code but I didn't want to have to deal with all the details. Just the most important ones! I mean, I wanted to write my own functions such as the sendData, sendInstruction, LcdInit, sendChar and sendString. Not more than this. But to accomplish these tasks I still need a correct timing sequence.

 

Anyway, I think I'm going to give up of trying to write my own code because I can't get the functions sendData and sendInstruction to work properly due to the timings sequence not be the correct one, I think!

 

My main goal were two. Understand and recall the LCD interfacing mechanisms and later, interface the AtMega328P-PU with an external memory IC to be able to send data and read data from it and put it to the LCD!

 

If I keep insisting in this LCD interfacing stuff, I won't be able to go further with anything else!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

PsySc0rpi0n wrote:
I just would like to understand what others have already done and maybe after tha

Did you read my post? ...

clawson wrote:

Unless you are really intent on reinventing the wheel just look here:

 

http://www.johanekdahl.se/rwiki/...

AFAIK that is one of the best collection of resources for HD44780 on AVR there is anywhere on the internet.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:

PsySc0rpi0n wrote:
I just would like to understand what others have already done and maybe after tha

 

Did you read my post? ...

clawson wrote:

Unless you are really intent on reinventing the wheel just look here:

 

http://www.johanekdahl.se/rwiki/...

AFAIK that is one of the best collection of resources for HD44780 on AVR there is anywhere on the internet.

 

Yes, I red your post and I have looked into the code available there! I'll try to use it and adapt it to my needs (way of coding)! But I still had no time to test on the hardware yet! Maybe later today after classes!

Last Edited: Thu. Oct 27, 2016 - 09:33 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The best approach, as others have said, really is to try and get some working code running first. This will ensure you have all the wires connected up right and the Vee contrast voltage is set correctly and so on. With all that proven you have a blank slate on which to then write your own implementation safe in the knowledge that if you do it right the thing WILL work.

 

If you have unknown hardware and unknown software there are just too many unknowns and you may get the software exactly right but still see nothing because it turned out to be a hardware issue.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:

The best approach, as others have said, really is to try and get some working code running first. This will ensure you have all the wires connected up right and the Vee contrast voltage is set correctly and so on. With all that proven you have a blank slate on which to then write your own implementation safe in the knowledge that if you do it right the thing WILL work.

 

If you have unknown hardware and unknown software there are just too many unknowns and you may get the software exactly right but still see nothing because it turned out to be a hardware issue.


I will do that. But I have a question about the code on the link you provided :
I only see delays in lcdsendnibble and lcdinitialize functions. The comments says that the code will work with a frequency of operation up to 8 MHz. But I have a 16 MHz XTAL attached to my uC. Shouldn't the code have delays between or before the E pin transitions? And for my XTAL could the already hardcoded delays be decreased?

Last Edited: Thu. Oct 27, 2016 - 10:11 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

If you look in post #19 I left you a complete working project for you to dissect.

Jim

If you want a career with a known path - become an undertaker. Dead people don't sue! - Kartman

Please Read: Code-of-Conduct

Atmel Studio6.2/AS7, DipTrace, Quartus, MPLAB user

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

jgmdesign wrote:
If you look in post #19 I left you a complete working project for you to dissect.

Jim


Yes, but that code is a lot different from the other one, I guess. So I cannot try to pick more than one code at the same time.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

What difference does that make? The .h and .c files are well commented to explain what is going on. I believe there is a manual on peters website too

Jim

If you want a career with a known path - become an undertaker. Dead people don't sue! - Kartman

Please Read: Code-of-Conduct

Atmel Studio6.2/AS7, DipTrace, Quartus, MPLAB user

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Of course, those of us in the know stopped using HD44780 displays a while back and now use those with an I2C interface as we only need 2 wires.

'This forum helps those who help themselves.'

 

pragmatic  adjective dealing with things sensibly and realistically in a way that is based on practical rather than theoretical consideration.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

jgmdesign wrote:
What difference does that make? The .h and .c files are well commented to explain what is going on. I believe there is a manual on peters website too

Jim

It will take much more time to me to dissect that one.
Peter's code is more straightforward, I guess.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Brian Fairchild wrote:

Of course, those of us in the know stopped using HD44780 displays a while back and now use those with an I2C interface as we only need 2 wires.

I'll also get to that point eventually in the future...

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Brian Fairchild wrote:

Of course, those of us in the know stopped using HD44780 displays a while back and now use those with an I2C interface as we only need 2 wires.


I too use I2c now as opposed to the parallel method but let's not confuse the OP more than he/she already is.

To the OP,
Johans library does the same thing Peters does. Puts characters on an LCD. If you look at both libraries there are many similarities.

If I get back at a reasonable time late tonight I may put together the same project I posted, but using Johans library.

In the meantime if you load the elf or hex file in the test LCD project you can at least confirm your screen works.

Jim

If you want a career with a known path - become an undertaker. Dead people don't sue! - Kartman

Please Read: Code-of-Conduct

Atmel Studio6.2/AS7, DipTrace, Quartus, MPLAB user

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

jgmdesign wrote:
Brian Fairchild wrote:

Of course, those of us in the know stopped using HD44780 displays a while back and now use those with an I2C interface as we only need 2 wires.


I too use I2c now as opposed to the parallel method but let's not confuse the OP more than he/she already is.

To the OP,
Johans library does the same thing Peters does. Puts characters on an LCD. If you look at both libraries there are many similarities.

If I get back at a reasonable time late tonight I may put together the same project I posted, but using Johans library.

In the meantime if you load the elf or hex file in the test LCD project you can at least confirm your screen works.

Jim


I'll try to get home as soon as possible after classes to test the code.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Brian Fairchild wrote:

Of course, those of us in the know stopped using HD44780 displays a while back and now use those with an I2C interface as we only need 2 wires.

I don't see how the I2C interface changes things very much.  In most cases the I2C interface is an adapter that sits between the microprocessor and the display.  The display is still driven by an HD44780 and the microprocessor still has to generate the signals required by that HD44780.  All that the I2C interface does is reduce the number of wires (and hence the number of I/O pins) involved.

 

Don

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

My problem is the timings for instructions and for data. Is to line them up correctly.

You really should follow the link in reply #2 and scroll down to the section labeled "Small things I've done" where you will find a link to some really nice timing diagrams.

 

Just above that is another section labeled "Other LCD resource on the web" where you will find a link to where there are some "LCD Programming Examples" that you may find informative.

 

Don

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

OP said: 

.... I'm about to quit trying to understand the mechanisms. I have already did this in the past...... almost 2 years ago..... I didn't want to have to deal with all the details. Just the most important ones!

 

"Without out understanding the details, how can you only write high level functions?"

 

You have been given several WORKING examples, but have not taken the TIMEto look at them to understand and learn how they work!   What is the point of continuing?

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

floresta1212 wrote:

 

My problem is the timings for instructions and for data. Is to line them up correctly.

 

You really should follow the link in reply #2 and scroll down to the section labeled "Small things I've done" where you will find a link to some really nice timing diagrams.

 

Just above that is another section labeled "Other LCD resource on the web" where you will find a link to where there are some "LCD Programming Examples" that you may find informative.

 

Don

 

 

 

I'm reading it and I'm using it! Later I'll test the code on my hardware!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

floresta1212 wrote:
I don't see how the I2C interface changes things very much.
No "E" wire to mess up the timing on ;-)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

First of all: Hello Everybody :)

And now to topic. The missing bit of information may be: As far as I remember E is sensitive to falling edges. The minimum time to keep E on high level before it can go low is PWeh=230ns (or 450ns, depending on voltage). RW and RS should be set at least tAS=40ns (or 60ns) before the rising edge on E appears, the databus should be set at least tDSW=80ns (or 195ns) before the falling edge appears. Data (as well as RS/RW) are accepted every time E goes low.
After sending a complete command (two nibbles) you either have to wait for some time - specified roughly in table 6 in the datasheet - before sending something else to the controller, or you can poll the busy-flag (which is what I prefer when I have a spare pin): receive 2 nibbles (tinging on E for each nibble) and check bit 7 of the combined value.

Beside: When your display still refuse to work then check that your init-sequence sticks to the diagram figure 24 in the datasheet (including the delay after power up). Somehow I always considered the init as the hardest part when handling a hd44780...

Beside II: I really appreciate your will to understand the stuff and get things running all by yourself. That's the way to go! :)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I have just tested the code I've just tested the code from this link:

http://www.johanekdahl.se/rwiki/...

 

and I added some delays but still could not make it work. Or better, some rubish shows up on the LCD display!

 

This is the code I tested:

/* Simple LCD character display module demonstrator using
 * the 4-bit interface. No handling of the busy flag is
 * done - all timing contraints are handled by delay loops.
 * This code should be good to run at up to 8 MHz.
 * Above that you might have to tweak the timing of things
 * including, but not restricted to, the existing delays.
 */

#include <avr/io.h>
#include <util/delay.h>

/* Here are some defines for the data connections,
 * DB4..7 on the LCD display module. These should be wired
 * up to the four high pins  of any port A..D. You need to alter
 * these to fit your wire up of your display.
 */
#define LCD_DATA_PORT PORTB
#define LCD_DATA_DDR  DDRB

#define LCD_D7 PB1
#define LCD_D6 PB2
#define LCD_D5 PB3
#define LCD_D4 PB4

/* Here are some defines for the control connections
 * (RS, R/W and E on the LCD display module). These can be wired
 * to any pins on any port A..D (all to the same port though).
 * You need to alter these to fit your wire up of your display.
 */
#define LCD_CTRL_PORT PORTC
#define LCD_CTRL_DDR  DDRC
#define LCD_RW        PC5
#define LCD_E         PC4
#define LCD_RS        PC3

/*
 * YOU SHOULD NOT NEED TO ALTER ANYTHING BEYOND THIS POINT!
 *
 * This holds as long as you have the four data signals
 * connected to the upper 4-bit nibble of the AVR port,
 * and you run at a maximum frequency of 8 MHz.
 */

/*
 * Here are some defines for the LCD instructions we'll use.
 */
#define LCD_FUNCTION_SET      0x30 // 0b00110000, 8 bit, 2 lines, 5x8 dots
#define LCD_FUNCTION_SET_4BIT 0x28 // 0b00101000, 4 bit, 2 lines, 5x8 dots
#define LCD_DISPLAY_OFF       0x08 // 0b00001000, Disp OFF, cursor OFF, blink OFF
#define LCD_DISPLAY_ON        0x0C // 0b00001111, Disp ON, cursor ON, Blink ON
#define LCD_DISPLAY_CLEAR     0x01 // 0b00000001, Display clear
#define LCD_ENTRY_MODE_SET    0x06 // 0b00000110, Increment ADC, no display shift
#define LCD_CURSOR_HOME       0x02 // 0b00000010, Return home


/* LcdSendNibble
 *
 * Sends a 4-bit nibble to the display.
 *
 * Parameters:
 *  uint8_t nibble  The high nibble of of this byte
 *                  is sent to the display.
 * Returns:
 *  nothing
 */
void LcdSendNibble( uint8_t nibble ){
   _delay_us(50);

   // Output upper nibble on the data ports upper bits
   LCD_DATA_PORT = (nibble & 0xF0) | (LCD_DATA_PORT & 0x0F);

   // Toggle the E line
   LCD_CTRL_PORT |=  (1 << LCD_E);   // Going up..
   _delay_us(50);
   LCD_CTRL_PORT &= ~(1 << LCD_E);  // ..and down.
}

/* LcdSendByte
 *
 * Sends a 8-bit byte to the display.
 *
 * Parameters:
 *  uint8_t theByte The byte to send to the display
 *
 * Returns:
 *  nothing
 */
void LcdSendByte(uint8_t theByte){
   // Send the high nibble
   LcdSendNibble(theByte);
   // Shift theByte to get lower nibble in upper part...
   // ...and send it
   LcdSendNibble(theByte << 4);
}

/* LcdSendInstruction
 *
 * Sends an instruction to the display.
 *
 * Parameters:
 *  uint8_t command This byte is sent to the display as
 *                  an instruction (RS low).
 * Returns:
 *  nothing
 */
void LcdSendInstruction( uint8_t theInstruction ){
   // RS low for instructions
   LCD_CTRL_PORT &= ~(1 << LCD_RS);

   // Send the instruction
   LcdSendByte(theInstruction);
}

/* LcdSendCharacter
 *
 * Sends a character to the display.
 *
 * Parameters:
 *  uint8_t nibble  This byte is sent to the display as
 *                  a character (RS high).
 * Returns:
 *  nothing
 */
void LcdSendCharacter(uint8_t theChar){
   // RS high for characters to display
   LCD_CTRL_PORT |= (1 << LCD_RS);

   // Send the command
   LcdSendByte(theChar);
}

/* LcdInitialize
 *
 * Initialize the display.
 *
 * Parameters:
 *  none
 *
 * Returns:
 *  nothing
 */
void LcdInitialize(void){
   // initialize LCD control lines
   LCD_CTRL_PORT &= ~(1 << LCD_RS);   // RS low
   LCD_CTRL_PORT &= ~(1 << LCD_RW);   // R/W low
   LCD_CTRL_PORT &= ~(1 << LCD_E);    // E low

   // initialize LCD control lines to output
   LCD_CTRL_DDR |= (1 << LCD_RS);
   LCD_CTRL_DDR |= (1 << LCD_RW);
   LCD_CTRL_DDR |= (1 << LCD_E);

   // initialize LCD data port to input
   LCD_DATA_DDR |= 0xF0;      // Data on high four bits of port for now...

   // First part of init sequence is 3 x Function Set with
   // stipulated waits. Note that the display is in 8-bit mode
   // initially, but although the four low data lines are not connected
   // this does not matter as the instructions low nibble is zero anyway.
   _delay_ms(100);
   LcdSendNibble( LCD_FUNCTION_SET );
   _delay_ms(10);
   LcdSendNibble( LCD_FUNCTION_SET );
   _delay_us(200);
   LcdSendNibble( LCD_FUNCTION_SET );
   _delay_us(200);

   // Now, still in 8-bit mode, set the display to 4-bit mode
   LcdSendNibble( LCD_FUNCTION_SET_4BIT );
   _delay_us(80);
   // We are now in 4-bit mode.
   // Do the rest of the init sequence.
   LcdSendInstruction( LCD_FUNCTION_SET_4BIT );
   _delay_us(80);
   LcdSendInstruction( LCD_DISPLAY_OFF );
   _delay_us(80);
   LcdSendInstruction( LCD_DISPLAY_CLEAR );
   _delay_ms(4);
   LcdSendInstruction( LCD_ENTRY_MODE_SET );
   _delay_us(80);
   LcdSendInstruction( LCD_DISPLAY_ON );
   _delay_us(80);
}

char message[] = "4-bit avr-gcc";


int main(void){
   LcdInitialize();

   volatile int i = 0;
   while (message[i] != '\0'){
       LcdSendCharacter(message[i++]);
   }

   while(1);
   return 0;
}

This code is not working... Prints some garbage chars on the screen.

 

Howver, this code below, works perfectly and it prints in the LCD display correctly (the code is not mine and it was takne from here http://web.alfredstate.edu/weima...):

/****************************************************************************
    LCD-AVR-4d.c  - Use an HD44780U based LCD with an Atmel ATmega processor
 
    Copyright (C) 2013 Donald Weiman    (weimandn@alfredstate.edu)
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.
 
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
 
    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
/****************************************************************************
         File:    LCD-AVR-4d.c
         Date:    September 16, 2013
 
       Target:    ATmega328
     Compiler:    avr-gcc (AVR Studio 6)
       Author:    Donald Weiman
 
      Summary:    4-bit data interface, busy flag not implemented.
                  Any LCD pin can be connected to any available I/O port.
                  Includes a simple write string routine.
 */
/******************************* Program Notes ******************************
 
            This program uses a 4-bit data interface but does not use the
              busy flag to determine when the LCD controller is ready.  The
              LCD RW line (pin 5) is not connected to the uP and it must be
              connected to GND for the program to function.
 
            All time delays are longer than those specified in most datasheets
              in order to accommodate slower than normal LCD modules.  This
              requirement is well documented but almost always ignored.  The
              information is in a note at the bottom of the right hand
              (Execution Time) column of the instruction set.
 
  ***************************************************************************
 
            The four data lines as well as the two control lines may be
              implemented on any available I/O pin of any port.  These are
              the connections used for this program:
 
                 -----------                   ----------
                | ATmega328 |                 |   LCD    |
                |           |                 |          |
                |        PD7|---------------->|D7        |
                |        PD6|---------------->|D6        |
                |        PD5|---------------->|D5        |
                |        PD4|---------------->|D4        |
                |           |                 |D3        |
                |           |                 |D2        |
                |           |                 |D1        |
                |           |                 |D0        |
                |           |                 |          |
                |        PB1|---------------->|E         |
                |           |         GND --->|RW        |
                |        PB0|---------------->|RS        |
                 -----------                   ----------
 
  **************************************************************************/

#define F_CPU 16000000UL

#include <avr/io.h>
#include <util/delay.h>

// LCD interface (should agree with the diagram above)
//   make sure that the LCD RW pin is connected to GND
#define lcd_D7_port     PORTD                   // lcd D7 connection
#define lcd_D7_bit      PORTD7
#define lcd_D7_ddr      DDRD

#define lcd_D6_port     PORTD                   // lcd D6 connection
#define lcd_D6_bit      PORTD6
#define lcd_D6_ddr      DDRD

#define lcd_D5_port     PORTD                   // lcd D5 connection
#define lcd_D5_bit      PORTD5
#define lcd_D5_ddr      DDRD

#define lcd_D4_port     PORTD                   // lcd D4 connection
#define lcd_D4_bit      PORTD4
#define lcd_D4_ddr      DDRD

#define lcd_E_port      PORTB                   // lcd Enable pin
#define lcd_E_bit       PORTB1
#define lcd_E_ddr       DDRB

#define lcd_RS_port     PORTB                   // lcd Register Select pin
#define lcd_RS_bit      PORTB0
#define lcd_RS_ddr      DDRB

// LCD module information
#define lcd_LineOne     0x00                    // start of line 1
#define lcd_LineTwo     0x40                    // start of line 2
//#define   lcd_LineThree   0x14                  // start of line 3 (20x4)
//#define   lcd_lineFour    0x54                  // start of line 4 (20x4)
//#define   lcd_LineThree   0x10                  // start of line 3 (16x4)
//#define   lcd_lineFour    0x50                  // start of line 4 (16x4)

// LCD instructions
#define lcd_Clear           0b00000001          // replace all characters with ASCII 'space'
#define lcd_Home            0b00000010          // return cursor to first position on first line
#define lcd_EntryMode       0b00000110          // shift cursor from left to right on read/write
#define lcd_DisplayOff      0b00001000          // turn display off
#define lcd_DisplayOn       0b00001100          // display on, cursor off, don't blink character
#define lcd_FunctionReset   0b00110000          // reset the LCD
#define lcd_FunctionSet4bit 0b00101000          // 4-bit data, 2-line display, 5 x 7 font
#define lcd_SetCursor       0b10000000          // set cursor position

// Program ID
uint8_t program_author[]   = "Donald Weiman";
uint8_t program_version[]  = "LCD-AVR-4d (gcc)";
uint8_t program_date[]     = "Sep 16, 2013";

// Function Prototypes
void lcd_write_4(uint8_t);
void lcd_write_instruction_4d(uint8_t);
void lcd_write_character_4d(uint8_t);
void lcd_write_string_4d(uint8_t *);
void lcd_init_4d(void);

/******************************* Main Program Code *************************/
int main(void)
{
// configure the microprocessor pins for the data lines
    lcd_D7_ddr |= (1<<lcd_D7_bit);                  // 4 data lines - output
    lcd_D6_ddr |= (1<<lcd_D6_bit);
    lcd_D5_ddr |= (1<<lcd_D5_bit);
    lcd_D4_ddr |= (1<<lcd_D4_bit);

// configure the microprocessor pins for the control lines
    lcd_E_ddr |= (1<<lcd_E_bit);                    // E line - output
    lcd_RS_ddr |= (1<<lcd_RS_bit);                  // RS line - output

// initialize the LCD controller as determined by the defines (LCD instructions)
    lcd_init_4d();                                  // initialize the LCD display for a 4-bit interface

// display the first line of information
    lcd_write_string_4d(program_author);

// set cursor to start of second line
    lcd_write_instruction_4d(lcd_SetCursor | lcd_LineTwo);
    _delay_us(80);                                  // 40 uS delay (min)

// display the second line of information
    lcd_write_string_4d(program_version);

// endless loop
    while(1);
    return 0;
}
/******************************* End of Main Program Code ******************/

/*============================== 4-bit LCD Functions ======================*/
/*
  Name:     lcd_init_4d
  Purpose:  initialize the LCD module for a 4-bit data interface
  Entry:    equates (LCD instructions) set up for the desired operation
  Exit:     no parameters
  Notes:    uses time delays rather than checking the busy flag
*/
void lcd_init_4d(void)
{
// Power-up delay
    _delay_ms(100);                                 // initial 40 mSec delay

// IMPORTANT - At this point the LCD module is in the 8-bit mode and it is expecting to receive  
//   8 bits of data, one bit on each of its 8 data lines, each time the 'E' line is pulsed.
//
// Since the LCD module is wired for the 4-bit mode, only the upper four data lines are connected to 
//   the microprocessor and the lower four data lines are typically left open.  Therefore, when 
//   the 'E' line is pulsed, the LCD controller will read whatever data has been set up on the upper 
//   four data lines and the lower four data lines will be high (due to internal pull-up circuitry).
//
// Fortunately the 'FunctionReset' instruction does not care about what is on the lower four bits so  
//   this instruction can be sent on just the four available data lines and it will be interpreted 
//   properly by the LCD controller.  The 'lcd_write_4' subroutine will accomplish this if the 
//   control lines have previously been configured properly.

// Set up the RS and E lines for the 'lcd_write_4' subroutine.
    lcd_RS_port &= ~(1<<lcd_RS_bit);                // select the Instruction Register (RS low)
    lcd_E_port &= ~(1<<lcd_E_bit);                  // make sure E is initially low

// Reset the LCD controller
    lcd_write_4(lcd_FunctionReset);                 // first part of reset sequence
    _delay_ms(10);                                  // 4.1 mS delay (min)

    lcd_write_4(lcd_FunctionReset);                 // second part of reset sequence
    _delay_us(200);                                 // 100uS delay (min)

    lcd_write_4(lcd_FunctionReset);                 // third part of reset sequence
    _delay_us(200);                                 // this delay is omitted in the data sheet

// Preliminary Function Set instruction - used only to set the 4-bit mode.
// The number of lines or the font cannot be set at this time since the controller is still in the
//  8-bit mode, but the data transfer mode can be changed since this parameter is determined by one 
//  of the upper four bits of the instruction.
 
    lcd_write_4(lcd_FunctionSet4bit);               // set 4-bit mode
    _delay_us(80);                                  // 40uS delay (min)

// Function Set instruction
    lcd_write_instruction_4d(lcd_FunctionSet4bit);   // set mode, lines, and font
    _delay_us(80);                                  // 40uS delay (min)

// The next three instructions are specified in the data sheet as part of the initialization routine, 
//  so it is a good idea (but probably not necessary) to do them just as specified and then redo them 
//  later if the application requires a different configuration.

// Display On/Off Control instruction
    lcd_write_instruction_4d(lcd_DisplayOff);        // turn display OFF
    _delay_us(80);                                  // 40uS delay (min)

// Clear Display instruction
    lcd_write_instruction_4d(lcd_Clear);             // clear display RAM
    _delay_ms(4);                                   // 1.64 mS delay (min)

// ; Entry Mode Set instruction
    lcd_write_instruction_4d(lcd_EntryMode);         // set desired shift characteristics
    _delay_us(80);                                  // 40uS delay (min)

// This is the end of the LCD controller initialization as specified in the data sheet, but the display
//  has been left in the OFF condition.  This is a good time to turn the display back ON.
 
// Display On/Off Control instruction
    lcd_write_instruction_4d(lcd_DisplayOn);         // turn the display ON
    _delay_us(80);                                  // 40uS delay (min)
}

/*...........................................................................
  Name:     lcd_write_string_4d
; Purpose:  display a string of characters on the LCD
  Entry:    (theString) is the string to be displayed
  Exit:     no parameters
  Notes:    uses time delays rather than checking the busy flag
*/
void lcd_write_string_4d(uint8_t theString[])
{
    volatile int i = 0;                             // character counter*/
    while (theString[i] != 0)
    {
        lcd_write_character_4d(theString[i]);
        i++;
        _delay_us(80);                              // 40 uS delay (min)
    }
}

/*...........................................................................
  Name:     lcd_write_character_4d
  Purpose:  send a byte of information to the LCD data register
  Entry:    (theData) is the information to be sent to the data register
  Exit:     no parameters
  Notes:    does not deal with RW (busy flag is not implemented)
*/

void lcd_write_character_4d(uint8_t theData)
{
    lcd_RS_port |= (1<<lcd_RS_bit);                 // select the Data Register (RS high)
    lcd_E_port &= ~(1<<lcd_E_bit);                  // make sure E is initially low
    lcd_write_4(theData);                           // write the upper 4-bits of the data
    lcd_write_4(theData << 4);                      // write the lower 4-bits of the data
}

/*...........................................................................
  Name:     lcd_write_instruction_4d
  Purpose:  send a byte of information to the LCD instruction register
  Entry:    (theInstruction) is the information to be sent to the instruction register
  Exit:     no parameters
  Notes:    does not deal with RW (busy flag is not implemented)
*/
void lcd_write_instruction_4d(uint8_t theInstruction)
{
    lcd_RS_port &= ~(1<<lcd_RS_bit);                // select the Instruction Register (RS low)
    lcd_E_port &= ~(1<<lcd_E_bit);                  // make sure E is initially low
    lcd_write_4(theInstruction);                    // write the upper 4-bits of the data
    lcd_write_4(theInstruction << 4);               // write the lower 4-bits of the data
}


/*...........................................................................
  Name:     lcd_write_4
  Purpose:  send a byte of information to the LCD module
  Entry:    (theByte) is the information to be sent to the desired LCD register
            RS is configured for the desired LCD register
            E is low
            RW is low
  Exit:     no parameters
  Notes:    use either time delays or the busy flag
*/
void lcd_write_4(uint8_t theByte)
{
    lcd_D7_port &= ~(1<<lcd_D7_bit);                        // assume that data is '0'
    if (theByte & 1<<7) lcd_D7_port |= (1<<lcd_D7_bit);     // make data = '1' if necessary

    lcd_D6_port &= ~(1<<lcd_D6_bit);                        // repeat for each data bit
    if (theByte & 1<<6) lcd_D6_port |= (1<<lcd_D6_bit);

    lcd_D5_port &= ~(1<<lcd_D5_bit);
    if (theByte & 1<<5) lcd_D5_port |= (1<<lcd_D5_bit);

    lcd_D4_port &= ~(1<<lcd_D4_bit);
    if (theByte & 1<<4) lcd_D4_port |= (1<<lcd_D4_bit);

// write the data
                                                    // 'Address set-up time' (40 nS)
    lcd_E_port |= (1<<lcd_E_bit);                   // Enable pin high
    _delay_us(1);                                   // implement 'Data set-up time' (80 nS) and 'Enable pulse width' (230 nS)
    lcd_E_port &= ~(1<<lcd_E_bit);                  // Enable pin low
    _delay_us(1);                                   // implement 'Data hold time' (10 nS) and 'Enable cycle time' (500 nS)
}

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ok
Now did you try what I posted and have confirmed working? The hex file is ready to go. You do not need to add anything to the code.

I'll try johans library when I get off this train, but I will guess it will work just fine

Jim

If you want a career with a known path - become an undertaker. Dead people don't sue! - Kartman

Please Read: Code-of-Conduct

Atmel Studio6.2/AS7, DipTrace, Quartus, MPLAB user

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

WEll ok, I stand corrected somewhat.

 

After putting Johans code into a new Studio project I too am getting garbage on the screen.  A little check of my connections shows that my DATA lines on my LCD are not on the same pins Johan has listed in his code.  He has the three control lines on PORTx 0, 1, 2 and the DATA lines on PORTx 4, 5, 6, 7.

 

My LCD is setup to have the control on the same pins as the code, but my DATA lines are on PORTx 3,4,5,6.  I am not in the mood to change the wires, so I will put this off until tomorrow sometime.

 

All for now. 

 

JIm

If you want a career with a known path - become an undertaker. Dead people don't sue! - Kartman

Please Read: Code-of-Conduct

Atmel Studio6.2/AS7, DipTrace, Quartus, MPLAB user

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

ki0bk wrote:

OP said:

.... I'm about to quit trying to understand the mechanisms. I have already did this in the past...... almost 2 years ago..... I didn't want to have to deal with all the details. Just the most important ones!

 

"Without out understanding the details, how can you only write high level functions?"

 

You have been given several WORKING examples, but have not taken the TIMEto look at them to understand and learn how they work!   What is the point of continuing?

 

 

 

I'm sorry I have not replied to you. Somehow your reply got unnoticed to me! Please bare with me regarding the amount code examples that have been posted here! I need to stick to one or two codes so that O don't loose focus or this will get really messy! I'm now trying to work with the code posted here:

http://www.johanekdahl.se/rwiki/doku.php?id=resources:avr_cldc_4-bit_c_demo

 

What I said about quitting was about trying to understand the mechanisms. Just use some working code and go on from there on!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

jgmdesign wrote:

WEll ok, I stand corrected somewhat.

 

After putting Johans code into a new Studio project I too am getting garbage on the screen.  A little check of my connections shows that my DATA lines on my LCD are not on the same pins Johan has listed in his code.  He has the three control lines on PORTx 0, 1, 2 and the DATA lines on PORTx 4, 5, 6, 7.

 

My LCD is setup to have the control on the same pins as the code, but my DATA lines are on PORTx 3,4,5,6.  I am not in the mood to change the wires, so I will put this off until tomorrow sometime.

 

All for now.

 

JIm

 

The first code I posted is from the link provided here and I just adjusted the defines to match my wiring and firstly I tried the code just after adjusting the defines. It didn't worked.

 

Then I tried the other code I got from somewhere else, and also adjusted the defines to match my wirings and it worked.

Then I tried to add timings to the first code as they were on the second code but I can only see garbage chars on the display!

I just got to work now and only in 8 hours time I can get my hands on the hardware again to try to make it work!

 

I have noticed also some differences in the init routine of the LCD on both codes and I tried to change the 1st code to match the second but still no good!

Last Edited: Fri. Oct 28, 2016 - 07:20 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

jgmdesign wrote:

Ok
Now did you try what I posted and have confirmed working? The hex file is ready to go. You do not need to add anything to the code.

I'll try johans library when I get off this train, but I will guess it will work just fine

Jim

 

The hardware is working properly! It is working with that code I got from other site!

Last Edited: Fri. Oct 28, 2016 - 07:19 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

What code you got from the other site? Johans, or the project I posted? This is becoming quite confusing.

Jim

If you want a career with a known path - become an undertaker. Dead people don't sue! - Kartman

Please Read: Code-of-Conduct

Atmel Studio6.2/AS7, DipTrace, Quartus, MPLAB user

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

jgmdesign wrote:

What code you got from the other site? Johans, or the project I posted? This is becoming quite confusing.

Jim

 

Code not working:

http://www.johanekdahl.se/rwiki/doku.php?id=resources:avr_cldc_4-bit_c_demo

 

Code working

http://web.alfredstate.edu/weimandn/programming/lcd/ATmega328/LCD_code_gcc_4d.html

 

This last code (the working  one) is not from this forum. It's a code I found somewhere that I managed to get working with my hardware!

Last Edited: Fri. Oct 28, 2016 - 11:26 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Did you try the project I posted using Fleury's library?  The one I made sure worked with your display type so you would have a solid working platform to look under the hood on?

 

I applaud you for searching for answers on your own, but you are truly making this thread confusing to follow, much less provide proper assistance.

 

Jim

If you want a career with a known path - become an undertaker. Dead people don't sue! - Kartman

Please Read: Code-of-Conduct

Atmel Studio6.2/AS7, DipTrace, Quartus, MPLAB user

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

jgmdesign wrote:

Did you try the project I posted using Fleury's library?  The one I made sure worked with your display type so you would have a solid working platform to look under the hood on?

 

I applaud you for searching for answers on your own, but you are truly making this thread confusing to follow, much less provide proper assistance.

 

Jim

 

I only tested 2 codes, the ones I posted at post #39.

If you mean the code at post #19, no, I haven't tested it. I'll test it today when I get home!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

MAke sure you confirm your wiring as the connections in my post are different than in Johans.

 

JIm

If you want a career with a known path - become an undertaker. Dead people don't sue! - Kartman

Please Read: Code-of-Conduct

Atmel Studio6.2/AS7, DipTrace, Quartus, MPLAB user

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

jgmdesign wrote:

MAke sure you confirm your wiring as the connections in my post are different than in Johans.

 

JIm

 

I'll make my best to make things work.

I'm sorry that I have not been able to follow everyone's directions! Fortunately there are many people helping, therefore, it's likely that I miss one post or another!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

 

This last code (the working  one) is not from this forum. It's a code I found somewhere that I managed to get working with my hardware!

You got that code by following the second recommendation in reply #36.  A simple Google search for LCD programming examples should get you there as well.  It pops up first when searching from the US, I don't know about other countries.

 

Don

Pages