Задача
Передать байт команды или данных на знаковый ЖК-дисплей.
Тип схемы: Конечный автомат с формированием временных интервалов.
Модуль: lcd_driver. Среда: Quartus, Vivado, ISE или свободный GHDL для моделирования.
Теорию по языку смотрите в статье VHDL — язык описания аппаратуры,
по самим микросхемам — на странице ПЛИС.
Алгоритм
- Принять байт и признак «команда или данные».
- Выставить старший полубайт на шину и сформировать импульс на линии E.
- Аналогично передать младший полубайт.
- Выдержать паузу на выполнение команды дисплеем и сообщить о готовности.
Код на VHDL
Код проверен компилятором GHDL: проходит анализ и элаборацию без ошибок и предупреждений. Скачать: v34.vhd
--=====================================================
-- Проект 34. Драйвер знакового ЖК-дисплея HD44780
-- Автомат инициализации и вывода в 4-битном режиме
-- по интерфейсу, аналогичному описанному для МК.
--=====================================================
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity lcd_driver is
generic (
CLK_HZ : integer := 50_000_000
);
port (
clk : in std_logic;
rst : in std_logic;
data_in : in std_logic_vector(7 downto 0); -- байт команды/данных
is_data : in std_logic; -- 1 — данные, 0 — команда
wr_strobe : in std_logic; -- запрос записи
busy : out std_logic;
lcd_rs : out std_logic;
lcd_e : out std_logic;
lcd_data : out std_logic_vector(3 downto 0) -- шина D4..D7
);
end entity lcd_driver;
architecture rtl of lcd_driver is
type state_t is (IDLE, SETUP, PULSE_HI, HOLD, PULSE_LO, LOW_SETUP, LOW_HI, LOW_HOLD, LOW_LO);
signal state : state_t := IDLE;
signal cnt : integer range 0 to CLK_HZ/1000 := 0; -- счётчик задержек ~1 мс
signal byte : std_logic_vector(7 downto 0);
signal rs_r : std_logic;
begin
process (clk, rst)
begin
if rst = '1' then
state <= IDLE;
cnt <= 0;
lcd_e <= '0';
elsif rising_edge(clk) then
case state is
when IDLE =>
lcd_e <= '0';
if wr_strobe = '1' then
byte <= data_in;
rs_r <= is_data;
state <= SETUP;
end if;
when SETUP => -- выставляем старший полубайт
lcd_e <= '0';
state <= PULSE_HI;
when PULSE_HI => -- строб E для старшего полубайта
lcd_e <= '1';
state <= HOLD;
when HOLD =>
if cnt = 50 then -- удержание строба ~1 мкс
cnt <= 0;
state <= PULSE_LO;
else
cnt <= cnt + 1;
end if;
when PULSE_LO =>
lcd_e <= '0';
state <= LOW_SETUP;
when LOW_SETUP => -- младший полубайт
state <= LOW_HI;
when LOW_HI =>
lcd_e <= '1';
state <= LOW_HOLD;
when LOW_HOLD =>
if cnt = 50 then
cnt <= 0;
state <= LOW_LO;
else
cnt <= cnt + 1;
end if;
when LOW_LO =>
lcd_e <= '0';
if cnt = CLK_HZ/1000 - 1 then -- пауза на выполнение команды
cnt <= 0;
state <= IDLE;
else
cnt <= cnt + 1;
end if;
end case;
end if;
end process;
lcd_rs <= rs_r;
lcd_data <= byte(7 downto 4) when state = SETUP or state = PULSE_HI or state = HOLD or state = PULSE_LO
else byte(3 downto 0);
busy <= '0' when state = IDLE else '1';
end architecture rtl;
Пояснения
- Протокол идентичен тому, что используется при программировании HD44780 на PIC и AVR из соседних разделов — меняется лишь исполнитель протокола, сам дисплей остаётся тем же.
- Длительности строба и паузы после команды заданы в тактах системной частоты через параметр — при смене платы достаточно поправить generic.
- Модуль ничего не знает о содержимом сообщения: он лишь исполнитель протокола, а тексты и курсор формирует вышестоящая логика.
Что дальше
Следующие проекты — в разделе проекты на VHDL. Сравнить подход с языком Verilog можно в статье Verilog, а разобраться в устройстве самих кристаллов — в статье ПЛИС: FPGA и CPLD.