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

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

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

Задача

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

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

Алгоритм

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

Код на VHDL

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

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

entity uart_tx is
    generic (
        CLK_DIV : 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 CLK_DIV := 0;
    signal bit_idx : integer range 0 to 7 := 0;
    signal shreg   : std_logic_vector(7 downto 0) := (others => '0');
    signal tx_reg  : std_logic := '1';                  -- линия в покое высокая
begin
    process (clk, rst)
    begin
        if rst = '1' then
            state   <= IDLE;
            tx_reg  <= '1';
            baud    <= 0;
            bit_idx <= 0;
        elsif rising_edge(clk) then
            case state is
                when IDLE =>
                    tx_reg <= '1';
                    if start = '1' then
                        shreg <= data;                  -- защёлкиваем байт
                        baud  <= 0;
                        state <= START_BIT;
                    end if;

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

                when DATA_BITS =>
                    tx_reg <= shreg(bit_idx);           -- младшим битом вперёд
                    if baud = CLK_DIV - 1 then
                        baud <= 0;
                        if bit_idx = 7 then
                            state <= STOP_BIT;
                        else
                            bit_idx <= bit_idx + 1;
                        end if;
                    else
                        baud <= baud + 1;
                    end if;

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

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

Пояснения

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

Что дальше

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