LCD control and data lines. I threw in the LED output to
aid in debugging and to keep the LED bank busy when
the LCD operates. I used the LEDs to debug by switching
certain LEDs on when I arrived at a desired point in the
execution of the LCD driver code. For instance, I turned
on LED1 when I entered the display_set code area.
The PmodCLP LCD module’s chipset command set
is much like that of the industry standard HD44780
LCD chipset. Thus, we can use existing HD44780
microcontroller code to model our Verilog PmodCLP
driver firmware. Here are the FPGA register layouts we will
need to display characters on our PmodCLP:
//current LCD driver state
//LCD function timer
//points to next character
//to display on LCD
reg [8:0] char2display;//the character to
//display on the LCD
reg [22:0] clk_divider; //used to slow down LED
//blink rate
reg [3:0] lcd_state;
reg [19:0] lcd_clk;
reg [8:0] char_ptr;
Now that we have places to store states, clock counts
and ASCII characters, let’s parameterize the PmodCLP
commands we will be using in our Verilog LCD driver code:
parameter cmd_function_set = 8’h3C, //8-bit interface,
//2-line display
cmd_display_set = 8’h0C, //LCD on, no
//blink, no cursor
cmd_display_clr = 8’h01, //clear the LCD
cmd_entry_mode = 8’h06, //shift to right
cmd_display_home = 8’h02, //move to home
//position
cmd_display_line2 = 8’hC0; //move to line 2
Since we took the time to hack out some space for a
register called lcd_state, that’s a good indication that we’re
probably going to use a state machine approach in our
LCD driver code. Here are the states we will be traversing:
parameter power_up = 4’h0,
function_set = 4’h1,
display_set = 4’h2,
display_clr = 4’h3,
display_home = 4’h4,
display_msg = 4’h5,
msg_table = 4’h6,
display_line2 = 4’h7,
idle = 4’h8;
Implementing a state machine in our Verilog-based
LCD driver makes the code easy to understand and follow.
Most of the states operate in a similar manner to the
function_set state. So, to gain an understanding of how
the state machine code works, we’ll take a detailed look
at the function_set state source code.
Each tick of our clk_10mhz clock source is equivalent
to 0.1 μs. Thus, it takes 10 clocks to span a time of 1.0 μs.
Since the lcd_clk value drives the internal LCD task
execution timing within a state, we always want the
lcd_clk register to have a value of zero when a state is
entered. That’s especially true when we call the first of the
initialization states, which happens to be the function_set
THE DESIGN CYCLE
state. That’s where the reset code comes into play. Here’s
what the reset routine looks like:
always @(posedge clk_10mhz or negedge reset)
begin
if(reset == 0)
begin
lcd_e <= 0;
lcd_rs <= 0;
lcd_clk <= 0;
lcd_state <= function_set;
char_ptr <= 1;
end
else
begin
case(lcd_state)
I instructed the ISE WebPACK application to apply a
pullup to the reset I/O pin. When the reset pin is taken
logically low, the initialization of the LCD control lines
takes place along with the clearing of the LCD internal
task clock. Within the reset routine, the next state to be
entered following a reset operation is set for function_set
and the character-to-display pointer is set to the point to
the first ASCII character in the message table.
If the reset input is not logically low, the LCD state
machine code is allowed to execute. Our state machine is
actually a Verilog case structure. The states we parameterized
earlier are used as case statement arguments. The reset
code assigned the function_set parameter as the next case
argument is done by loading the lcd_state register with
the function_set parameter, which equates to binary 0001.
Here’s the case code that makes up the function_set state:
function_set:
begin
if(lcd_clk == 400) //40 uS
begin
lcd_clk <= 0;
lcd_state <= display_set;
end
else if(lcd_clk == 20)
begin
lcd_e <= 0;
lcd_state <= function_set;
lcd_clk <= lcd_clk + 1;
end
else if(lcd_clk == 10)
begin
lcd_e <= 1;
lcd_state <= function_set;
lcd_clk <= lcd_clk + 1;
end
else
begin
lcd_rs <= 0;
lcd_data <= cmd_function_set;
lcd_state <= function_set;
lcd_clk <= lcd_clk + 1;
end
end
When the function_set case code is entered, the lcd_clk
register value is equal to zero. Thus, the execution begins in
the else clause, which resides at the end of the function_set
code. The PmodCLP’s RS control line is driven logically low
for a command operation and the function_set command
September 2008 73