You are on page 1of 19

8.

4 A Case Study: using LCD Module on DE2 Board


8.4.1 LCD module on DE2 board

On the Altera DE2 FPGA development board, there is a Liquid Crystal Display (LCD)
module that can be used to display text by sending appropriate commands from FPGA chip to the
LCD module. There is a built-in controller HD44780 in the LCD module to drive the display. The
interface between FPGA chip and the LCD module is shown in Fig.8.22.








Fig.8.22 The interface between FPGA chip and LCD module

LCD_ON (high-active) turns on the power of LCD module. LCD_BLON controls the LCD
back light. LCD_DATA [0:7] is an 8-bit data bus for their data communication. LCD_RW
defines the direction of data bus (LCD_RW=1 for read: from LCD to FPGA; LCD_RW=0 for
write: from FPGA to LCD). LCD_EN is a read/write enable signal, i.e., a falling edge on
LCD_EN is required for a data read/write operation. LCD_RS is a Data/Command select signal,
i.e., LCD_DATA [0:7] is data for display when LCD_RS is 1; LCD_DATA is a command
(display clear, cursor shift and address information etc.) if LCD_RS is 0. The FPGA pin
assignment of the interface is shown in Table.8.1




FPGA
L
C
D

d
i
s
p
l
a
y
(
c
o
n
t
r
o
l
l
e
r

H
D
4
4
7
8
0
i
n
c
l
u
d
e
d
)
LCD_ON
LCD_BLON
LCD_RW
LCD_RS
LCD_EN
LCD_DATA[0:7]
Table 8.1 LCD related pin assignments on DE2


8.4.2 Function description of LCD module

1) Registers, DDRAM, and CGROM in LCD module
The controller HD44780, built in LCD module, plays an important role in the LCD display
task. The controller communicates with FPGA, and controls the LCD display. The block diagram
of HD44780 is shown in Fig.8.23.
The HD44780 has two 8-bit registers (IR, and DR) to store information sent from FPGA.
The operation and selection of two registers are defined by Table 8.2. The IR (instruction register)
stores instruction codes, such as display clear, cursor shift, and address information for display
data RAM (DDRAM) and character generator RAM (CGRAM). The IR can only be written from
FPGA. The DR (data register) temporarily stores data to be written into DDRAM or CGRAM and
temporarily stores data read from DDRAM or CGRAM. Here we only consider the situation that
data are written into DDRAM or CGRAM. So, in our following discussion, LCD_RW is always
set to 0 for writing only, unless otherwise stated. By LCD_RS, the data LCD_DATA [0:7] is
interpreted either as instruction codes (when LCD_RS=0) or as display data (when LCD_RS=1).

Fig.8.23 HD44780 block diagram


Table 8.2 Register Selection











Fig.8.24 the relationship between DDRAM addresses and positions on LCD
The address counter (AC) receives an initial address through IR based on a
command/instruction code, and assigns and updates addresses to both DDRAM and CGRAM.
The DDRAM is used to store the display data represented in 8-bit character codes and sent from
FPGA. Each address of DDRAM corresponds to a position on the LCD. The relationship between
DDRAM addresses and positions on LCD is shown in Fig. 8.24 (2-by-16 character display).
Character Generator ROM (CGROM): The CGROM generate 5x8 dot or 5x10 dot
character patterns from 8-bit character codes. See Table 8.3. For example, in order to display 2 on
the first line position 5, 8h32 should be written into the address (AC) 8h04 of DDRAM.

Table 8.3 CGROM pattern (5x8 dots)























2) Instruction table
Table 8.4 includes all available instructions to control LCD. The MPU/FPGA will send a
sequence of instructions to LCD with appropriate timing constraints. The instructions are divided
into two types: 1) command; 2) data transfer. The command instructions (RS=0) are used to
initialize the LCD or set required address. The data transfer instructions are used to send the data
(to be displayed on LCD) to LCD or read the data information from RAM in LCD built-in
controller (HD44780).

Table 8.4 Instruction table

3) Timing requirements
The MPU/FPGA should meet the following timing requirements when it communicates with
LCD. For write operation (R/W=0), a negative edge of E (i.e. LCD_EN) is required to send DB
(i.e. LCD_DATA [0:7]) to LCD, as shown in Fig.8.25. For read operation, it is the same except
that R/W needs to be 1.


Fig. 8.25 Writing operation





8.4.3 Initializing of LCD

FPGA chip needs to send a series of initialization commands to initialize the LCD. These
commands include function set, display off, display clear, and entry mode set. The datasheet
provides a typical initialization process shown in Fig.8.26. Thus an entire flowchart of LCD tasks
is illustrated in Fig.8.27.




Fig. 8.26 Initialization steps










Initialization Text display
Fig.8.27 Flowchart of LCD display

8.4.4 An example: digital clock on LCD

