8.4 A Case Study: Using LCD Module On DE2 Board
8.4 A Case Study: Using LCD Module On DE2 Board
FPGA
LCD_EN
LCD_DATA[0:7]
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
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.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.
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).
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.
Set DDRAM address for 2nd line display Display clear Display on Display chars in 2nd line
Mode set
Return home
Text display
Reset1
{LCD_EN, LCD_RS}=2b10 LCD_DATA_VALUE=8h38 {LCD_EN, LCD_RS}=2b00 LCD_DATA_VALUE=8h38 {LCD_EN, LCD_RS}=2b10 LCD_DATA_VALUE=8h38 {LCD_EN, LCD_RS}=2b00 LCD_DATA_VALUE=8h38 {LCD_EN, LCD_RS}=2b10 LCD_DATA_VALUE=8h38 {LCD_EN, LCD_RS}=2b00 LCD_DATA_VALUE=8h38
Func_set
Drop_e1
Drop_e4
Reset2
Display_off
Drop_e2
Drop_e5
Reset3
Display_clr
Drop_e3
Drop_e6
(to be continued)
Display_on
Drop_e12
Drop_e7
Write_m0 Mode_set
{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}=2b01 LCD_DATA_VALUE= {3b011,bcd_hrd1}
Drop_e8
Drop_e13
Write_h1
Write_dot2
Drop_e9
Drop_e14
Write_h0
Write_s1 Drop_e10
{LCD_EN, LCD_RS}=2b01 LCD_DATA_VALUE=
Drop_e15 Write_dot1
{LCD_EN, LCD_RS}=2b11 LCD_DATA_VALUE=
Write_s0
Drop_e11
Drop_e16
Write_m1
Set_add_line2
Drop_e17
Write_J
Drop_e18
Set_add_line1
Drop_en ``
Go back to Write_h1
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.
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