How does arduino control a 16x2 or 20x 4 LCD display?

Hi guys,

I would like to know how an arduino controls a LCD display. No I am not asking about the connections between the arduino and the LCD. I want to understand the working principle as to how the signals are sent, etc etc.

Did some research and this is what I understand:
The RS pin chooses between sending commands and sending characters (data). If it is 0V, it send commands such as clearing of LCD, setting cursor, etc, by using the command register. and if it is 5V, it sends characters such as printing characters, etc by using the data register.

The R/W pin is usually tied to ground as we are only interested in writing to the LCD.

The enable Pin switches from high to low so that the lcd can see the state of the other pins and execute the commands.

But I do not understand some things:
How and when is the low and high signal given to the RS pin to tell it to send commands/characters?
Similarly, how and when is the enable pin given signals to tell it to swtch from high to low?
How does the switching from high to low allows the lcd to see the state of the other pins and excute commands?
And the data bus D0-D7, how and what type of signals are given from the arduino to each of the D pins(namely D4-D7)?

Hope someone could help to give a better explanation as to how the Arduino controls the LCD display. Thanks.

Search for a datasheet, and read about the signals.
Or try this link, http://itee.uq.edu.au/~design/software/xilinx_tut4/#Function%20Description%20of%20LCD

Well, first of all have you been using such display yet ?
If yes, you'd know that you need to use the library to that.
That library is like a windows device driver and helps the system to control the display.
Assuming you are a windows user, you can look up the library by browsing to your Arduino IDE install location, go to libraries and then to liquidCrystal.
You can read the files using wordpad (notepad will result in a hard to read sheet).
I guess reading those files might give you some clues to what you want to know.

Then, some of the control pins aren't used in most cases.
If you have the display exclisively connected to the pins, you don't need to switch the enable pin and leave it in the enabled state.
As you typically don't need to read from the display you also don't need to switch that pin.

Your analysis of the signals is essentially correct.

But I do not understand some things: ...

For this you will need to look at the datasheet, specifically Figure 25 which is the timing diagram for writing information to the LCD controller. It's also shown in the bottom part of Figure 5 of the link provided by Erdin. That link points to an abbreviated version of the datasheet. You read the diagram from left to right and top to bottom.

How and when is the low and high signal given to the RS pin to tell it to send commands/characters?

This is done first as shown at the top of the diagram. If you are using the R/W line you can set it at the same time. Note that E must be low while you are dealing with RS.

You then must wait for a specific amount of time (tAS) before driving E high.

Somewhere in here you must also deal with the data lines. The diagram shows them being implemented after E is driven high but careful study of the diagram will reveal that it is OK to deal with them when you deal with RS and R/W and this is when most programmers do so.

Similarly, how and when is the enable pin given signals to tell it to swtch from high to low?

You must make sure that the data lines are stable for a specific amount of time (tDSW) before you drive E low.

How does the switching from high to low allows the lcd to see the state of the other pins and excute commands?

That's just how the LCD controller is programmed to work.

And the data bus D0-D7, how and what type of signals are given from the arduino to each of the D pins(namely D4-D7)?

You determine this from the Instruction set given in Table 6 of the data sheet. This is figure 4 in the link above.

Don

Check out theese two documents for information.

Part 1
Part 2

Thanks for the reply guys. Really helped me understand more.

Anyway, I was looking at the Liquidcrystal library code as follows:

#ifndef LiquidCrystal_h
#define LiquidCrystal_h

#include <inttypes.h>
#include "Print.h"

// commands
#define LCD_CLEARDISPLAY 0x01
#define LCD_RETURNHOME 0x02
#define LCD_ENTRYMODESET 0x04
#define LCD_DISPLAYCONTROL 0x08
#define LCD_CURSORSHIFT 0x10
#define LCD_FUNCTIONSET 0x20
#define LCD_SETCGRAMADDR 0x40
#define LCD_SETDDRAMADDR 0x80

