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

Проект 18. Ведущий SPI

Модуль формирует тактовый сигнал, выдвигает байт наружу и одновременно принимает байт от ведомого. Один обмен — восемь тактов, данные идут в обе стороны.

Задача

Обменяться байтом с ведомым устройством по шине SPI.

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

Алгоритм

  1. Опустить сигнал выбора ведомого и начать генерацию тактов.
  2. По одному фронту принимать бит с линии MISO.
  3. По противоположному фронту выдвигать очередной бит на MOSI.
  4. После восьми битов поднять выбор и выдать признак завершения.

Код на VHDL

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

--=====================================================
-- Проект 18. Ведущий SPI
-- Выдаёт тактовый сигнал, сдвигает байт наружу
-- и одновременно принимает байт от ведомого.
--=====================================================
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity spi_master is
    generic (
        CLK_DIV : integer := 4                          -- делитель тактовой частоты
    );
    port (
        clk      : in  std_logic;
        rst      : in  std_logic;
        start    : in  std_logic;
        data_in  : in  std_logic_vector(7 downto 0);    -- байт на передачу
        data_out : out std_logic_vector(7 downto 0);    -- принятый байт
        done     : out std_logic;
        sclk     : out std_logic;
        mosi     : out std_logic;
        miso     : in  std_logic;
        cs_n     : out std_logic                        -- выбор ведомого
    );
end entity spi_master;

architecture rtl of spi_master is
    type state_t is (IDLE, TRANSFER);
    signal state    : state_t := IDLE;
    signal divider  : integer range 0 to CLK_DIV := 0;
    signal bit_cnt  : integer range 0 to 8 := 0;
    signal sclk_reg : std_logic := '0';
    signal tx_reg   : std_logic_vector(7 downto 0) := (others => '0');
    signal rx_reg   : std_logic_vector(7 downto 0) := (others => '0');
begin
    process (clk, rst)
    begin
        if rst = '1' then
            state    <= IDLE;
            sclk_reg <= '0';
            done     <= '0';
        elsif rising_edge(clk) then
            done <= '0';

            case state is
                when IDLE =>
                    sclk_reg <= '0';
                    divider  <= 0;
                    bit_cnt  <= 0;
                    if start = '1' then
                        tx_reg <= data_in;
                        state  <= TRANSFER;
                    end if;

                when TRANSFER =>
                    if divider = CLK_DIV - 1 then
                        divider  <= 0;
                        sclk_reg <= not sclk_reg;

                        if sclk_reg = '0' then
                            -- нарастающий фронт: принимаем бит
                            rx_reg <= rx_reg(6 downto 0) & miso;
                        else
                            -- спад: выдвигаем следующий бит
                            tx_reg <= tx_reg(6 downto 0) & '0';
                            if bit_cnt = 7 then
                                state <= IDLE;
                                done  <= '1';
                            else
                                bit_cnt <= bit_cnt + 1;
                            end if;
                        end if;
                    else
                        divider <= divider + 1;
                    end if;
            end case;
        end if;
    end process;

    sclk     <= sclk_reg;
    mosi     <= tx_reg(7);                              -- старшим битом вперёд
    cs_n     <= '1' when state = IDLE else '0';
    data_out <= rx_reg;
end architecture rtl;

Пояснения

  • SPI полнодуплексный: за те же восемь тактов байт уходит и байт приходит — приёмный и передающий регистры работают одновременно.
  • Приём и передача разнесены по разным фронтам такта: так данные успевают устояться до момента защёлкивания.
  • Сочетание полярности и фазы такта образует четыре режима SPI; здесь реализован самый распространённый.

Что дальше

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