We will design a digital clock on Altera DE2 board, shown in Fig.8.28. The time will be
displayed on both 7-segment displays HEX0 to HEX7 and LCD module. The digital_clock block
delivers BCD of each digit of time (hour, minute, and second) to module fsm_lcd, which is an
FSM that communicates with the LCD for display.

















Fig.8.28 Diagram of a digital clock on DE2
reset
Function set
Display off
Display clear
Display on
Mode set
Display char 1
Display other chars
Display chars in 2
nd
line
Return home
Set DDRAM address for 2
nd

line display

State transition diagram for fsm_lcd

Based on the flow of LCD tasks and timing requirements, a state transition diagram is
developed as follows. Each task in Fig.8.27 requires two states since a falling edge on LCD_EN
is needed to write commands/data (LCD_DATA) to DDRAM




























(to be continued)













Reset1
{LCD_EN, LCD_RS}=2b10
LCD_DATA_VALUE=8h38

Drop_e1
{LCD_EN, LCD_RS}=2b00
LCD_DATA_VALUE=8h38

Reset2
{LCD_EN, LCD_RS}=2b10
LCD_DATA_VALUE=8h38

Drop_e2
{LCD_EN, LCD_RS}=2b00
LCD_DATA_VALUE=8h38

Reset3
{LCD_EN, LCD_RS}=2b10
LCD_DATA_VALUE=8h38

Drop_e3
{LCD_EN, LCD_RS}=2b00
LCD_DATA_VALUE=8h38

Func_set
{LCD_EN, LCD_RS}=2b10
LCD_DATA_VALUE=8h38

Drop_e4
{LCD_EN, LCD_RS}=2b00
LCD_DATA_VALUE=8h38

Display_off
{LCD_EN, LCD_RS}=2b10
LCD_DATA_VALUE=8h08

{LCD_EN, LCD_RS}=2b00
LCD_DATA_VALUE=8h08

Drop_e5
Display_clr
{LCD_EN, LCD_RS}=2b10
LCD_DATA_VALUE=8h01

Drop_e6
{LCD_EN, LCD_RS}=2b00
LCD_DATA_VALUE=8h08















































Display_on
Drop_e7
{LCD_EN, LCD_RS}=2b10
LCD_DATA_VALUE=8h0c

Mode_set
{LCD_EN, LCD_RS}=2b00
LCD_DATA_VALUE=8h0c

{LCD_EN, LCD_RS}=2b10
LCD_DATA_VALUE=8h06

{LCD_EN, LCD_RS}=2b00
LCD_DATA_VALUE=8h06

{LCD_EN, LCD_RS}=2b11
LCD_DATA_VALUE=
{3b011,bcd_hrd1}

{LCD_EN, LCD_RS}= 2b11
LCD_DATA_VALUE=

{LCD_EN, LCD_RS}=2b01
LCD_DATA_VALUE=

{LCD_EN, LCD_RS}=2b11
LCD_DATA_VALUE=

{LCD_EN, LCD_RS}=2b01
LCD_DATA_VALUE=

{LCD_EN, LCD_RS}=2b11
LCD_DATA_VALUE=

Drop_e8
Write_h1
Drop_e9
Write_h0
Drop_e10
Write_dot1
Drop_e11
Write_m1
{LCD_EN, LCD_RS}=2b01
LCD_DATA_VALUE=
{3b011,bcd_hrd1}

Drop_e12
Write_m0
Drop_e13
{LCD_EN, LCD_RS}=2b01
LCD_DATA_VALUE=


{LCD_EN, LCD_RS}=2b11
LCD_DATA_VALUE=


{LCD_EN, LCD_RS}=2b01
LCD_DATA_VALUE=


Write_dot2
{LCD_EN, LCD_RS}=2b11
LCD_DATA_VALUE=


{LCD_EN, LCD_RS}=2b01
LCD_DATA_VALUE=


{LCD_EN, LCD_RS}=2b11
LCD_DATA_VALUE=


Drop_e14
Drop_e15
Write_s1
Write_s0
{LCD_EN, LCD_RS}=2b11
LCD_DATA_VALUE=


{LCD_EN, LCD_RS}=2b01
LCD_DATA_VALUE=


Drop_e16
{LCD_EN, LCD_RS}=2b01
LCD_DATA_VALUE=




















Continue for other letters of student name











``




Notes:
1) Each task is followed by drop_e state. In drop_e sate, LCD_EN drops to zero while other
output signals keep the same.
2) LCD_RS=0 for selecting instruction register (IR) (i.e. command), LCD_RS=1 for selecting
data register (DR) (i.e. data display)
3) Set_add_line1: before going back to display digit bcd_hrd1, set the DDRAM address for the
position of displaying bcd_hrd1.
Set_add_line2 {LCD_EN, LCD_RS}=2b10
LCD_DATA_VALUE=8hc0;

