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

Проект 17. Приёмник UART

Приёмник ловит старт-бит, а затем читает каждый бит строго в середине интервала — так связь остаётся надёжной даже при небольшом расхождении частот.

Задача

Принять байт с последовательной линии и выдать его с признаком готовности.

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

Алгоритм

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

Код на VHDL

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

--=====================================================
-- Проект 17. Приёмник UART
-- Ловит старт-бит, выбирает каждый бит в середине
-- интервала и выдаёт готовый байт с признаком valid.
--=====================================================
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity uart_rx is
    generic (
        CLK_DIV : integer := 5208                       -- 50 МГц / 9600 бод
    );
    port (
        clk     : in  std_logic;
        rst     : in  std_logic;
        rx      : in  std_logic;                        -- линия приёма
        data    : out std_logic_vector(7 downto 0);
        valid   : out std_logic                         -- байт принят
    );
end entity uart_rx;

architecture rtl of uart_rx is
    type state_t is (IDLE, START_BIT, DATA_BITS, STOP_BIT);
    signal state   : state_t := IDLE;
    signal sync    : std_logic_vector(1 downto 0) := "11";
    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');
begin
    process (clk, rst)
    begin
        if rst = '1' then
            state <= IDLE;
            valid <= '0';
            sync  <= "11";
        elsif rising_edge(clk) then
            sync  <= sync(0) & rx;                      -- синхронизация входа
            valid <= '0';                               -- признак живёт один такт

            case state is
                when IDLE =>
                    if sync(1) = '0' then               -- обнаружен старт-бит
                        baud  <= 0;
                        state <= START_BIT;
                    end if;

                when START_BIT =>
                    if baud = CLK_DIV/2 - 1 then        -- смещаемся в середину бита
                        if sync(1) = '0' then
                            baud    <= 0;
                            bit_idx <= 0;
                            state   <= DATA_BITS;
                        else
                            state <= IDLE;              -- ложное срабатывание
                        end if;
                    else
                        baud <= baud + 1;
                    end if;

                when DATA_BITS =>
                    if baud = CLK_DIV - 1 then
                        baud         <= 0;
                        shreg(bit_idx) <= sync(1);      -- выборка в середине бита
                        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 =>
                    if baud = CLK_DIV - 1 then
                        baud  <= 0;
                        state <= IDLE;
                        if sync(1) = '1' then           -- стоп-бит корректен
                            data  <= shreg;
                            valid <= '1';
                        end if;
                    else
                        baud <= baud + 1;
                    end if;
            end case;
        end if;
    end process;
end architecture rtl;

Пояснения

  • Выборка в середине бита — ключевой приём: он даёт максимальный запас по расхождению тактовых частот передатчика и приёмника.
  • Проверка уровня в середине старт-бита отсеивает короткие помехи, которые иначе запустили бы ложный приём.
  • Признак готовности живёт ровно один такт — это стандартный интерфейс для передачи данных дальше по схеме.

Что дальше

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