-- Filename: lcd_driver_hd44780_module.vhd
|
-- Filename: lcd_driver_hd44780_module.vhd
|
-- Filetype: VHDL Source Code
|
-- Filetype: VHDL Source Code
|
-- Date: 26 oct 2012
|
-- Date: 26 oct 2012
|
-- Update: -
|
-- Update: -
|
-- Description: VHDL Description for driving an HD44780 based LCD driver
|
-- Description: VHDL Description for driving an HD44780 based LCD driver
|
-- Author: J. op den Brouw
|
-- Author: J. op den Brouw
|
-- State: Demo
|
-- State: Demo
|
-- Error: -
|
-- Error: -
|
-- Version: 1.2alpha
|
-- Version: 1.2alpha
|
-- Copyright: (c)2012, De Haagse Hogeschool
|
-- Copyright: (c)2012, De Haagse Hogeschool
|
|
|
-- This file contains a VHDL description for driving an HD44780 based LCD
|
-- This file contains a VHDL description for driving an HD44780 based LCD
|
-- driver, see for a standard information of such a display:
|
-- driver, see for a standard information of such a display:
|
-- https://decibel.ni.com/content/servlet/JiveServlet/download/2741-3-3217/hd44780.pdf
|
-- https://decibel.ni.com/content/servlet/JiveServlet/download/2741-3-3217/hd44780.pdf
|
--
|
--
|
-- Currently, this driver uses the 8-bit databus mode. This is not a big problem
|
-- Currently, this driver uses the 8-bit databus mode. This is not a big problem
|
-- for most FPGA's because of the numerous pins.
|
-- for most FPGA's because of the numerous pins.
|
--
|
--
|
-- Please note that there are a lot of almost-the-same displays available, so
|
-- Please note that there are a lot of almost-the-same displays available, so
|
-- it's not guaranteed to work with all displays available. Also, timing may differ.
|
-- it's not guaranteed to work with all displays available. Also, timing may differ.
|
--
|
--
|
-- This code is tested on a Terasic DE0-board with an optional
|
-- This code is tested on a Terasic DE0-board with an optional
|
-- LCD display. See the weblinks
|
-- LCD display. See the weblinks
|
-- http://www.terasic.com.tw/cgi-bin/page/archive.pl?Language=English&CategoryNo=56&No=364
|
-- http://www.terasic.com.tw/cgi-bin/page/archive.pl?Language=English&CategoryNo=56&No=364
|
-- http://www.terasic.com.tw/cgi-bin/page/archive.pl?Language=English&CategoryNo=78&No=396
|
-- http://www.terasic.com.tw/cgi-bin/page/archive.pl?Language=English&CategoryNo=78&No=396
|
-- for more info. The display used has only two lines.
|
-- for more info. The display used has only two lines.
|
--
|
--
|
-- The VHDL description can both be simulated and synthesized.
|
-- This VHDL description can both be simulated and synthesized.
|
--
|
--
|
-- This driver has a User Side and a LCD Side. The user is to interface at the User Side
|
-- This driver has a User Side and a LCD Side. The user is to interface at the User Side
|
-- and has a number of "routines" at her disposal. The User Side implements the following
|
-- and has a number of "routines" at her disposal. The User Side implements the following
|
-- inputs/routines in order of priority:
|
-- inputs/routines in order of priority:
|
--
|
--
|
-- Command inputs:
|
-- Command inputs:
|
-- init: a logic 1 initializes the display
|
-- init: a logic 1 initializes the display
|
-- cls: a logic 1 clears the display (and goes to home)
|
-- cls: a logic 1 clears the display (and goes to home)
|
-- home: a logic 1 sets the cursor to row 0, column 0
|
-- home: a logic 1 sets the cursor to row 0, column 0
|
-- goto10: a logic 1 sets the cursor to row 1, column 0
|
-- goto10: a logic 1 sets the cursor to row 1, column 0
|
-- goto20: a logic 1 sets the cursor to row 2, column 0
|
-- goto20: a logic 1 sets the cursor to row 2, column 0
|
-- goto30: a logic 1 sets the cursor to row 3, column 0
|
-- goto30: a logic 1 sets the cursor to row 3, column 0
|
-- wr: a logic 1 writes a character to the display
|
-- wr: a logic 1 writes a character to the display
|
--
|
--
|
-- Data inputs:
|
-- Data inputs:
|
--
|
--
|
-- data: an 8-bit data to be written to the display
|
-- data: an 8-bit data to be written to the display
|
--
|
--
|
-- The user has one observable output:
|
-- The user has one observable output:
|
--
|
--
|
-- busy: a logic 1 indicates that the driver is currently
|
-- busy: a logic 1 indicates that the driver is currently
|
-- busy driving the display, a logic 0 indicates that
|
-- busy driving the display, a logic 0 indicates that
|
-- the driver waits for the next command.
|
-- the driver waits for the next command.
|
--
|
--
|
-- The user can supply the next generics, which are processed at
|
-- The user can supply the next generics, which are processed at
|
-- instantiation of the module:
|
-- instantiation of the module:
|
--
|
--
|
-- freq: the clock frequency at which the hardware has to run.
|
-- freq: the clock frequency at which the hardware has to run.
|
-- this frequency is mandatory because of internal delays
|
-- this frequency is mandatory because of internal delays
|
-- calculated, defaults to 50 MHz.
|
-- calculated, defaults to 50 MHz.
|
-- areset_pol:
|
-- areset_pol:
|
-- the polarity of the reset signal, defaults to High (1)
|
-- the polarity of the reset signal, defaults to High (1)
|
-- time_init1:
|
-- time_init1:
|
-- the time to wait after Vcc > 4.5 V
|
-- the time to wait after Vcc > 4.5 V
|
-- time_init2:
|
-- time_init2:
|
-- the time to wait after first "contact"
|
-- the time to wait after first "contact"
|
-- time_init3:
|
-- time_init3:
|
-- the time to wait after the second contact
|
-- the time to wait after the second contact
|
-- time_tas:
|
-- time_tas:
|
-- the RW and RS signal setup time with respect to the positive
|
-- the RW and RS signal setup time with respect to the positive
|
-- edge of the E pulse
|
-- edge of the E pulse
|
-- time_cycle_e:
|
-- time_cycle_e:
|
-- the complete cycle time
|
-- the complete cycle time
|
-- time_pweh:
|
-- time_pweh:
|
-- the E pulse width high time
|
-- the E pulse width high time
|
-- time_no_bf:
|
-- time_no_bf:
|
-- time to wait before command completion if no Busy Flag reading is done,
|
-- time to wait before command completion if no Busy Flag reading is done,
|
-- some designs connect RW to logic 0, so reading from the LCD is not
|
-- some designs connect RW to logic 0, so reading from the LCD is not
|
-- possible, saves a pin.
|
-- possible, saves a pin.
|
-- cursor_on:
|
-- cursor_on:
|
-- true to set the cursor on at the display, false for no cursor
|
-- true to set the cursor on at the display, false for no cursor
|
-- blink_on:
|
-- blink_on:
|
-- true to let the cursor blink, false for no blink (just a underscore)
|
-- true to let the cursor blink, false for no blink (just a underscore)
|
-- use_bf: true if Busy Flag reading is to be used, false for no BF reading
|
-- use_bf: true if Busy Flag reading is to be used, false for no BF reading
|
--
|
--
|
-- Note: it's not possible to write command codes to the display.
|
-- Note: it's not possible to write command codes to the display.
|
--
|
--
|
-- Changes to v1.0: LCD_E is renamed to LCD_EN
|
-- Changes to v1.0: LCD_E is renamed to LCD_EN
|
-- LCD_DB is renamed to LCD_DATA
|
-- LCD_DB is renamed to LCD_DATA
|
-- busy is now registered
|
-- busy is now registered
|
-- removed hardware simulation architecture
|
-- removed hardware simulation architecture
|
--
|
--
|
-- Changes to v1.1: added timing generics for better steering of the E pulse cycle
|
-- Changes to v1.1: added timing generics for better steering of the E pulse cycle
|
-- implemented cursor on/off/blink with generic parameters
|
-- implemented cursor on/off/blink with generic parameters
|
--
|
--
|
-- Changes to v1.2: added Busy Flag reading mode, selectable at instantiation time
|
-- Changes to v1.2: added Busy Flag reading mode, selectable at instantiation time
|
-- LCD_EN changes back to LCD_E
|
-- LCD_EN changes back to LCD_E
|
-- LCD_DATA changed back to LCD_DB
|
-- LCD_DATA changed back to LCD_DB
|
--
|
--
|
-- To do: use 4-bit mode or 8-bit mode at instantiation time
|
-- To do: use 4-bit mode or 8-bit mode at instantiation time
|
--
|
--
|
|
|
-- The libraries to use.
|
-- The libraries to use.
|
library ieee;
|
library ieee;
|
use ieee.std_logic_1164.all;
|
use ieee.std_logic_1164.all;
|
|
|
-- The entity of the LCD Driver Module.
|
-- The entity of the LCD Driver Module.
|
entity lcd_driver_hd44780_module is
|
entity lcd_driver_hd44780_module is
|
generic (freq : integer := 50000000;
|
generic (freq : integer := 50000000;
|
areset_pol : std_logic := '1';
|
areset_pol : std_logic := '1';
|
time_init1 : time := 40 ms;
|
time_init1 : time := 40 ms;
|
time_init2 : time := 4100 us;
|
time_init2 : time := 4100 us;
|
time_init3 : time := 100 us;
|
time_init3 : time := 100 us;
|
time_tas : time := 60 ns;
|
time_tas : time := 60 ns;
|
time_cycle_e : time := 1000 ns;
|
time_cycle_e : time := 1000 ns;
|
time_pweh : time := 500 ns;
|
time_pweh : time := 500 ns;
|
time_no_bf : time := 2 ms;
|
time_no_bf : time := 2 ms;
|
cursor_on : boolean := false;
|
cursor_on : boolean := false;
|
blink_on : boolean := false;
|
blink_on : boolean := false;
|
use_bf : boolean := true
|
use_bf : boolean := true
|
);
|
);
|
port (clk : in std_logic;
|
port (clk : in std_logic;
|
areset : in std_logic;
|
areset : in std_logic;
|
-- User site
|
-- User site
|
init : in std_logic;
|
init : in std_logic;
|
data : in std_logic_vector(7 downto 0);
|
data : in std_logic_vector(7 downto 0);
|
wr : in std_logic;
|
wr : in std_logic;
|
cls : in std_logic;
|
cls : in std_logic;
|
home : in std_logic;
|
home : in std_logic;
|
goto10 : in std_logic;
|
goto10 : in std_logic;
|
goto20 : in std_logic;
|
goto20 : in std_logic;
|
goto30 : in std_logic;
|
goto30 : in std_logic;
|
busy : out std_logic;
|
busy : out std_logic;
|
-- LCD side
|
-- LCD side
|
LCD_E : out std_logic;
|
LCD_E : out std_logic;
|
LCD_RS : out std_logic;
|
LCD_RS : out std_logic;
|
LCD_RW : out std_logic;
|
LCD_RW : out std_logic;
|
LCD_DB : inout std_logic_vector(7 downto 0)
|
LCD_DB : inout std_logic_vector(7 downto 0)
|
);
|
);
|
end entity lcd_driver_hd44780_module;
|
end entity lcd_driver_hd44780_module;
|
|
|
-- This architecture drives the LCD.
|
-- This architecture drives the LCD.
|
architecture hardware_driver of lcd_driver_hd44780_module is
|
architecture hardware_driver of lcd_driver_hd44780_module is
|
|
|
-- Delays... Please note that if the frequency is (too) low,
|
-- Delays... Please note that if the frequency is (too) low,
|
-- some of the delays will 0. The code will take care of that.
|
-- some of the delays will 0. The code will take care of that.
|
-- Converting time to integer is problematic. Please note the use of
|
-- Converting time to integer is problematic. Please note the use of
|
-- the simulator time step in the calculations.
|
-- the simulator time step in the calculations.
|
-- The simulator timestep
|
-- The simulator timestep
|
constant simulator_timestep : time := 1 ns;
|
constant simulator_timestep : time := 1 ns;
|
-- Number of simulator timesteps per second.
|
-- Number of simulator timesteps per second.
|
constant sim_steps_per_sec : real := real(1 sec/simulator_timestep);
|
constant sim_steps_per_sec : real := real(1 sec/simulator_timestep);
|
|
|
constant delay_init1 : integer := integer( real(freq) * real(time_init1/simulator_timestep) / sim_steps_per_sec);
|
constant delay_init1 : integer := integer( real(freq) * real(time_init1/simulator_timestep) / sim_steps_per_sec);
|
constant delay_init2 : integer := integer( real(freq) * real(time_init2/simulator_timestep) / sim_steps_per_sec);
|
constant delay_init2 : integer := integer( real(freq) * real(time_init2/simulator_timestep) / sim_steps_per_sec);
|
constant delay_init3 : integer := integer( real(freq) * real(time_init3/simulator_timestep) / sim_steps_per_sec);
|
constant delay_init3 : integer := integer( real(freq) * real(time_init3/simulator_timestep) / sim_steps_per_sec);
|
constant delay_tas : integer := integer( real(freq) * real(time_tas/simulator_timestep) / sim_steps_per_sec);
|
constant delay_tas : integer := integer( real(freq) * real(time_tas/simulator_timestep) / sim_steps_per_sec);
|
constant delay_cycle_e : integer := integer( real(freq) * real(time_cycle_e/simulator_timestep) / sim_steps_per_sec);
|
constant delay_cycle_e : integer := integer( real(freq) * real(time_cycle_e/simulator_timestep) / sim_steps_per_sec);
|
constant delay_pweh : integer := integer( real(freq) * real(time_pweh/simulator_timestep) / sim_steps_per_sec);
|
constant delay_pweh : integer := integer( real(freq) * real(time_pweh/simulator_timestep) / sim_steps_per_sec);
|
constant delay_no_bf : integer := integer( real(freq) * real(time_no_bf/simulator_timestep) / sim_steps_per_sec);
|
constant delay_no_bf : integer := integer( real(freq) * real(time_no_bf/simulator_timestep) / sim_steps_per_sec);
|
|
|
-- The next statements do work in Quartus but not in (32 bit) ModelSim...
|
-- The next statements do work in Quartus but not in (32 bit) ModelSim...
|
--constant delay_init1 : integer := integer(real(freq)*real(time'pos(time_init1)) / real(time'pos(1 sec)));
|
--constant delay_init1 : integer := integer(real(freq)*real(time'pos(time_init1)) / real(time'pos(1 sec)));
|
--constant delay_init2 : integer := integer(real(freq)*real(time'pos(time_init2)) / real(time'pos(1 sec)));
|
--constant delay_init2 : integer := integer(real(freq)*real(time'pos(time_init2)) / real(time'pos(1 sec)));
|
--constant delay_init3 : integer := integer(real(freq)*real(time'pos(time_init3)) / real(time'pos(1 sec)));
|
--constant delay_init3 : integer := integer(real(freq)*real(time'pos(time_init3)) / real(time'pos(1 sec)));
|
--constant delay_tas : integer := integer(real(freq)*real(time'pos(time_tas)) / real(time'pos(1 sec)));
|
--constant delay_tas : integer := integer(real(freq)*real(time'pos(time_tas)) / real(time'pos(1 sec)));
|
--constant delay_cycle_e : integer := integer(real(freq)*real(time'pos(time_cycle_e)) / real(time'pos(1 sec)));
|
--constant delay_cycle_e : integer := integer(real(freq)*real(time'pos(time_cycle_e)) / real(time'pos(1 sec)));
|
--constant delay_pweh : integer := integer(real(freq)*real(time'pos(time_pweh)) / real(time'pos(1 sec)));
|
--constant delay_pweh : integer := integer(real(freq)*real(time'pos(time_pweh)) / real(time'pos(1 sec)));
|
--constant delay_no_bf : integer := integer(real(freq)*real(time'pos(time_no_bf)) / real(time'pos(1 sec)));
|
--constant delay_no_bf : integer := integer(real(freq)*real(time'pos(time_no_bf)) / real(time'pos(1 sec)));
|
|
|
-- Time the E signal must be low following E high
|
-- Time the E signal must be low following E high
|
constant delay_pwel : integer := delay_cycle_e-delay_pweh;
|
constant delay_pwel : integer := delay_cycle_e-delay_pweh;
|
|
|
-- Counter for the delays. Timer would be a better choice.
|
-- Counter for the delays. Timer would be a better choice.
|
-- Range should be to the longest delay.
|
-- Range should be to the longest delay.
|
signal delay_counter : integer range 0 to delay_init1;
|
signal delay_counter : integer range 0 to delay_init1;
|
|
|
-- Should we use Busy Flag reading?
|
-- Should we use Busy Flag reading?
|
signal use_bf_int : std_logic;
|
signal use_bf_int : std_logic;
|
|
|
-- The states of the state machine.
|
-- The states of the state machine.
|
type state_type is (reset, command_init, command_init_1, command_init_2, command_init_3,
|
type state_type is (reset, command_init, command_init_1, command_init_2, command_init_3,
|
command_init_4, command_init_5, command_init_6, command_init_7, command_init_8,
|
command_init_4, command_init_5, command_init_6, command_init_7, command_init_8,
|
command_init_9, command_init_10, command_init_11, command_init12,
|
command_init_9, command_init_10, command_init_11, command_init12,
|
wait_for_command,
|
wait_for_command,
|
command_cls, command_home,
|
command_cls, command_home,
|
command_goto10, command_goto20, command_goto30, command_wr,
|
command_goto10, command_goto20, command_goto30, command_wr,
|
pulse_e, pulse_e_1, pulse_e_2, pulse_e_3, pulse_e_4,
|
pulse_e, pulse_e_1, pulse_e_2, pulse_e_3, pulse_e_4,
|
pulse_busy_flag, pulse_busy_flag_1, pulse_busy_flag_2, pulse_busy_flag_3,
|
pulse_busy_flag, pulse_busy_flag_1, pulse_busy_flag_2, pulse_busy_flag_3,
|
pulse_busy_flag_4, pulse_busy_flag_5);
|
pulse_busy_flag_4, pulse_busy_flag_5);
|
|
|
|
|
-- The current state and one for the return state (to facilitate return to the caller).
|
-- The current state and one for the return state (to facilitate return to the caller).
|
signal current_state, return_state : state_type;
|
signal current_state, return_state : state_type;
|
begin
|
begin
|
|
|
-- The state machine ;-)
|
-- The state machine ;-)
|
nsl_state: process (clk, areset) is
|
nsl_state: process (clk, areset) is
|
-- Function to translate a boolean to std_logic
|
-- Function to translate a boolean to std_logic
|
function bool_to_stdlogic(l: boolean) return std_logic is
|
function bool_to_stdlogic(l: boolean) return std_logic is
|
begin
|
begin
|
if l then
|
if l then
|
return('1');
|
return('1');
|
else
|
else
|
return('0');
|
return('0');
|
end if;
|
end if;
|
end function bool_to_stdlogic;
|
end function bool_to_stdlogic;
|
begin
|
begin
|
if (areset = '1' and areset_pol = '1') or (areset = '0' and areset_pol = '0') then
|
if (areset = '1' and areset_pol = '1') or (areset = '0' and areset_pol = '0') then
|
current_state <= reset;
|
current_state <= reset;
|
delay_counter <= 0;
|
delay_counter <= 0;
|
busy <= '1';
|
busy <= '1';
|
LCD_DB <= (others => 'Z');
|
LCD_DB <= (others => 'Z');
|
LCD_E <= '0';
|
LCD_E <= '0';
|
LCD_RS <= '0';
|
LCD_RS <= '0';
|
LCD_RW <= '0';
|
LCD_RW <= '0';
|
use_bf_int <= '0';
|
use_bf_int <= '0';
|
elsif rising_edge(clk) then
|
elsif rising_edge(clk) then
|
-- Default is busy
|
-- Default is busy
|
busy <= '1';
|
busy <= '1';
|
-- Default values of the LCD side
|
-- Default values of the LCD side
|
LCD_E <= '0';
|
LCD_E <= '0';
|
LCD_RW <= '0';
|
LCD_RW <= '0';
|
case current_state is
|
case current_state is
|
when reset|command_init =>
|
when reset|command_init =>
|
-- The logic is reset. Start initialization routine.
|
-- The logic is reset. Start initialization routine.
|
LCD_DB <= (others => 'Z');
|
LCD_DB <= (others => 'Z');
|
LCD_RS <= '0';
|
LCD_RS <= '0';
|
use_bf_int <= '0';
|
use_bf_int <= '0';
|
delay_counter <= delay_init1;
|
delay_counter <= delay_init1;
|
current_state <= command_init_1;
|
current_state <= command_init_1;
|
when command_init_1 =>
|
when command_init_1 =>
|
-- Wait until Vcc > 4.5 V...
|
-- Wait until Vcc > 4.5 V...
|
LCD_DB <= (others => 'Z');
|
LCD_DB <= (others => 'Z');
|
LCD_RS <= '0';
|
LCD_RS <= '0';
|
use_bf_int <= '0';
|
use_bf_int <= '0';
|
-- If done write 0x30 to the LCD
|
-- If done write 0x30 to the LCD
|
if delay_counter = 0 or delay_counter = 1 then
|
if delay_counter = 0 or delay_counter = 1 then
|
LCD_DB <= "00110000"; -- 0x30
|
LCD_DB <= "00110000"; -- 0x30
|
current_state <= pulse_e;
|
current_state <= pulse_e;
|
return_state <= command_init_2;
|
return_state <= command_init_2;
|
else
|
else
|
delay_counter <= delay_counter-1;
|
delay_counter <= delay_counter-1;
|
end if;
|
end if;
|
when command_init_2 =>
|
when command_init_2 =>
|
-- Next, set up wait for 4.1 ms
|
-- Next, set up wait for 4.1 ms
|
LCD_DB <= (others => 'Z');
|
LCD_DB <= (others => 'Z');
|
LCD_RS <= '0';
|
LCD_RS <= '0';
|
use_bf_int <= '0';
|
use_bf_int <= '0';
|
delay_counter <= delay_init2;
|
delay_counter <= delay_init2;
|
current_state <= command_init_3;
|
current_state <= command_init_3;
|
when command_init_3 =>
|
when command_init_3 =>
|
-- Wait...
|
-- Wait...
|
LCD_DB <= (others => 'Z');
|
LCD_DB <= (others => 'Z');
|
LCD_RS <= '0';
|
LCD_RS <= '0';
|
use_bf_int <= '0';
|
use_bf_int <= '0';
|
-- If done write 0x30 to the LCD
|
-- If done write 0x30 to the LCD
|
if delay_counter = 0 or delay_counter = 1 then
|
if delay_counter = 0 or delay_counter = 1 then
|
LCD_DB <= "00110000"; -- 0x30
|
LCD_DB <= "00110000"; -- 0x30
|
current_state <= pulse_e;
|
current_state <= pulse_e;
|
return_state <= command_init_4;
|
return_state <= command_init_4;
|
else
|
else
|
delay_counter <= delay_counter-1;
|
delay_counter <= delay_counter-1;
|
end if;
|
end if;
|
when command_init_4 =>
|
when command_init_4 =>
|
-- Next, set up wait for 100 us
|
-- Next, set up wait for 100 us
|
LCD_DB <= (others => 'Z');
|
LCD_DB <= (others => 'Z');
|
LCD_RS <= '0';
|
LCD_RS <= '0';
|
use_bf_int <= '0';
|
use_bf_int <= '0';
|
delay_counter <= delay_init3;
|
delay_counter <= delay_init3;
|
current_state <= command_init_5;
|
current_state <= command_init_5;
|
when command_init_5 =>
|
when command_init_5 =>
|
-- Wait...
|
-- Wait...
|
LCD_DB <= (others => 'Z');
|
LCD_DB <= (others => 'Z');
|
LCD_RS <= '0';
|
LCD_RS <= '0';
|
use_bf_int <= '0';
|
use_bf_int <= '0';
|
-- If done write 0x30 to the LCD
|
-- If done write 0x30 to the LCD
|
if delay_counter = 0 or delay_counter = 1 then
|
if delay_counter = 0 or delay_counter = 1 then
|
LCD_DB <= "00110000"; -- 0x30
|
LCD_DB <= "00110000"; -- 0x30
|
current_state <= pulse_e;
|
current_state <= pulse_e;
|
return_state <= command_init_6;
|
return_state <= command_init_6;
|
else
|
else
|
delay_counter <= delay_counter-1;
|
delay_counter <= delay_counter-1;
|
end if;
|
end if;
|
when command_init_6 =>
|
when command_init_6 =>
|
-- Power up is now done, so let's enter some reasonable values...
|
-- Power up is now done, so let's enter some reasonable values...
|
LCD_DB <= "00110000";
|
LCD_DB <= "00110000";
|
LCD_RS <= '0';
|
LCD_RS <= '0';
|
use_bf_int <= bool_to_stdlogic(use_bf);
|
use_bf_int <= bool_to_stdlogic(use_bf);
|
current_state <= pulse_e;
|
current_state <= pulse_e;
|
return_state <= command_init_7;
|
return_state <= command_init_7;
|
when command_init_7 =>
|
when command_init_7 =>
|
-- 8-bit bus, 2(?) lines, 5x7 characters
|
-- 8-bit bus, 2(?) lines, 5x7 characters
|
LCD_DB <= "00111100"; -- 0x3C
|
LCD_DB <= "00111100"; -- 0x3C
|
LCD_RS <= '0';
|
LCD_RS <= '0';
|
current_state <= pulse_e;
|
current_state <= pulse_e;
|
return_state <= command_init_8;
|
return_state <= command_init_8;
|
when command_init_8 =>
|
when command_init_8 =>
|
-- Display off
|
-- Display off
|
LCD_DB <= "00001000"; -- 0x08
|
LCD_DB <= "00001000"; -- 0x08
|
LCD_RS <= '0';
|
LCD_RS <= '0';
|
current_state <= pulse_e;
|
current_state <= pulse_e;
|
return_state <= command_init_9;
|
return_state <= command_init_9;
|
when command_init_9 =>
|
when command_init_9 =>
|
-- Display clear
|
-- Display clear
|
LCD_DB <= "00000001"; -- 0x01
|
LCD_DB <= "00000001"; -- 0x01
|
LCD_RS <= '0';
|
LCD_RS <= '0';
|
current_state <= pulse_e;
|
current_state <= pulse_e;
|
return_state <= command_init_10;
|
return_state <= command_init_10;
|
when command_init_10 =>
|
when command_init_10 =>
|
-- Display on, cursor and blink...
|
-- Display on, cursor and blink...
|
LCD_DB <= "000011" & bool_to_stdlogic(cursor_on) & bool_to_stdlogic(blink_on); -- 0x0C + ...
|
LCD_DB <= "000011" & bool_to_stdlogic(cursor_on) & bool_to_stdlogic(blink_on); -- 0x0C + ...
|
LCD_RS <= '0';
|
LCD_RS <= '0';
|
current_state <= pulse_e;
|
current_state <= pulse_e;
|
return_state <= command_init_11;
|
return_state <= command_init_11;
|
when command_init_11 =>
|
when command_init_11 =>
|
-- Mode set, increment cursor address, cursor shift
|
-- Mode set, increment cursor address, cursor shift
|
LCD_DB <= "00000110"; -- 0x06
|
LCD_DB <= "00000110"; -- 0x06
|
LCD_RS <= '0';
|
LCD_RS <= '0';
|
current_state <= pulse_e;
|
current_state <= pulse_e;
|
return_state <= wait_for_command;
|
return_state <= wait_for_command;
|
|
|
-- The command dispatcher! This state waits for one of
|
-- The command dispatcher! This state waits for one of
|
-- the command inputs to be logic '1' and 'starts' the
|
-- the command inputs to be logic '1' and 'starts' the
|
-- accompanying routine. Note the priority encoding!
|
-- accompanying routine. Note the priority encoding!
|
when wait_for_command =>
|
when wait_for_command =>
|
LCD_DB <= (others => 'Z');
|
LCD_DB <= (others => 'Z');
|
LCD_RS <= '0';
|
LCD_RS <= '0';
|
busy <= '0';
|
busy <= '0';
|
if init = '1' then
|
if init = '1' then
|
busy <= '1';
|
busy <= '1';
|
current_state <= command_init;
|
current_state <= command_init;
|
elsif cls = '1' then
|
elsif cls = '1' then
|
busy <= '1';
|
busy <= '1';
|
current_state <= command_cls;
|
current_state <= command_cls;
|
elsif home = '1' then
|
elsif home = '1' then
|
busy <= '1';
|
busy <= '1';
|
current_state <= command_home;
|
current_state <= command_home;
|
elsif goto10 = '1' then
|
elsif goto10 = '1' then
|
busy <= '1';
|
busy <= '1';
|
current_state <= command_goto10;
|
current_state <= command_goto10;
|
elsif goto20 = '1' then
|
elsif goto20 = '1' then
|
busy <= '1';
|
busy <= '1';
|
current_state <= command_goto20;
|
current_state <= command_goto20;
|
elsif goto30 = '1' then
|
elsif goto30 = '1' then
|
busy <= '1';
|
busy <= '1';
|
current_state <= command_goto30;
|
current_state <= command_goto30;
|
elsif wr = '1' then
|
elsif wr = '1' then
|
-- Read in data! Do NOT forget that here!
|
-- Read in data! Do NOT forget that here!
|
LCD_DB <= data;
|
LCD_DB <= data;
|
busy <= '1';
|
busy <= '1';
|
current_state <= command_wr;
|
current_state <= command_wr;
|
end if;
|
end if;
|
|
|
when command_cls =>
|
when command_cls =>
|
-- Display clear
|
-- Display clear
|
LCD_DB <= "00000001";
|
LCD_DB <= "00000001";
|
LCD_RS <= '0';
|
LCD_RS <= '0';
|
current_state <= pulse_e;
|
current_state <= pulse_e;
|
return_state <= wait_for_command;
|
return_state <= wait_for_command;
|
|
|
when command_home =>
|
when command_home =>
|
-- Cursor home
|
-- Cursor home
|
LCD_DB <= "00000010";
|
LCD_DB <= "00000010";
|
LCD_RS <= '0';
|
LCD_RS <= '0';
|
current_state <= pulse_e;
|
current_state <= pulse_e;
|
return_state <= wait_for_command;
|
return_state <= wait_for_command;
|
|
|
when command_goto10 =>
|
when command_goto10 =>
|
-- Cursor to beginning of line 2nd line...
|
-- Cursor to beginning of line 2nd line...
|
LCD_DB <= "11000000"; --0x80+0x40;
|
LCD_DB <= "11000000"; --0x80+0x40;
|
LCD_RS <= '0';
|
LCD_RS <= '0';
|
current_state <= pulse_e;
|
current_state <= pulse_e;
|
return_state <= wait_for_command;
|
return_state <= wait_for_command;
|
|
|
when command_goto20 =>
|
when command_goto20 =>
|
-- Cursor to beginning of line 3rd line...
|
-- Cursor to beginning of line 3rd line...
|
LCD_DB <= "10010000"; --0x80+0x10;
|
LCD_DB <= "10010000"; --0x80+0x10;
|
LCD_RS <= '0';
|
LCD_RS <= '0';
|
current_state <= pulse_e;
|
current_state <= pulse_e;
|
return_state <= wait_for_command;
|
return_state <= wait_for_command;
|
|
|
when command_goto30 =>
|
when command_goto30 =>
|
-- Cursor to beginning of line 4th line...
|
-- Cursor to beginning of line 4th line...
|
LCD_DB <= "11010000"; --0x80+0x50;
|
LCD_DB <= "11010000"; --0x80+0x50;
|
LCD_RS <= '0';
|
LCD_RS <= '0';
|
current_state <= pulse_e;
|
current_state <= pulse_e;
|
return_state <= wait_for_command;
|
return_state <= wait_for_command;
|
|
|
when command_wr =>
|
when command_wr =>
|
-- Start character write cycle
|
-- Start character write cycle
|
-- Do NOT set data here!
|
-- Do NOT set data here!
|
LCD_RS <= '1';
|
LCD_RS <= '1';
|
current_state <= pulse_e;
|
current_state <= pulse_e;
|
return_state <= wait_for_command;
|
return_state <= wait_for_command;
|
|
|
-- --
|
-- --
|
-- Provide E strobing for data transfer.
|
-- Provide E strobing for data transfer.
|
-- Writes data byte to the LCD.
|
-- Writes data byte to the LCD.
|
-- Please note, DATA and RS are set by the caller!
|
-- Please note, DATA and RS are set by the caller!
|
when pulse_e =>
|
when pulse_e =>
|
-- wait 60 ns before E -> 1 (tAS)
|
-- wait 60 ns before E -> 1 (tAS)
|
delay_counter <= delay_tas;
|
delay_counter <= delay_tas;
|
current_state <= pulse_e_1;
|
current_state <= pulse_e_1;
|
when pulse_e_1 =>
|
when pulse_e_1 =>
|
if delay_counter = 0 or delay_counter = 1 then
|
if delay_counter = 0 or delay_counter = 1 then
|
-- timer set: E = 1 for 500 ns (PWeh)
|
-- timer set: E = 1 for 500 ns (PWeh)
|
delay_counter <= delay_pweh;
|
delay_counter <= delay_pweh;
|
current_state <= pulse_e_2;
|
current_state <= pulse_e_2;
|
else
|
else
|
delay_counter <= delay_counter-1;
|
delay_counter <= delay_counter-1;
|
end if;
|
end if;
|
when pulse_e_2 =>
|
when pulse_e_2 =>
|
LCD_E <= '1';
|
LCD_E <= '1';
|
if delay_counter = 0 or delay_counter = 1 then
|
if delay_counter = 0 or delay_counter = 1 then
|
-- timer set: E = 0 for 500 ns (tCycleE-PWeh)
|
-- timer set: E = 0 for 500 ns (tCycleE-PWeh)
|
delay_counter <= delay_pwel;
|
delay_counter <= delay_pwel;
|
current_state <= pulse_e_3;
|
current_state <= pulse_e_3;
|
else
|
else
|
delay_counter <= delay_counter-1;
|
delay_counter <= delay_counter-1;
|
end if;
|
end if;
|
when pulse_e_3 =>
|
when pulse_e_3 =>
|
LCD_E <= '0';
|
LCD_E <= '0';
|
-- Command completion, check for use busy flag
|
-- Command completion, check for use busy flag
|
if delay_counter = 0 or delay_counter = 1 then
|
if delay_counter = 0 or delay_counter = 1 then
|
-- If no busy flag used, wait for a amount of time.
|
-- If no busy flag used, wait for a amount of time.
|
if use_bf_int = '0' then
|
if use_bf_int = '0' then
|
delay_counter <= delay_no_bf;
|
delay_counter <= delay_no_bf;
|
current_state <= pulse_e_4;
|
current_state <= pulse_e_4;
|
else -- BF used
|
else -- BF used
|
current_state <= pulse_busy_flag;
|
current_state <= pulse_busy_flag;
|
end if;
|
end if;
|
else
|
else
|
delay_counter <= delay_counter-1;
|
delay_counter <= delay_counter-1;
|
end if;
|
end if;
|
when pulse_e_4 =>
|
when pulse_e_4 =>
|
-- Wait for the delay to finsh and then return to the caller.
|
-- Wait for the delay to finsh and then return to the caller.
|
if delay_counter = 0 or delay_counter = 1 then
|
if delay_counter = 0 or delay_counter = 1 then
|
current_state <= return_state;
|
current_state <= return_state;
|
else
|
else
|
delay_counter <= delay_counter-1;
|
delay_counter <= delay_counter-1;
|
end if;
|
end if;
|
|
|
-- Let's read the busy flag, see if the module is busy...
|
-- Let's read the busy flag, see if the module is busy...
|
when pulse_busy_flag =>
|
when pulse_busy_flag =>
|
LCD_DB <= (others => 'Z');
|
LCD_DB <= (others => 'Z');
|
LCD_RW <= '1';
|
LCD_RW <= '1';
|
LCD_RS <= '0';
|
LCD_RS <= '0';
|
-- wait 60 ns before E -> 1 (tAS)
|
-- wait 60 ns before E -> 1 (tAS)
|
delay_counter <= delay_tas;
|
delay_counter <= delay_tas;
|
current_state <= pulse_busy_flag_1;
|
current_state <= pulse_busy_flag_1;
|
when pulse_busy_flag_1 =>
|
when pulse_busy_flag_1 =>
|
LCD_RW <= '1';
|
LCD_RW <= '1';
|
if delay_counter = 0 or delay_counter = 1 then
|
if delay_counter = 0 or delay_counter = 1 then
|
-- timer set: E = 1 for 500 ns (tPWeh)
|
-- timer set: E = 1 for 500 ns (tPWeh)
|
delay_counter <= delay_pweh;
|
delay_counter <= delay_pweh;
|
current_state <= pulse_busy_flag_2;
|
current_state <= pulse_busy_flag_2;
|
else
|
else
|
delay_counter <= delay_counter-1;
|
delay_counter <= delay_counter-1;
|
end if;
|
end if;
|
when pulse_busy_flag_2 =>
|
when pulse_busy_flag_2 =>
|
LCD_E <= '1';
|
LCD_E <= '1';
|
LCD_RW <= '1';
|
LCD_RW <= '1';
|
if delay_counter = 0 or delay_counter = 1 then
|
if delay_counter = 0 or delay_counter = 1 then
|
-- timer set: E = 0 for 500 ns (tPWel)
|
-- timer set: E = 0 for 500 ns (tPWel)
|
delay_counter <= delay_pwel;
|
delay_counter <= delay_pwel;
|
current_state <= pulse_busy_flag_3;
|
current_state <= pulse_busy_flag_3;
|
else
|
else
|
delay_counter <= delay_counter-1;
|
delay_counter <= delay_counter-1;
|
end if;
|
end if;
|
when pulse_busy_flag_3 =>
|
when pulse_busy_flag_3 =>
|
LCD_E <= '0';
|
LCD_E <= '0';
|
LCD_RW <= '1';
|
LCD_RW <= '1';
|
if LCD_DB(7) = '0' then
|
if LCD_DB(7) = '0' then
|
-- operation ended
|
-- operation ended
|
current_state <= pulse_busy_flag_4;
|
current_state <= pulse_busy_flag_4;
|
else
|
else
|
-- operation in progress
|
-- operation in progress
|
current_state <= pulse_busy_flag_5;
|
current_state <= pulse_busy_flag_5;
|
end if;
|
end if;
|
when pulse_busy_flag_4 =>
|
when pulse_busy_flag_4 =>
|
if delay_counter = 0 or delay_counter = 1 then
|
if delay_counter = 0 or delay_counter = 1 then
|
-- Operation ended, return caller
|
-- Operation ended, return caller
|
current_state <= return_state;
|
current_state <= return_state;
|
else
|
else
|
delay_counter <= delay_counter-1;
|
delay_counter <= delay_counter-1;
|
end if;
|
end if;
|
when pulse_busy_flag_5 =>
|
when pulse_busy_flag_5 =>
|
if delay_counter = 0 or delay_counter = 1 then
|
if delay_counter = 0 or delay_counter = 1 then
|
-- Operation in progress, read BF again
|
-- Operation in progress, read BF again
|
current_state <= pulse_busy_flag;
|
current_state <= pulse_busy_flag;
|
else
|
else
|
delay_counter <= delay_counter-1;
|
delay_counter <= delay_counter-1;
|
end if;
|
end if;
|
|
|
when others => null;
|
when others => null;
|
end case;
|
end case;
|
end if;
|
end if;
|
|
|
end process;
|
end process;
|
|
|
end architecture hardware_driver;
|
end architecture hardware_driver;
|
|
|