Товарищи, а кто-нибудь работал с LCD 16x2 с шиной данных 4 бита?
Пользуясь datasheet'ом и некоторыми исходниками для 8ми битов написал вариант для ML605, но он работает не стабильно: раз 20 надо нажать кнопку, на которую завязан reset, и появляется верная надпись, ну не то чтоб 20, а совершенно рандомно.
Я думаю так, если на экране кракозябра держится весь цикл, и меняется при рестарте, то проблема в тактах или в задержках... Состояния автомата кажись описал верно. ModelSim и iSim дают адекватные результаты моделирования.
Подскажите, в чём я опять накосячил? Ради теста, первая половина верхней строки содержит символ 1, вторая 2.
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.std_logic_unsigned.all;
use ieee.numeric_std.all;
-- Generic tickNum must be set such that:
-- tickNum = 10us / Period clk
-- This provides an internal tick every 10us
-- Clk: 100 MHz, tickNum: 1000
-- Clk: 50 MHz, tickNum: 500
-- Clk: 32 MHz, tickNum: 320
-- Clk: 10 MHz, tickNum: 100
use Work.Global_pack.all;
entity LCD_iterface is
generic ( tickNum: positive := 500);
port (
reset : in STD_LOGIC;
clk : in STD_LOGIC;
clk_enable : in STD_LOGIC;
-- new char (dIn - data, charNum - position, wEn - enable write)
dIn : in std_logic_vector(7 downto 0);
charNum : in std_logic_vector(4 downto 0); -- 32 positions
wEn : in std_logic;
-- LCD Interface
LCD_RS : out STD_LOGIC;
LCD_RW : out STD_LOGIC;
LCD_E : out STD_LOGIC;
LCD_DATA_BUS: out STD_LOGIC_VECTOR (3 DOWNTO 0)
);
end LCD_iterface;
architecture Behavioral of LCD_iterface is
-- LCD interface constants
constant LCD_READ : std_logic := '1';
constant LCD_WRITE : std_logic := '0';
constant DATA_CODE : std_logic := '1';
constant INSN_CODE : std_logic := '0';
-- LCD characteristic
constant LCD_width : integer := 16;
constant LCD_height : integer := 2; -- LCD 16x2
constant LCD_length : integer := 32;
-- Tick Generation
subtype TICK_COUNTER_TYPE is integer range 0 to tickNum;
signal tick : std_logic;
constant WARMUP_DELAY : integer := 4000; -- 10us*2000: 40 ms
constant INIT_DELAY_1 : integer := 410; -- 10us*410: 4.1 ms
constant INIT_DELAY_2 : integer := 10; -- 10us*10: 100 us
constant RETURN_DELAY : integer := 160; -- 10us*160: 1.6 ms
constant CHAR_DELAY : integer := 4; -- 10us*4: 40 us
constant E_DELAY : integer := 4; -- 10us*4: 40 us
subtype DELAY_TYPE is integer range 0 to WARMUP_DELAY;
signal timer : DELAY_TYPE;
type INIT_ROM_TYPE is array (0 to 6) of std_logic_vector(7 downto 0);
constant initROM : INIT_ROM_TYPE := (b"00000011", -- Init
b"00000011", -- Init
b"00000011", -- Init
b"0010_1000", -- Function Set: 8 bit, 2 lines, 5x7 characters
b"0000_1100", -- Display On/Off Control: Display on, Cursor off, Blink off
b"0000_0001", -- Clear Display: Move cursor to home
b"0000_0110"); -- Entry Mode Set: Auto increment cursor, don't shift display
type CHAR_RAM_TYPE is array(0 to (LCD_length-1)) of std_logic_vector(7 downto 0);
signal charRAM : CHAR_RAM_TYPE := (0 to 8=>x"31",
16 to 24 =>x"32",
others=>x"A0");
signal setLine : std_logic;
signal lineNum : integer range 0 to 1;
signal initialising : std_logic;
signal initROMPointer : integer range 0 to INIT_ROM_TYPE'high;
signal charRAMPointer : integer range 0 to CHAR_RAM_TYPE'high;
type STATE_TYPE is (WARMUP, STAGE1, STAGE2,
SEND_UP, SEND_LOW,
TOGGLE_E, TOGGLE_E2, DELAY);
signal state : STATE_TYPE;
signal next_command : STATE_TYPE;
signal DATA_BUS_LOW : STD_LOGIC_VECTOR (3 DOWNTO 0);
signal DATA_BUS_VALUE : STD_LOGIC_VECTOR (7 DOWNTO 0);
BEGIN
LCD_RW <= LCD_WRITE;
TickGen : process(clk)
variable tickCounter : TICK_COUNTER_TYPE;
begin
if (rising_edge(clk) and clk_enable='1') then
if (tickCounter = 0) then
tickCounter := TICK_COUNTER_TYPE'high-1;
tick <= '1';
else
tickCounter := tickCounter - 1;
tick <= '0';
end if;
end if;
end process TickGen;
CharRAMWrite : process(clk)
variable add : integer range 0 to (LCD_length-1);
begin
if (rising_edge(clk)) then
if (wEn='1') then
add := to_integer(unsigned(charNum));
charRAM(add) <= dIn;
end if;
end if;
end process CharRAMWrite;
Controller : process (clk)
begin
if (rising_edge(clk)) then
if (reset='1') then
timer <= WARMUP_DELAY;
initROMPointer <= 0;
charRAMPointer <= 0;
LCD_RS <= INSN_CODE;
LCD_E <= '0';
LCD_DATA_BUS <= (others => '0');
initialising <= '1';
setLine <= '0';
lineNum <= 0;
state <= WARMUP;
elsif (tick='1') then
case state is
-- Perform initial long warmup delay
when WARMUP =>
if (timer=0) then
state <= STAGE1;
else
timer <= timer - 1;
end if;
-- Set the LCD data
-- Set the LCD RS
-- Initialise the timer with the required delay
when STAGE1 =>
if (initialising='1') then
LCD_RS <= INSN_CODE;
DATA_BUS_VALUE <= initROM(initROMPointer);
if (initROMPointer<3)
then state <= SEND_LOW;
else state <= SEND_UP;
end if;
elsif (setLine='1') then
LCD_RS <= INSN_CODE;
case lineNum is
when 0 =>
DATA_BUS_VALUE <= b"1000_0000"; -- x00
timer <= CHAR_DELAY;
when 1 =>
DATA_BUS_VALUE <= b"1100_0000"; -- x40
timer <= CHAR_DELAY;
end case;
state <= SEND_UP;
else
timer <= CHAR_DELAY;
LCD_RS <= DATA_CODE;
DATA_BUS_VALUE <= charRAM(charRAMPointer)(7 downto 0);
state <= SEND_UP;
end if;
--next_command <= STAGE2;
-- Set LCD_E (latching RS and RW)
when STAGE2 =>
if (initialising='1') then
if (initROMPointer=INIT_ROM_TYPE'high) then
initialising <= '0';
else
initROMPointer <= initROMPointer + 1;
end if;
elsif (setLine='1') then
setLine <= '0';
else
if (charRAMPointer=(LCD_width-1)) then
setLine <= '1';
lineNum <= 1;
elsif (charRAMPointer=(LCD_length-1)) then
setLine <= '1';
lineNum <= 0;
end if;
if (charRAMPointer=CHAR_RAM_TYPE'high) then
charRAMPointer <= 0;
else
charRAMPointer <= charRAMPointer + 1;
end if;
end if;
--LCD_E <= '1';
state <= DELAY;
--next_command <= STAGE1;
-- SEND --
when SEND_UP =>
LCD_E <= '1';
LCD_DATA_BUS <= DATA_BUS_VALUE(7 downto 4);
state <= TOGGLE_E;
when SEND_LOW =>
LCD_E <= '1';
LCD_DATA_BUS <= DATA_BUS_VALUE(3 downto 0);
state <= TOGGLE_E2;
-- TOGGLE -- // Clear LCD_E (latching data)
when TOGGLE_E =>
LCD_E <= '0';
timer <= E_DELAY;
state <= SEND_LOW;
when TOGGLE_E2 =>
LCD_E <= '0';
if (initialising='1')
then case (initROMPointer) is
when 0 => timer <= INIT_DELAY_1;
when 1 => timer <= INIT_DELAY_2;
when 2 => timer <= E_DELAY;
when 3 => timer <= E_DELAY;
when 4 => timer <= E_DELAY;
when 5 => timer <= RETURN_DELAY;
when 6 => timer <= E_DELAY;
end case;
else timer <= E_DELAY;
end if;
state <= STAGE2;
-- Provide delay to allow instruciton to execute
when DELAY =>
if (timer=0) then
state <= STAGE1;
else
timer <= timer - 1;
end if;
end case;
end if;
end if;
end process;
end Behavioral;
FPGA_LCD_HD44780.pdf
LCD16D2_ST7077U.pdf
_____________________
отловил ошибку. вопрос снимается :D