Drop_e17
{LCD_EN, LCD_RS}=2b00
LCD_DATA_VALUE=8hc0;

Write_J
{LCD_EN, LCD_RS}=2b11
LCD_DATA_VALUE=


Drop_e18
{LCD_EN, LCD_RS}=2b01
LCD_DATA_VALUE=


Set_add_line1
{LCD_EN, LCD_RS}=2b10
LCD_DATA_VALUE=8h80;

Drop_en
{LCD_EN, LCD_RS}=2b01
LCD_DATA_VALUE=8h80


Go back to Write_h1


Verilog code for fsm_lcd

// FSM to control LCD for display time h1h0:mim0:s1s0
module fsm_lcd(CLK_400Hz, resetn,
bcd_hrd1, bcd_hrd0, bcd_mind1, bcd_mind0, bcd_secd1, bcd_secd0,
LCD_ON, LCD_RS, LCD_EN, LCD_RW, LCD_DATA);


input CLK_400Hz, resetn;
input [3:0] bcd_hrd1, bcd_hrd0, bcd_mind1, bcd_mind0, bcd_secd1, bcd_secd0;
output LCD_ON, LCD_RS, LCD_EN, LCD_RW;
output [7:0] LCD_DATA;

reg [5:0] p_state, n_state;
reg LCD_EN, LCD_RS;
reg [7:0] LCD_DATA_VALUE;


parameter [5:0] reset1=1, reset2=2, reset3=3, FUNC_SET=4,
display_off=5, display_clear=6,
display_on=7, mode_set=8, write_char1=9,
write_char2=10, write_char3=11, write_char4=12,
write_char5=13, write_char6=14, write_char7=15,
write_char8=16, write_char9=17, write_char10=18,
return_home=19,
toggle_e1=20,toggle_e2=21, toggle_e3=22, toggle_e4=23,
toggle_e5=24,toggle_e6=25, toggle_e7=26, toggle_e8=27,
toggle_e9=28,toggle_e10=29, toggle_e11=30, toggle_e12=31,
toggle_e13=32,toggle_e14=33, toggle_e15=34, toggle_e16=35,
toggle_e17=36,toggle_e18=37, toggle_e19=38, w_address=39, write_w=40;

parameter [5:0] toggle_e20=41, toggle_e21=42, char1_address=43, write_e=44;


assign LCD_ON=1;
assign LCD_RW=0;
assign LCD_DATA = LCD_RW? 8'bzzzzzzzz: LCD_DATA_VALUE;

always @ (p_state)
begin
case (p_state)
reset1:
begin
n_state = toggle_e1;
{LCD_EN, LCD_RS}=2'b10;
LCD_DATA_VALUE = 8'h38;
end
toggle_e1:
begin
n_state = reset2;
{LCD_EN, LCD_RS}=2'b00;
LCD_DATA_VALUE = 8'h38;
end

reset2:
begin
n_state = toggle_e2;
{LCD_EN, LCD_RS}=2'b10;
LCD_DATA_VALUE = 8'h38;
end

toggle_e2:
begin
n_state = reset3;
{LCD_EN, LCD_RS}=2'b00;
LCD_DATA_VALUE = 8'h38;
end

reset3:
begin
n_state = toggle_e3;
{LCD_EN, LCD_RS}=2'b10;
LCD_DATA_VALUE = 8'h38;
end

toggle_e3:
begin
n_state = FUNC_SET;
{LCD_EN, LCD_RS}=2'b00;
LCD_DATA_VALUE = 8'h38;
end

FUNC_SET:

begin
n_state = toggle_e4;
{LCD_EN, LCD_RS}=2'b10;
LCD_DATA_VALUE = 8'h38;
end

toggle_e4:
begin
n_state = display_off;
{LCD_EN, LCD_RS}=2'b00;
LCD_DATA_VALUE = 8'h38;
end
display_off:
begin
n_state = toggle_e5;
{LCD_EN, LCD_RS}=2'b10;
LCD_DATA_VALUE = 8'h08;
end

toggle_e5:
begin
n_state = display_clear;
{LCD_EN, LCD_RS}=2'b00;
LCD_DATA_VALUE = 8'h08;
end
display_clear:

begin
n_state = toggle_e6;
{LCD_EN, LCD_RS}=2'b10;
LCD_DATA_VALUE = 8'h01;
end

toggle_e6:
begin
n_state = display_on;
{LCD_EN, LCD_RS}=2'b00;
LCD_DATA_VALUE = 8'h01;
end

display_on:

begin
n_state = toggle_e7;
{LCD_EN, LCD_RS}=2'b10;
LCD_DATA_VALUE = 8'h0c;
end

toggle_e7:
begin
n_state = mode_set;
{LCD_EN, LCD_RS}=2'b00;
LCD_DATA_VALUE = 8'h0c;
end
mode_set:

