Professional Documents
Culture Documents
Display data RAM (DDRAM) stores display data represented in 8-bit character codes.
Its extended capacity is 80 X 8 bits, or 80 characters. The area in display data RAM
(DDRAM) that is not used for display can be used as general data RAM. So whatever
you send on the DDRAM is actually displayed on the LCD. For LCDs like 1x16, only
16 characters are visible, so whatever you write after 16 chars is written in DDRAM
but is not visible to the user.
Figures below will show you the DDRAM addresses of 1 Line, 2 Line and 4 Line LCDs.
Now you might be thinking that when you send an ascii value to DDRAM, how the
character is displayed on LCD? so the answer is CGROM. The character generator
ROM generates 5 x 8 dot or 5 x 10 dot character patterns from 8-bit character codes
(see Figure 5 and Figure 6 for more details). It can generate 208 5 x 8 dot character
patterns and 32 5 x 10 dot character patterns. Userdefined character patterns are
also available by mask-programmed ROM.
As you can see in both the code maps, the character code from 0x00 to 0x07 is
occupied by the CGRAM characters or the user defined characters. If user want to
display the fourth custom character then the code to display it is 0x03 i.e. when
user send 0x03 code to the LCD DDRAM then the fourth user created charater or
patteren will be displayed on the LCD.
As clear from the name, CGRAM area is used to create custom characters in LCD. In
the character generator RAM, the user can rewrite character patterns by program.
For 5 x 8 dots, eight character patterns can be written, and for 5 x 10 dots, four
character patterns can be written. Later in this tutorial i will explain how to use
CGRAM area to make custom character and also making animations to give nice
effects to your application.
BF - Busy Flag
Busy Flag is an status indicator flag for LCD. When we send a command or data to
the LCD for processing, this flag is set (i.e BF =1) and as soon as the instruction is
executed successfully this flag is cleared (BF = 0). This is helpful in producing and
exact ammount of delay. for the LCD processing.
To read Busy Flag, the condition RS = 0 and R/W = 1 must be met and The MSB of
the LCD data bus (D7) act as busy flag. When BF = 1 means LCD is busy and will
not accept next command or data and BF = 0 means LCD is ready for the next
command or data to process.
There are two 8-bit registers in HD44780 controller Instruction and Data register.
Instruction register corresponds to the register where you send commands to LCD
e.g LCD shift command, LCD clear, LCD address etc. and Data register is used for
storing data which is to be displayed on LCD. when send the enable signal of the
LCD is asserted, the data on the pins is latched in to the data register and data is
then moved automatically to the DDRAM and hence is displayed on the LCD.
Data Register is not only used for sending data to DDRAM but also for CGRAM, the
address where you want to send the data, is decided by the instruction you send to
LCD. We will discuss more on LCD instuction set further in this tutorial.
No.
Instruction
Hex
0x30
0x38
No.
Instruction
Hex
0x20
0x28
Entry Mode
0x06
0x08
Display on Cursor on
0x0E
0x0C
0x0F
10
0x18
11
0x1C
12
0x10
13
0x14
14
0x01
15
0x80 + address*
16
0x40 + address**
No.
Instruction
Hex
Table 4: Frequently used commands and instructions for LCD
Before using the LCD for display purpose, LCD has to be initialized either by the
internal reset circuit or sending set of commands to initialize the LCD. It is the user
who has to decide whether an LCD has to be initialized by instructions or by internal
reset circuit. we will dicuss both ways of initialization one by one.
Initialization by internal Reset Circuit
An internal reset circuit automatically initializes the HD44780U when the power is
turned on. The following instructions are executed during the initialization. The busy
flag (BF) is kept in the busy state until the initialization ends (BF = 1). The busy
state lasts for 10 ms after VCC rises to 4.5 V.
Display clear
Function set:
DL = 1; 8-bit interface data
N = 0; 1-line display
F = 0; 5 x 8 dot character font
Display on/off control:
D = 0; Display off
C = 0; Cursor off
B = 0; Blinking off
Entry mode set:
I/D = 1; Increment by 1
S = 0; No shift
Note: If the electrical characteristics conditions listed under the table Power Supply
Conditions Using Internal Reset Circuit are not met, the internal reset circuit will not
operate normally and will fail to initialize the HD44780U. For such a case, initialization must be performed by the MCU as explained in the section, Initializing by
Instruction.
As mentioned in the Note, there are certain condtions that has to be met, if user
want to use initialization by internal reset circuit. These conditions are shown in the
Table 5 below.
Figure 7 shows the test condition which are to be met for internal reset circuit to be
active.
Now the problem with the internal reset circuit is, it is highly dependent on power
supply, to meet this critical power supply conditions is not hard but are difficult to
achive when you are making a simple application. So usually the second menthod
i.e. Initialization by instruction is used and is recommended most of the time.
Initialization by instructions
Initializing LCD with instructions is really simple. Given below is a flowchart that
describles the step to follow, to initialize the LCD.
As you can see from the flow chart, the LCD is initialized in the following sequence...
The first 3 commands are usually not required but are recomended when you are
using 4-bit interface. So you can program the LCD starting from step 7 when
working with 8-bit interface. Function set command depends on what kind of LCD
you are using and what kind of interface you are using (see Table 4 in LCD
Command section).
LCD Entry mode:
From Table 3 in command section, you can see that the two bits decide the entry
mode for LCD, these bits are:
a) I/D - Increment/Decrement bit
b) S - Display shift.
With these two bits we get four combinations of entry mode which are
0x04,0x05,0x06,0x07 (see table 3 in LCD Command section). So we get different
results with these different entry modes. Normally entry mode 0x06 is used which is
No shift and auto incremement. I recommend you to try all the possible entry modes
and see the results, I am sure you will be surprised.
LCD_data equ P2
LCD_init:
mov LCD_data,#38H ;Function set: 2 Line, 8-bit, 5x7 dots
clr LCD_rs
clr LCD_rw
setb LCD_en
;Enable H->
L
clr LCD_en
acall LCD_busy
clr LCD_rw
setb LCD_en
;Enable H->
L
clr LCD_en
acall LCD_busy
clr LCD_rs
clr LCD_rw
setb LCD_en
L
;Enable H->
clr LCD_en
acall LCD_busy
clr LCD_rw
setb LCD_en
;Enable H->
L
clr LCD_en
acall LCD_busy
ret
Now we can do the same thing in C, I am giving example using Keil C. Similar code
can be written for SDCC.
#include <AT89X51.H>
.
#define LCD_data P2
#define LCD_D7 P2_7
#define LCD_rs P1_0
#define LCD_rw P1_1
#define LCD_en P1_2
void LCD_init()
{
LCD_data = 0x38;
LCD_rs = 0;
LCD_rw = 0;
LCD_en = 1;
//Enable H->
L
LCD_en = 0;
LCD_busy();
LCD_data = 0x0F;
LCD_rs = 0;
LCD_rw = 0;
LCD_en = 1;
//Enable H->
L
LCD_en = 0;
LCD_busy();
LCD_data = 0x01;
//Clear LCD
LCD_rs = 0;
LCD_rw = 0;
LCD_en = 1;
//Enable H->
L
LCD_en = 0;
LCD_busy();
LCD_data = 0x06;
LCD_rs = 0;
LCD_rw = 0;
LCD_en = 1;
//Enable H->
L
LCD_busy();
With the help of the above code, you are able to initialize the LCD. Now there is a
function/subroutine coming in the code i.e. LCD_busy, which is used to put delay for
LCD so that there should not be any command or data sent to the LCD untill it finish
executing the command. More on this delay routine is explained in the next section.
So following the above steps we can write the code in assembly as below...
1.
2.
3.
LCD_busy:
4.
setb LCD_D7
5.
setb LCD_en
6.
clr
7.
setb LCD_rw
8.
check:
9.
10.
LCD_rs
clr
LCD_en
;Enable H->
11.
setb LCD_en
12.
jb
13.
ret
The equivalent C code Keil C compiler. Similar code can be written for SDCC.
1.
void LCD_busy()
2.
3.
4.
LCD_D7 = 1;
5.
LCD_en = 1;
6.
LCD_rs = 0;
7.
LCD_rw = 1;
8.
9.
while(LCD_D7){
10.
11.
LCD_en = 0;
L
12.
LCD_en = 1;
13.
14.
15.
The above routine will provide the necessary delay for the instructions to complete. If you dont want
to read the busy flag you can simply use a delay routine to provide the a specific ammount of delay.
A simple delay routine for the LCD is given below.
1.
LCD_busy:
2.
3.
mov r7,#50H
back:
4.
mov r6,#FFH
5.
djnz r6,$
6.
djnz r7,back
7.
ret
1.
void LCD_busy()
2.
3.
4.
for(i=0;i<50;i++)
5.
6.
for(j=0;j<255;j++);
}
Now we are ready with the initialization routine and the busy routine for LCD. In the next section we
will see how to send data and command to the LCD
Keeping these steps in mind we can write LCD command routine as.
1.
2.
3.
4.
LCD_command:
5.
mov LCD_data,A
6.
clr LCD_rs
7.
clr LCD_rw
8.
setb LCD_en
;Enable H->
9.
10.
clr LCD_en
11.
acall LCD_busy
12.
ret
13.
14.
15.
16.
17.
18.
; mov a,#01H
19.
; acall LCD_command
The equivalent C code Keil C compiler. Similar code can be written for SDCC.
1.
2.
3.
LCD_data = var;
4.
LCD_rs = 0;
5.
LCD_rw = 0;
6.
LCD_en = 1;
//Enable H->
7.
8.
L
LCD_en = 0;
9.
LCD_busy();
10.
11.
12.
13.
// e.g.
14.
//
15.
// LCD_command(0x01);
1.
2.
Bit7 6 5 4 3 2 1 0
1 AD6 AD5 AD4 AD3 AD2 AD1 AD0
The seventh bit is always 1, and bit 0 to 7 are DDRAM address (See the introduction section of LCD).
so if you want to put the cursor on first position the address will be '0b0000000' in binary and 7th bit
is 1. so address will be 0x80, so for DDRAM all address starts from 0x80.
For 2 line and 16 character LCD. The address from 0x80 to 0x8F are visible on first line and 0xC0 to
0xCF is visible on second line, rest of the DDRAM area is still available but is not visible on the LCD, if
you want to check this thing, then simply put a long sting greater than 16 character and shift the
entire display, you will see all the missing character coming from the back.. this way you can make
scrolling line on LCD (see more on shifting display in commands section).
Below is an example for setting cursor position on LCD.
1.
2.
3.
4.
mov a,#83H
5.
acall LCD_command
1.
2.
// as we done before
3.
LCD_command(0x83);
Keeping these steps in mind we can write LCD command routine as.
1.
2.
3.
4.
LCD_senddata:
5.
mov LCD_data,A
6.
setb LCD_rs
7.
clr LCD_rw
8.
setb LCD_en
9.
10.
clr LCD_en
11.
acall LCD_busy
12.
ret
13.
14.
15.
16.
17.
18.
19.
; mov a,#'A'
20.
21.
The equivalent C code Keil C compiler. Similar code can be written for SDCC.
1.
2.
3.
LCD_data = var;
4.
LCD_rs = 1;
5.
LCD_rw = 0;
6.
LCD_en = 1;
//Enable H->
7.
8.
LCD_en = 0;
9.
LCD_busy();
10.
11.
12.
13.
// e.g.
14.
//
15.
// LCD_senddata('A');
Now you have seen that its really easy to send command and data to LCD. Now what if we have a
string to send to LCD? how we are going to do that?
Is simple, we will store the LCD string in the ROM of controller and call the string character by
character. A simple exmple is shown below.
1.
2.
3.
LCD_sendstring:
4.
clr a
5.
movc a,@a+dptr
6.
jz
7.
acall lcd_senddata
8.
inc dptr
9.
sjmp LCD_sendstring
10.
exit
exit:
11.
ret
;End of routine
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
; acall LCD_sendstring
22.
23.
24.
;Send string
25.
; To store a string..
26.
27.
; my_string:
28.
29.
30.
31.
The equivalent C code Keil C compiler. Similar code can be written for SDCC.
1.
2.
3.
while(*var)
4.
5.
6.
7.
8.
// e.g.
9.
//
10.
// LCD_sendstring("LCD Tutorial");
Now we are ready with sending data and sending command to LCD. Now the last and final section
which is creating custom characters or patterns to display on LCD.