// flags for display entry mode
#define LCD_ENTRYRIGHT 0x00
#define LCD_ENTRYLEFT 0x02
#define LCD_ENTRYSHIFTINCREMENT 0x01
#define LCD_ENTRYSHIFTDECREMENT 0x00

// flags for display on/off control
#define LCD_DISPLAYON 0x04
#define LCD_DISPLAYOFF 0x00
#define LCD_CURSORON 0x02
#define LCD_CURSOROFF 0x00
#define LCD_BLINKON 0x01
#define LCD_BLINKOFF 0x00

// flags for display/cursor shift
#define LCD_DISPLAYMOVE 0x08
#define LCD_CURSORMOVE 0x00
#define LCD_MOVERIGHT 0x04
#define LCD_MOVELEFT 0x00

// flags for function set
#define LCD_8BITMODE 0x10
#define LCD_4BITMODE 0x00
#define LCD_2LINE 0x08
#define LCD_1LINE 0x00
#define LCD_5x10DOTS 0x04
#define LCD_5x8DOTS 0x00

class LiquidCrystal : public Print {
public:
  LiquidCrystal(uint8_t rs, uint8_t enable,
                uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3,
                uint8_t d4, uint8_t d5, uint8_t d6, uint8_t d7);
  LiquidCrystal(uint8_t rs, uint8_t rw, uint8_t enable,
                uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3,
                uint8_t d4, uint8_t d5, uint8_t d6, uint8_t d7);
  LiquidCrystal(uint8_t rs, uint8_t rw, uint8_t enable,
                uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3);
  LiquidCrystal(uint8_t rs, uint8_t enable,
                uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3);

  void init(uint8_t fourbitmode, uint8_t rs, uint8_t rw, uint8_t enable,
            uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3,
            uint8_t d4, uint8_t d5, uint8_t d6, uint8_t d7);
   
  void begin(uint8_t cols, uint8_t rows, uint8_t charsize = LCD_5x8DOTS);

  void clear();
  void home();

  void noDisplay();
  void display();
  void noBlink();
  void blink();
  void noCursor();
  void cursor();
  void scrollDisplayLeft();
  void scrollDisplayRight();
  void leftToRight();
  void rightToLeft();
  void autoscroll();
  void noAutoscroll();

  void createChar(uint8_t, uint8_t[]);
  void setCursor(uint8_t, uint8_t);
  virtual void write(uint8_t);
  void command(uint8_t);
private:
  void send(uint8_t, uint8_t);
  void write4bits(uint8_t);
  void write8bits(uint8_t);
  void pulseEnable();

  uint8_t _rs_pin; // LOW: command.  HIGH: character.
  uint8_t _rw_pin; // LOW: write to LCD.  HIGH: read from LCD.
  uint8_t _enable_pin; // activated by a HIGH pulse.
  uint8_t _data_pins[8];

  uint8_t _displayfunction;
  uint8_t _displaycontrol;
  uint8_t _displaymode;

  uint8_t _initialized;

  uint8_t _numlines,_currline;
};

#endif

May I know what do all the "0 x 01", "0 x 02" etc etc mean?
And what does the "uint8_t rs" etc etc mean?

May I know what do all the "0 x 01", "0 x 02" etc etc mean?

Those are sometimes referred to as 'magic numbers' because they appear to only have some mysterious relationship with the real world.

To understand them you have to convert them to binary and then you will have a better chance of finding out how they relate to the information in the datasheet.

Let's start with this batch where I have added the binary values as comments:

// commands
#define LCD_CLEARDISPLAY 0x01   // 0 0 0 0 0 0 0 1
#define LCD_RETURNHOME 0x02     // 0 0 0 0 0 0 1 0
#define LCD_ENTRYMODESET 0x04   // 0 0 0 0 0 1 0 0
#define LCD_DISPLAYCONTROL 0x08 // 0 0 0 0 1 0 0 0
#define LCD_CURSORSHIFT 0x10    // 0 0 0 1 0 0 0 0
#define LCD_FUNCTIONSET 0x20    // 0 0 1 0 0 0 0 0
#define LCD_SETCGRAMADDR 0x40   // 0 1 0 0 0 0 0 0
#define LCD_SETDDRAMADDR 0x80   // 1 0 0 0 0 0 0 0