begin
n_state = toggle_e8;
{LCD_EN, LCD_RS}=2'b10;
LCD_DATA_VALUE = 8'h06;
end

toggle_e8:
begin
n_state = write_char1;
{LCD_EN, LCD_RS}=2'b00;
LCD_DATA_VALUE = 8'h06;
end

write_char1:

begin
n_state = toggle_e9;
{LCD_EN, LCD_RS}=2'b11;
LCD_DATA_VALUE = {3'b011, bcd_hrd1};
end
toggle_e9:
begin
n_state = write_char2;
{LCD_EN, LCD_RS}=2'b01;
LCD_DATA_VALUE = {3'b011, bcd_hrd1};
end
write_char2:

begin
n_state = toggle_e10;
{LCD_EN, LCD_RS}=2'b11;
LCD_DATA_VALUE = {3'b011, bcd_hrd0};
end
toggle_e10:
begin
n_state = write_char3;
{LCD_EN, LCD_RS}=2'b01;
LCD_DATA_VALUE = {3'b011, bcd_hrd0};
end
write_char3:
begin
n_state = toggle_e11;
{LCD_EN, LCD_RS}=2'b11;
LCD_DATA_VALUE = 8'h3a;
end
toggle_e11:
begin
n_state = write_char4;
{LCD_EN, LCD_RS}=2'b01;
LCD_DATA_VALUE = 8'h3a;
end
write_char4:

begin
n_state = toggle_e12;
{LCD_EN, LCD_RS}=2'b11;
LCD_DATA_VALUE = {3'b011, bcd_mind1};
end
toggle_e12:
begin
n_state = write_char5;
{LCD_EN, LCD_RS}=2'b01;
LCD_DATA_VALUE = {3'b011, bcd_mind1};
end
write_char5:
begin
n_state = toggle_e13;
{LCD_EN, LCD_RS}=2'b11;
LCD_DATA_VALUE = {3'b011, bcd_mind0};
end
toggle_e13:
begin
n_state = write_char6;
{LCD_EN, LCD_RS}=2'b01;
LCD_DATA_VALUE = {3'b011, bcd_mind0};
end
write_char6:
begin
n_state = toggle_e14;
{LCD_EN, LCD_RS}=2'b11;
LCD_DATA_VALUE = 8'h3a;
end
toggle_e14:
begin
n_state = write_char7;
{LCD_EN, LCD_RS}=2'b01;
LCD_DATA_VALUE = 8'h3a;
end
write_char7:
begin
n_state = toggle_e15;
{LCD_EN, LCD_RS}=2'b11;
LCD_DATA_VALUE ={3'b011, bcd_secd1};
end
toggle_e15:
begin
n_state = write_char8;
{LCD_EN, LCD_RS}=2'b01;
LCD_DATA_VALUE ={3'b011, bcd_secd1};
end
write_char8:
begin
n_state = toggle_e16;
{LCD_EN, LCD_RS}=2'b11;
LCD_DATA_VALUE ={3'b011, bcd_secd0};
end
toggle_e16:
begin
n_state = w_address;
{LCD_EN, LCD_RS}=2'b01;
LCD_DATA_VALUE ={3'b011, bcd_secd0};
end



// set DDRAM address for the second line
w_address:
begin
n_state = toggle_e17;
{LCD_EN, LCD_RS}=2'b10;
LCD_DATA_VALUE =8'hc0;
end
toggle_e17:
begin
n_state = write_w;
{LCD_EN, LCD_RS}=2'b00;
LCD_DATA_VALUE =8'hc0;
end
write_w:
begin
n_state = toggle_e18;
{LCD_EN, LCD_RS}=2'b11;
LCD_DATA_VALUE =8'h57;
end
toggle_e18:
begin
n_state = write_e;
{LCD_EN, LCD_RS}=2'b00;
LCD_DATA_VALUE =8'h57;
end
write_e:
begin
n_state = toggle_e20;
{LCD_EN, LCD_RS}=2'b11;
LCD_DATA_VALUE =8'h65;
end
toggle_e20:
begin
n_state = return_home;
{LCD_EN, LCD_RS}=2'b00;
LCD_DATA_VALUE =8'h65;
end
return_home:
begin
n_state = toggle_e21;
{LCD_EN, LCD_RS}=2'b10;
LCD_DATA_VALUE =8'h80; //8'h80;
end
toggle_e21:
begin
n_state = write_char1;
{LCD_EN, LCD_RS}=2'b00;
LCD_DATA_VALUE =8'h80;//8'h80;
end
endcase
end

always @ (posedge CLK_400Hz, negedge resetn)
begin
if (resetn == 0)
begin
p_state <= reset1;

end
else
p_state <= n_state;
end
endmodule

You might also like