AFON // радиотехник affonya@mail.ru
← Проекты на VHDL

Проект 12. Передатчик UART

Модуль превращает байт в последовательный кадр: старт-бит, восемь бит данных, стоп-бит. Скорость задаётся делителем тактовой частоты.

Задача

Передать байт по последовательной линии в формате UART.

Тип схемы: Конечный автомат с четырьмя состояниями и счётчиком битового интервала.
Модуль: uart_tx. Среда: Quartus, Vivado, ISE или свободный GHDL для моделирования. Теорию по языку смотрите в статье VHDL — язык описания аппаратуры, по самим микросхемам — на странице ПЛИС.

Алгоритм

  1. Дождаться запроса передачи и запомнить байт в сдвиговом регистре.
  2. Выдать старт-бит длительностью в один битовый интервал.
  3. Передать восемь бит данных, младшим вперёд.
  4. Завершить кадр стоп-битом и вернуться в ожидание.

Код на VHDL

Код проверен компилятором GHDL: проходит анализ и элаборацию без ошибок и предупреждений. Скачать: v12.vhd

--=====================================================
-- Проект 12. Передатчик UART
-- Кадр: старт-бит, восемь бит данных, стоп-бит.
-- Скорость задаётся делителем тактовой частоты.
--=====================================================
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity uart_tx is
    generic ( DIVISOR : integer := 5208 );              -- 50 МГц / 9600 бод
    port (
        clk     : in  std_logic;
        rst     : in  std_logic;
        start   : in  std_logic;                        -- запрос передачи
        data    : in  std_logic_vector(7 downto 0);
        tx      : out std_logic;                        -- линия передачи
        busy    : out std_logic
    );
end entity uart_tx;

architecture rtl of uart_tx is
    type state_t is (IDLE, START_BIT, DATA_BITS, STOP_BIT);
    signal state  : state_t := IDLE;
    signal baud   : integer range 0 to DIVISOR := 0;
    signal bitidx : integer range 0 to 7 := 0;
    signal shreg  : std_logic_vector(7 downto 0) := (others => '0');
    signal tx_r   : std_logic := '1';                   -- в покое линия в единице
begin
    process (clk, rst)
    begin
        if rst = '1' then
            state <= IDLE; tx_r <= '1'; baud <= 0; bitidx <= 0;
        elsif rising_edge(clk) then
            case state is
                when IDLE =>
                    tx_r <= '1';
                    if start = '1' then
                        shreg <= data;                  -- запоминаем байт
                        baud  <= 0;
                        state <= START_BIT;
                    end if;

                when START_BIT =>
                    tx_r <= '0';                        -- старт-бит
                    if baud = DIVISOR - 1 then
                        baud <= 0; bitidx <= 0; state <= DATA_BITS;
                    else
                        baud <= baud + 1;
                    end if;

                when DATA_BITS =>
                    tx_r <= shreg(0);                   -- младшим битом вперёд
                    if baud = DIVISOR - 1 then
                        baud  <= 0;
                        shreg <= '0' & shreg(7 downto 1);
                        if bitidx = 7 then
                            state <= STOP_BIT;
                        else
                            bitidx <= bitidx + 1;
                        end if;
                    else
                        baud <= baud + 1;
                    end if;

                when STOP_BIT =>
                    tx_r <= '1';                        -- стоп-бит
                    if baud = DIVISOR - 1 then
                        baud <= 0; state <= IDLE;
                    else
                        baud <= baud + 1;
                    end if;
            end case;
        end if;
    end process;

    tx   <= tx_r;
    busy <= '0' when state = IDLE else '1';
end architecture rtl;

Пояснения

  • Делитель считается как тактовая частота, делённая на скорость: при 50 МГц и 9600 бод получается 5208 тактов на бит.
  • В покое линия удерживается в единице — иначе приёмник примет тишину за бесконечный старт-бит.
  • Сигнал занятости нужен внешней логике, чтобы не подать новый байт посреди кадра.

Что дальше

Следующие проекты — в разделе проекты на VHDL. Сравнить подход с языком Verilog можно в статье Verilog, а разобраться в устройстве самих кристаллов — в статье ПЛИС: FPGA и CPLD.