Now compare this with the command set (Table 6) in the datasheet and you will see that each of the commands in that table starts out (reading left to right) with a bunch of 0s to the left of the first '1' and the first '1' is associated with a different data byte for each command.
Here is a list of those commands with the location of the leftmost '1' identified.

Clear display ............. DB0
Return home ............... DB1
Entry mode set ............ DB2
Display on/off ............ DB3
Cursor or display shift ... DB4
Function set .............. DB5
Set CGRAM address ......... DB6
Set DDRAM address ......... DB7

Now compare the two lists and you should be able to tell where the 'magic numbers' came from.

Don

floresta:

May I know what do all the "0 x 01", "0 x 02" etc etc mean?

Those are sometimes referred to as 'magic numbers' because they appear to only have some mysterious relationship with the real world.

To understand them you have to convert them to binary and then you will have a better chance of finding out how they relate to the information in the datasheet.

Let's start with this batch where I have added the binary values as comments:

// commands

#define LCD_CLEARDISPLAY 0x01   // 0 0 0 0 0 0 0 1
#define LCD_RETURNHOME 0x02     // 0 0 0 0 0 0 1 0
#define LCD_ENTRYMODESET 0x04   // 0 0 0 0 0 1 0 0
#define LCD_DISPLAYCONTROL 0x08 // 0 0 0 0 1 0 0 0
#define LCD_CURSORSHIFT 0x10    // 0 0 0 1 0 0 0 0
#define LCD_FUNCTIONSET 0x20    // 0 0 1 0 0 0 0 0
#define LCD_SETCGRAMADDR 0x40   // 0 1 0 0 0 0 0 0
#define LCD_SETDDRAMADDR 0x80   // 1 0 0 0 0 0 0 0




Now compare this with the command set (Table 6) in the datasheet and you will see that each of the commands in that table starts out (reading left to right) with a bunch of 0s to the left of the first '1' and the first '1' is associated with a different data byte for each command.
Here is a list of those commands with the location of the leftmost '1' identified.



Clear display ............. DB0
Return home ............... DB1
Entry mode set ............ DB2
Display on/off ............ DB3
Cursor or display shift ... DB4
Function set .............. DB5
Set CGRAM address ......... DB6
Set DDRAM address ......... DB7




Now compare the two lists and you should be able to tell where the 'magic numbers' came from.

Don

Oooohh now that makes a lot of sense.

But only the last 2 digits ( those 02,01 etc) are the hexadecimal value right? Then what does the 0 x in front signify?

And say I am using 4 bit data transfer mode.. So does that mean the D0-D3 are always low?

#ifndef LiquidCrystal_h
#define LiquidCrystal_h

#include <inttypes.h>
#include "Print.h"

class LiquidCrystal : public Print {
public:
  LiquidCrystal(uint8_t rs, uint8_t enable,
                uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3,
                uint8_t d4, uint8_t d5, uint8_t d6, uint8_t d7);
  LiquidCrystal(uint8_t rs, uint8_t rw, uint8_t enable,
                uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3,
                uint8_t d4, uint8_t d5, uint8_t d6, uint8_t d7);
  LiquidCrystal(uint8_t rs, uint8_t rw, uint8_t enable,
                uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3);
  LiquidCrystal(uint8_t rs, uint8_t enable,
                uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3);

  void init(uint8_t fourbitmode, uint8_t rs, uint8_t rw, uint8_t enable,
            uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3,
            uint8_t d4, uint8_t d5, uint8_t d6, uint8_t d7);
   
  void begin(uint8_t cols, uint8_t rows, uint8_t charsize = LCD_5x8DOTS);

  void clear();
  void home();

