Tutorial 2 On Using The LCD On The Spartan 3E Starter Kit Board
Tutorial 2 On Using The LCD On The Spartan 3E Starter Kit Board
Tutorial 2 On Using The LCD On The Spartan 3E Starter Kit Board
----------
--- The 2 x 16 character LCD on the board has an internal Sitronix ST7066U
graphics
-- controller that is functionally equivalent with the following devices:
-- * Samsung S6A0069X or KS0066U
-- * Hitachi HD44780
-- * SMOS SED1278
-- To minimize pin icount the FPGA controls the LCD via the 4-bit data
interface.
-library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity lcd is
port( clk
rst
SF_D
LCD_E
LCD_RS
LCD_RW
SF_CE0
end lcd;
:
:
:
:
:
:
:
in std_logic;
in std_logic;
out std_logic_vector(11 downto 8);
out std_logic;
out std_logic;
out std_logic;
out std_logic);
signal
sm
signal
signal
signal
signal
LCD_E
signal
LCD_RS
signal
SF_D
signal
signal
signal
signal
signal
signal
signal
signal
signal
signal
signal
dstate, next_dstate
idone, next_idone
count, next_count
nibble
enable, next_enable
:
:
:
:
regsel, next_regsel
byte
timer_15ms
timer_4100us
timer_100us
timer_40us
timer_1640us
txdone, next_txdone
txcount, next_txcount
selnibble
next_selnibble
digit, next_digit
cnt, next_cnt
:
:
:
:
:
:
:
:
:
:
:
std_logic;--initialization done
integer range 0 to 750000;
std_logic_vector(3 downto 0);
std_logic;--register enable signal put out to
std_logic;
std_logic;
std_logic;
std_logic;
std_logic;
std_logic;
integer range 0 to 2068;
std_logic;
std_logic;
std_logic_vector(3 downto 0);
integer range 0 to 50000000;
begin
--- concurrent assignments (LCD interface)
-SF_CE0 <= '1'; --disable intel strataflash memory.
--FPGA has full read/write access to LCD.
LCD_RW <= '0'; --write LCD (LCD accepts data).
--putting LCD_RW=0 also prevent the LCD screen
--from presenting undesired data.
SF_D
<= nibble;
LCD_E <= enable;
LCD_RS <= regsel;
--- the data_selector choose what data to pass to the LCD
-- depending on the operation's state of the system
-data_selector: process(istate, dstate, digit)
begin
-- the following section of the code is for the
-- LCD's initialization process so it is always
-- the same
case istate is
when istep_two | istep_four | istep_six =>
byte <= X"30";
when istep_eight =>
byte <= X"20";
when function_set =>
byte <= X"28";
when entry_mode =>
byte <= X"06";
when control_display =>
end if;
end process digit_incr;
----
s14. Allow at least 1.64 ms (82,000 clock cycles) after issuing a clear
display command.
--------
---------
Once power comes on, the init_sm makes sure we go through all the
necessary steps of the LCD initialization process.
The only purpose of the sm is to control that the system evolve
properly through the various initialization steps. Besides this task
the sm doesn't do much. The "real work" is done behind the scenes
by tx_m and time_m
--- time_m provides all signals needed to correctly "time" the various
-- LCD's operations
-time_m: process(istate, count, idone)
begin
--- by default hold the state, keep the counter at rest, and
-- hold the timer's outputs low
-next_count
<= count;
timer_15ms
<= '0'; -- combinational output
timer_4100us <= '0'; -- combinational output
timer_100us <= '0'; -- combinational output
timer_40us
<= '0'; -- combinational output
timer_1640us <= '0'; -- combinational output
case istate is
when istep_one
=>
next_count <= count + 1;
if (count = 750000) then
next_count <= 0;
timer_15ms <= '1';
end if;
when istep_three =>
next_count <= count + 1;
if (count = 205000) then
next_count
<= 0;
timer_4100us <= '1';
end if;
when istep_five =>
next_count <= count + 1;
if (count = 5000) then
next_count <= 0;
--- tx_m generate the control (LCD_E, LCD_RS, LCD_RW, SF_CE0) and data (SF_D)
-- signals needed to drive the LCD according to the 4-bit interface protocol
-- used by the Spartan 3E starter kit board.
-tx_m: process(istate, txcount, byte, selnibble, enable, txdone,
idone, dstate)
begin
next_selnibble <= selnibble;
next_txdone
<= txdone;
next_txcount
<= txcount;
next_enable
<= enable;
--- the following section of the state machine allow
-- to transmit the data necessary for the LCD's
-- initialization (which is pretty much the same
-- no matter what the user what to write on the
-- display), as well as transmitting the bytes
-- needed to display the text the user want to put
-- on the screen (which is user specific so it
-- will require cutomization)
-case istate is
when istep_one | istep_three | istep_seven | istep_nine =>
next_selnibble <= '1'; -- pass hign nibble
-- transmit a nibble
when istep_two | istep_four | istep_six | istep_eight =>
next_txcount <= txcount + 1;
if (txcount = 1) then
next_enable <= '1';
end if;
if (txcount = 10) then
if (istate /= init_done or
(istate = init_done and
(dstate = set_start_address or
dstate = write_data_V or
dstate = write_data_H or
dstate = write_data_D or
dstate = write_data_L or
dstate = address_digit or
dstate = write_digit
))) then
next_txcount <= txcount + 1;
if (txcount = 1) then
next_enable <= '1';
end if;
if (txcount = 10) then
next_enable <= '0';
end if;
if (txcount = 11) then
-- next we could pass zeros on the SF_D bus
end if;
-- the timing between the falling edge of the upper nibble's enable
-- and the rising edge of the lower nibble's rising edge of an
-- operation is 1us (50 clock cycles)
if (txcount = 58) then
-- 10 + 1 + 50 - 2 = 58
next_selnibble <= '0'; -- pass lower nibble
end if;
if (txcount = 60) then
next_enable <= '1';
end if;
if (txcount = 69) then
-Write data into DDRAM if the command follows a previous Set DDRAM
Address.
-- 3. Return Cursor Home command
-Return the cursor to the home position, the top-left corner. DDRAM
contents are
-unaffected.
--- The specific sequence of commands executed in the state machine depend on
the text
-- the user want to display on the LCD
--- the logic to activate the register select signal is placed in this block
-display_sm: process(dstate, txdone, idone, regsel, txcount)
begin
-- by default hold state
next_dstate <= dstate;
next_regsel <= regsel;
if txcount = 11 then
next_regsel <= '0';
end if;
if txcount = 58 then
next_regsel <= idone; --high for active write dstates, low for istates
end if;
if txcount = 70 then
next_regsel <= '0';
end if;
case dstate is
when didle =>
next_regsel <= '0'; -- must be low for active istates
if (idone = '1') then
next_dstate <= set_start_address;
next_regsel <= '0'; -- must be low for address commands
end if;
when set_start_address => -- start the text at the first
-- location of the first line
-- of the LCD (0x80)
next_regsel <= '0';
if (txdone = '1') then
next_dstate <= write_data_V;
next_regsel <= '1'; --must be high for write commands
end if;
when write_data_V => -- V = 0x56
if (txdone = '1') then
next_dstate <= write_data_H;
next_regsel <= '1';
end if;
when write_data_H => -- H = 0x48
if (txdone = '1') then
enable
<= '0';
txdone
<= '0';
regsel
<= '0';
digit
<= (others => '0');
cnt
<= 0;
elsif clk = '1' and clk'event then
istate
<= next_istate;
dstate
<= next_dstate;
idone
<= next_idone;
count
<= next_count;
txcount
<= next_txcount;
selnibble <= next_selnibble;
enable
<= next_enable;
txdone
<= next_txdone;
regsel
<= next_regsel;
digit
<= next_digit;
cnt
<= next_cnt;
end if;
end process registers;
end rtl;