-------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------
|
-- Title : 16x2 LCD controller
|
-- Title : 16x2 LCD controller
|
-- Project :
|
-- Project :
|
-------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------
|
-- File : lcd16x2_ctrl.vhd
|
-- File : lcd16x2_ctrl.vhd
|
-- Author : <stachelsau@T420>
|
-- Author : <stachelsau@T420>
|
-- Company :
|
-- Company :
|
-- Created : 2012-07-28
|
-- Created : 2012-07-28
|
-- Last update: 2012-07-29
|
-- Last update: 2012-11-28
|
-- Platform :
|
-- Platform :
|
-- Standard : VHDL'93/02
|
-- Standard : VHDL'93/02
|
-------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------
|
-- Description: The controller initializes the display when rst goes to '0'.
|
-- Description: The controller initializes the display when rst goes to '0'.
|
-- After that it writes the contend of the input signals
|
-- After that it writes the contend of the input signals
|
-- line1_buffer and line2_buffer continously to the display.
|
-- line1_buffer and line2_buffer continously to the display.
|
-------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------
|
-- Copyright (c) 2012
|
-- Copyright (c) 2012
|
-------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------
|
-- Revisions :
|
-- Revisions :
|
-- Date Version Author Description
|
-- Date Version Author Description
|
-- 2012-07-28 1.0 stachelsau Created
|
-- 2012-07-28 1.0 stachelsau Created
|
-------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------
|
|
|
library ieee;
|
library ieee;
|
use ieee.std_logic_1164.all;
|
use ieee.std_logic_1164.all;
|
|
|
entity lcd16x2_ctrl is
|
entity lcd16x2_ctrl is
|
|
|
generic (
|
generic (
|
CLK_PERIOD_NS : positive := 20); -- 50MHz
|
CLK_PERIOD_NS : positive := 20); -- 50MHz
|
|
|
port (
|
port (
|
clk : in std_logic;
|
clk : in std_logic;
|
rst : in std_logic;
|
rst : in std_logic;
|
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 : out std_logic_vector(7 downto 4);
|
lcd_db : out std_logic_vector(7 downto 4);
|
line1_buffer : in std_logic_vector(127 downto 0); -- 16x8bit
|
line1_buffer : in std_logic_vector(127 downto 0); -- 16x8bit
|
line2_buffer : in std_logic_vector(127 downto 0));
|
line2_buffer : in std_logic_vector(127 downto 0));
|
|
|
end entity lcd16x2_ctrl;
|
end entity lcd16x2_ctrl;
|
|
|
architecture rtl of lcd16x2_ctrl is
|
architecture rtl of lcd16x2_ctrl is
|
|
|
constant DELAY_15_MS : positive := 15 * 10**6 / CLK_PERIOD_NS + 1;
|
constant DELAY_15_MS : positive := 15 * 10**6 / CLK_PERIOD_NS + 1;
|
constant DELAY_1640_US : positive := 1640 * 10**3 / CLK_PERIOD_NS + 1;
|
constant DELAY_1640_US : positive := 1640 * 10**3 / CLK_PERIOD_NS + 1;
|
constant DELAY_4100_US : positive := 4100 * 10**3 / CLK_PERIOD_NS + 1;
|
constant DELAY_4100_US : positive := 4100 * 10**3 / CLK_PERIOD_NS + 1;
|
constant DELAY_100_US : positive := 100 * 10**3 / CLK_PERIOD_NS + 1;
|
constant DELAY_100_US : positive := 100 * 10**3 / CLK_PERIOD_NS + 1;
|
constant DELAY_40_US : positive := 40 * 10**3 / CLK_PERIOD_NS + 1;
|
constant DELAY_40_US : positive := 40 * 10**3 / CLK_PERIOD_NS + 1;
|
|
|
constant DELAY_NIBBLE : positive := 10**3 / CLK_PERIOD_NS + 1;
|
constant DELAY_NIBBLE : positive := 10**3 / CLK_PERIOD_NS + 1;
|
constant DELAY_LCD_E : positive := 230 / CLK_PERIOD_NS + 1;
|
constant DELAY_LCD_E : positive := 230 / CLK_PERIOD_NS + 1;
|
constant DELAY_SETUP_HOLD : positive := 40 / CLK_PERIOD_NS + 1;
|
constant DELAY_SETUP_HOLD : positive := 40 / CLK_PERIOD_NS + 1;
|
|
|
constant MAX_DELAY : positive := DELAY_15_MS;
|
constant MAX_DELAY : positive := DELAY_15_MS;
|
|
|
-- this record describes one write operation
|
-- this record describes one write operation
|
type op_t is record
|
type op_t is record
|
rs : std_logic;
|
rs : std_logic;
|
data : std_logic_vector(7 downto 0);
|
data : std_logic_vector(7 downto 0);
|
delay_h : integer range 0 to MAX_DELAY;
|
delay_h : integer range 0 to MAX_DELAY;
|
delay_l : integer range 0 to MAX_DELAY;
|
delay_l : integer range 0 to MAX_DELAY;
|
end record op_t;
|
end record op_t;
|
constant default : op_t := (rs => '1', data => X"00", delay_h => DELAY_NIBBLE, delay_l => DELAY_40_US);
|
constant default_op : op_t := (rs => '1', data => X"00", delay_h => DELAY_NIBBLE, delay_l => DELAY_40_US);
|
constant op_select_line1 : op_t := (rs => '0', data => X"80", delay_h => DELAY_NIBBLE, delay_l => DELAY_40_US);
|
constant op_select_line1 : op_t := (rs => '0', data => X"80", delay_h => DELAY_NIBBLE, delay_l => DELAY_40_US);
|
constant op_select_line2 : op_t := (rs => '0', data => X"C0", delay_h => DELAY_NIBBLE, delay_l => DELAY_40_US);
|
constant op_select_line2 : op_t := (rs => '0', data => X"C0", delay_h => DELAY_NIBBLE, delay_l => DELAY_40_US);
|
|
|
-- init + config operations:
|
-- init + config operations:
|
-- write 3 x 0x3 followed by 0x2
|
-- write 3 x 0x3 followed by 0x2
|
-- function set command
|
-- function set command
|
-- entry mode set command
|
-- entry mode set command
|
-- display on/off command
|
-- display on/off command
|
-- clear display
|
-- clear display
|
type config_ops_t is array(0 to 5) of op_t;
|
type config_ops_t is array(0 to 5) of op_t;
|
constant config_ops : config_ops_t
|
constant config_ops : config_ops_t
|
:= (5 => (rs => '0', data => X"33", delay_h => DELAY_4100_US, delay_l => DELAY_100_US),
|
:= (5 => (rs => '0', data => X"33", delay_h => DELAY_4100_US, delay_l => DELAY_100_US),
|
4 => (rs => '0', data => X"32", delay_h => DELAY_40_US, delay_l => DELAY_40_US),
|
4 => (rs => '0', data => X"32", delay_h => DELAY_40_US, delay_l => DELAY_40_US),
|
3 => (rs => '0', data => X"28", delay_h => DELAY_NIBBLE, delay_l => DELAY_40_US),
|
3 => (rs => '0', data => X"28", delay_h => DELAY_NIBBLE, delay_l => DELAY_40_US),
|
2 => (rs => '0', data => X"06", delay_h => DELAY_NIBBLE, delay_l => DELAY_40_US),
|
2 => (rs => '0', data => X"06", delay_h => DELAY_NIBBLE, delay_l => DELAY_40_US),
|
1 => (rs => '0', data => X"0C", delay_h => DELAY_NIBBLE, delay_l => DELAY_40_US),
|
1 => (rs => '0', data => X"0C", delay_h => DELAY_NIBBLE, delay_l => DELAY_40_US),
|
0 => (rs => '0', data => X"01", delay_h => DELAY_NIBBLE, delay_l => DELAY_1640_US));
|
0 => (rs => '0', data => X"01", delay_h => DELAY_NIBBLE, delay_l => DELAY_1640_US));
|
|
|
signal this_op : op_t;
|
signal this_op : op_t;
|
|
|
type op_state_t is (IDLE,
|
type op_state_t is (IDLE,
|
WAIT_SETUP_H,
|
WAIT_SETUP_H,
|
ENABLE_H,
|
ENABLE_H,
|
WAIT_HOLD_H,
|
WAIT_HOLD_H,
|
WAIT_DELAY_H,
|
WAIT_DELAY_H,
|
WAIT_SETUP_L,
|
WAIT_SETUP_L,
|
ENABLE_L,
|
ENABLE_L,
|
WAIT_HOLD_L,
|
WAIT_HOLD_L,
|
WAIT_DELAY_L,
|
WAIT_DELAY_L,
|
DONE);
|
DONE);
|
|
|
signal op_state : op_state_t := DONE;
|
signal op_state : op_state_t := DONE;
|
signal next_op_state : op_state_t;
|
signal next_op_state : op_state_t;
|
signal cnt : natural range 0 to MAX_DELAY;
|
signal cnt : natural range 0 to MAX_DELAY;
|
signal next_cnt : natural range 0 to MAX_DELAY;
|
signal next_cnt : natural range 0 to MAX_DELAY;
|
|
|
type state_t is (RESET,
|
type state_t is (RESET,
|
CONFIG,
|
CONFIG,
|
SELECT_LINE1,
|
SELECT_LINE1,
|
WRITE_LINE1,
|
WRITE_LINE1,
|
SELECT_LINE2,
|
SELECT_LINE2,
|
WRITE_LINE2);
|
WRITE_LINE2);
|
|
|
signal state : state_t := RESET;
|
signal state : state_t := RESET;
|
signal next_state : state_t;
|
signal next_state : state_t;
|
signal ptr : natural range 0 to 15 := 0;
|
signal ptr : natural range 0 to 15 := 0;
|
signal next_ptr : natural range 0 to 15;
|
signal next_ptr : natural range 0 to 15;
|
|
|
begin
|
begin
|
|
|
proc_state : process(state, op_state, ptr, line1_buffer, line2_buffer) is
|
proc_state : process(state, op_state, ptr, line1_buffer, line2_buffer) is
|
begin
|
begin
|
case state is
|
case state is
|
when RESET =>
|
when RESET =>
|
this_op <= default;
|
this_op <= default_op;
|
next_state <= CONFIG;
|
next_state <= CONFIG;
|
next_ptr <= config_ops_t'high;
|
next_ptr <= config_ops_t'high;
|
|
|
when CONFIG =>
|
when CONFIG =>
|
this_op <= config_ops(ptr);
|
this_op <= config_ops(ptr);
|
next_ptr <= ptr;
|
next_ptr <= ptr;
|
next_state <= CONFIG;
|
next_state <= CONFIG;
|
if op_state = DONE then
|
if op_state = DONE then
|
next_ptr <= ptr - 1;
|
next_ptr <= ptr - 1;
|
if ptr = 0 then
|
if ptr = 0 then
|
next_state <= SELECT_LINE1;
|
next_state <= SELECT_LINE1;
|
end if;
|
end if;
|
end if;
|
end if;
|
|
|
when SELECT_LINE1 =>
|
when SELECT_LINE1 =>
|
this_op <= op_select_line1;
|
this_op <= op_select_line1;
|
next_ptr <= 15;
|
next_ptr <= 15;
|
if op_state = DONE then
|
if op_state = DONE then
|
next_state <= WRITE_LINE1;
|
next_state <= WRITE_LINE1;
|
else
|
else
|
next_state <= SELECT_LINE1;
|
next_state <= SELECT_LINE1;
|
end if;
|
end if;
|
|
|
when WRITE_LINE1 =>
|
when WRITE_LINE1 =>
|
this_op <= default;
|
this_op <= default_op;
|
this_op.data <= line1_buffer(ptr*8 + 7 downto ptr*8);
|
this_op.data <= line1_buffer(ptr*8 + 7 downto ptr*8);
|
next_ptr <= ptr;
|
next_ptr <= ptr;
|
next_state <= WRITE_LINE1;
|
next_state <= WRITE_LINE1;
|
if op_state = DONE then
|
if op_state = DONE then
|
next_ptr <= ptr - 1;
|
next_ptr <= ptr - 1;
|
if ptr = 0 then
|
if ptr = 0 then
|
next_state <= SELECT_LINE2;
|
next_state <= SELECT_LINE2;
|
end if;
|
end if;
|
end if;
|
end if;
|
|
|
when SELECT_LINE2 =>
|
when SELECT_LINE2 =>
|
this_op <= op_select_line2;
|
this_op <= op_select_line2;
|
next_ptr <= 15;
|
next_ptr <= 15;
|
if op_state = DONE then
|
if op_state = DONE then
|
next_state <= WRITE_LINE2;
|
next_state <= WRITE_LINE2;
|
else
|
else
|
next_state <= SELECT_LINE2;
|
next_state <= SELECT_LINE2;
|
end if;
|
end if;
|
|
|
when WRITE_LINE2 =>
|
when WRITE_LINE2 =>
|
this_op <= default;
|
this_op <= default_op;
|
this_op.data <= line2_buffer(ptr*8 + 7 downto ptr*8);
|
this_op.data <= line2_buffer(ptr*8 + 7 downto ptr*8);
|
next_ptr <= ptr;
|
next_ptr <= ptr;
|
next_state <= WRITE_LINE2;
|
next_state <= WRITE_LINE2;
|
if op_state = DONE then
|
if op_state = DONE then
|
next_ptr <= ptr - 1;
|
next_ptr <= ptr - 1;
|
if ptr = 0 then
|
if ptr = 0 then
|
next_state <= SELECT_LINE1;
|
next_state <= SELECT_LINE1;
|
end if;
|
end if;
|
end if;
|
end if;
|
|
|
end case;
|
end case;
|
end process proc_state;
|
end process proc_state;
|
|
|
reg_state : process(clk)
|
reg_state : process(clk)
|
begin
|
begin
|
if rising_edge(clk) then
|
if rising_edge(clk) then
|
if rst = '1' then
|
if rst = '1' then
|
state <= RESET;
|
state <= RESET;
|
ptr <= 0;
|
ptr <= 0;
|
else
|
else
|
state <= next_state;
|
state <= next_state;
|
ptr <= next_ptr;
|
ptr <= next_ptr;
|
end if;
|
end if;
|
end if;
|
end if;
|
end process reg_state;
|
end process reg_state;
|
|
|
-- we never read from the lcd
|
-- we never read from the lcd
|
lcd_rw <= '0';
|
lcd_rw <= '0';
|
|
|
proc_op_state : process(op_state, cnt, this_op) is
|
proc_op_state : process(op_state, cnt, this_op) is
|
begin
|
begin
|
case op_state is
|
case op_state is
|
when IDLE =>
|
when IDLE =>
|
lcd_db <= (others => '0');
|
lcd_db <= (others => '0');
|
lcd_rs <= '0';
|
lcd_rs <= '0';
|
lcd_e <= '0';
|
lcd_e <= '0';
|
next_op_state <= WAIT_SETUP_H;
|
next_op_state <= WAIT_SETUP_H;
|
next_cnt <= DELAY_SETUP_HOLD;
|
next_cnt <= DELAY_SETUP_HOLD;
|
|
|
when WAIT_SETUP_H =>
|
when WAIT_SETUP_H =>
|
lcd_db <= this_op.data(7 downto 4);
|
lcd_db <= this_op.data(7 downto 4);
|
lcd_rs <= this_op.rs;
|
lcd_rs <= this_op.rs;
|
lcd_e <= '0';
|
lcd_e <= '0';
|
if cnt = 0 then
|
if cnt = 0 then
|
next_op_state <= ENABLE_H;
|
next_op_state <= ENABLE_H;
|
next_cnt <= DELAY_LCD_E;
|
next_cnt <= DELAY_LCD_E;
|
else
|
else
|
next_op_state <= WAIT_SETUP_H;
|
next_op_state <= WAIT_SETUP_H;
|
next_cnt <= cnt - 1;
|
next_cnt <= cnt - 1;
|
end if;
|
end if;
|
|
|
when ENABLE_H =>
|
when ENABLE_H =>
|
lcd_db <= this_op.data(7 downto 4);
|
lcd_db <= this_op.data(7 downto 4);
|
lcd_rs <= this_op.rs;
|
lcd_rs <= this_op.rs;
|
lcd_e <= '1';
|
lcd_e <= '1';
|
if cnt = 0 then
|
if cnt = 0 then
|
next_op_state <= WAIT_HOLD_H;
|
next_op_state <= WAIT_HOLD_H;
|
next_cnt <= DELAY_SETUP_HOLD;
|
next_cnt <= DELAY_SETUP_HOLD;
|
else
|
else
|
next_op_state <= ENABLE_H;
|
next_op_state <= ENABLE_H;
|
next_cnt <= cnt - 1;
|
next_cnt <= cnt - 1;
|
end if;
|
end if;
|
|
|
when WAIT_HOLD_H =>
|
when WAIT_HOLD_H =>
|
lcd_db <= this_op.data(7 downto 4);
|
lcd_db <= this_op.data(7 downto 4);
|
lcd_rs <= this_op.rs;
|
lcd_rs <= this_op.rs;
|
lcd_e <= '0';
|
lcd_e <= '0';
|
if cnt = 0 then
|
if cnt = 0 then
|
next_op_state <= WAIT_DELAY_H;
|
next_op_state <= WAIT_DELAY_H;
|
next_cnt <= this_op.delay_h;
|
next_cnt <= this_op.delay_h;
|
else
|
else
|
next_op_state <= WAIT_HOLD_H;
|
next_op_state <= WAIT_HOLD_H;
|
next_cnt <= cnt - 1;
|
next_cnt <= cnt - 1;
|
end if;
|
end if;
|
|
|
when WAIT_DELAY_H =>
|
when WAIT_DELAY_H =>
|
lcd_db <= (others => '0');
|
lcd_db <= (others => '0');
|
lcd_rs <= '0';
|
lcd_rs <= '0';
|
lcd_e <= '0';
|
lcd_e <= '0';
|
if cnt = 0 then
|
if cnt = 0 then
|
next_op_state <= WAIT_SETUP_L;
|
next_op_state <= WAIT_SETUP_L;
|
next_cnt <= DELAY_SETUP_HOLD;
|
next_cnt <= DELAY_SETUP_HOLD;
|
else
|
else
|
next_op_state <= WAIT_DELAY_H;
|
next_op_state <= WAIT_DELAY_H;
|
next_cnt <= cnt - 1;
|
next_cnt <= cnt - 1;
|
end if;
|
end if;
|
|
|
when WAIT_SETUP_L =>
|
when WAIT_SETUP_L =>
|
lcd_db <= this_op.data(3 downto 0);
|
lcd_db <= this_op.data(3 downto 0);
|
lcd_rs <= this_op.rs;
|
lcd_rs <= this_op.rs;
|
lcd_e <= '0';
|
lcd_e <= '0';
|
if cnt = 0 then
|
if cnt = 0 then
|
next_op_state <= ENABLE_L;
|
next_op_state <= ENABLE_L;
|
next_cnt <= DELAY_LCD_E;
|
next_cnt <= DELAY_LCD_E;
|
else
|
else
|
next_op_state <= WAIT_SETUP_L;
|
next_op_state <= WAIT_SETUP_L;
|
next_cnt <= cnt - 1;
|
next_cnt <= cnt - 1;
|
end if;
|
end if;
|
|
|
when ENABLE_L =>
|
when ENABLE_L =>
|
lcd_db <= this_op.data(3 downto 0);
|
lcd_db <= this_op.data(3 downto 0);
|
lcd_rs <= this_op.rs;
|
lcd_rs <= this_op.rs;
|
lcd_e <= '1';
|
lcd_e <= '1';
|
if cnt = 0 then
|
if cnt = 0 then
|
next_op_state <= WAIT_HOLD_L;
|
next_op_state <= WAIT_HOLD_L;
|
next_cnt <= DELAY_SETUP_HOLD;
|
next_cnt <= DELAY_SETUP_HOLD;
|
else
|
else
|
next_op_state <= ENABLE_L;
|
next_op_state <= ENABLE_L;
|
next_cnt <= cnt - 1;
|
next_cnt <= cnt - 1;
|
end if;
|
end if;
|
|
|
when WAIT_HOLD_L =>
|
when WAIT_HOLD_L =>
|
lcd_db <= this_op.data(3 downto 0);
|
lcd_db <= this_op.data(3 downto 0);
|
lcd_rs <= this_op.rs;
|
lcd_rs <= this_op.rs;
|
lcd_e <= '0';
|
lcd_e <= '0';
|
if cnt = 0 then
|
if cnt = 0 then
|
next_op_state <= WAIT_DELAY_L;
|
next_op_state <= WAIT_DELAY_L;
|
next_cnt <= this_op.delay_l;
|
next_cnt <= this_op.delay_l;
|
else
|
else
|
next_op_state <= WAIT_HOLD_L;
|
next_op_state <= WAIT_HOLD_L;
|
next_cnt <= cnt - 1;
|
next_cnt <= cnt - 1;
|
end if;
|
end if;
|
|
|
when WAIT_DELAY_L =>
|
when WAIT_DELAY_L =>
|
lcd_db <= (others => '0');
|
lcd_db <= (others => '0');
|
lcd_rs <= '0';
|
lcd_rs <= '0';
|
lcd_e <= '0';
|
lcd_e <= '0';
|
if cnt = 0 then
|
if cnt = 0 then
|
next_op_state <= DONE;
|
next_op_state <= DONE;
|
next_cnt <= 0;
|
next_cnt <= 0;
|
else
|
else
|
next_op_state <= WAIT_DELAY_L;
|
next_op_state <= WAIT_DELAY_L;
|
next_cnt <= cnt - 1;
|
next_cnt <= cnt - 1;
|
end if;
|
end if;
|
|
|
when DONE =>
|
when DONE =>
|
lcd_db <= (others => '0');
|
lcd_db <= (others => '0');
|
lcd_rs <= '0';
|
lcd_rs <= '0';
|
lcd_e <= '0';
|
lcd_e <= '0';
|
next_op_state <= IDLE;
|
next_op_state <= IDLE;
|
next_cnt <= 0;
|
next_cnt <= 0;
|
|
|
end case;
|
end case;
|
end process proc_op_state;
|
end process proc_op_state;
|
|
|
reg_op_state : process (clk) is
|
reg_op_state : process (clk) is
|
begin
|
begin
|
if rising_edge(clk) then
|
if rising_edge(clk) then
|
if state = RESET then
|
if state = RESET then
|
op_state <= IDLE;
|
op_state <= IDLE;
|
else
|
else
|
op_state <= next_op_state;
|
op_state <= next_op_state;
|
cnt <= next_cnt;
|
cnt <= next_cnt;
|
end if;
|
end if;
|
end if;
|
end if;
|
end process reg_op_state;
|
end process reg_op_state;
|
|
|
|
|
end architecture rtl;
|
end architecture rtl;
|
|
|