  void noDisplay();
  void display();
  void noBlink();
  void blink();
  void noCursor();
  void cursor();
  void scrollDisplayLeft();
  void scrollDisplayRight();
  void leftToRight();
  void rightToLeft();
  void autoscroll();
  void noAutoscroll();

  void createChar(uint8_t, uint8_t[]);
  void setCursor(uint8_t, uint8_t);
  virtual void write(uint8_t);
  void command(uint8_t);
private:
  void send(uint8_t, uint8_t);
  void write4bits(uint8_t);
  void write8bits(uint8_t);
  void pulseEnable();

  uint8_t _rs_pin; // LOW: command.  HIGH: character.
  uint8_t _rw_pin; // LOW: write to LCD.  HIGH: read from LCD.
  uint8_t _enable_pin; // activated by a HIGH pulse.
  uint8_t _data_pins[8];

  uint8_t _displayfunction;
  uint8_t _displaycontrol;
  uint8_t _displaymode;

  uint8_t _initialized;

  uint8_t _numlines,_currline;
};

#endif

And would be grateful if someone could explain to me what the remaining codes(as shown above) are doing..

But only the last 2 digits ( those 02,01 etc) are the hexadecimal value right? Then what does the 0 x in front signify?

The 0x tells the C compiler which number system you are using. For example the number 01 represents the same quantity in binary, decimal, and hex but the number 10 is different in all three systems.

And say I am using 4 bit data transfer mode.. So does that mean the D0-D3 are always low?

They are usually left floating but tying them low isn't a bad idea. The LCD controller ignores those four bits when it is in the 4-bit mode.

And would be grateful if someone could explain to me what the remaining codes(as shown above) are doing..

That is precisely the purpose for comments in a program. Without comments it is difficult for anyone not intimately familiar with the program and/or the device being controlled to figure out what is going on. This includes the original programmer after the passage of time.

Unfortunately there are only three comments in the code fragment that you have posted. Those three are intended to remind the programmer what each pin does, which isn't a bad idea except that the third one (about the Enable pin) is incorrect.

I am not a C programmer but it looks to me that the stuff you have quoted in the post just above consists of instructions to the C compiler, not instructions to the LCD controller.

Don

floresta:
And say I am using 4 bit data transfer mode.. So does that mean the D0-D3 are always low?

They are usually left floating but tying them low isn't a bad idea. The LCD controller ignores those four bits when it is in the 4-bit mode.
[/quote]

Please correct me if I am wrong. In 8 bit data mode, the 8 binary values are sent all at once. But for 4 bit mode, the 8 values are broken into 2 sets, and sent in 2 seperate codes am I right? So the difference between the 2 is just the speed of transferring the data?

And would be grateful if someone could explain to me what the remaining codes(as shown above) are doing..
That is precisely the purpose for comments in a program. Without comments it is difficult for anyone not intimately familiar with the program and/or the device being controlled to figure out what is going on. This includes the original programmer after the passage of time.

Unfortunately there are only three comments in the code fragment that you have posted. Those three are intended to remind the programmer what each pin does, which isn't a bad idea except that the third one (about the Enable pin) is incorrect.

I am not a C programmer but it looks to me that the stuff you have quoted in the post just above consists of instructions to the C compiler, not instructions to the LCD controller.

Don

Guess I went too detail into knowing about the codes.
So in summary, the arduino controls the RS, R/W, E and the DB pins right?
And as for the HD44780 timing diagram, the timings are determined and controlled by the HD44780 chip itself? Or does arduino control that too?

So the difference between the 2 is just the speed of transferring the data?

Yes - but the difference in speed due to the requirement for two data transfers is insignificant compared to the time it takes for the LCD controller to deal with the data once it is received.

And as for the HD44780 timing diagram, the timings are determined and controlled by the HD44780 chip itself? Or does arduino control that too?

The timings are determined by the requirements of the HD44780 chip but it is the program in the Arduino that actually makes sure that the timing requirements of the HD44780 are met as it generates the signals.

Don