VHDLでSPI / ADコンバーターMAX1118 / CH1も読めるようにする

テキストはCH0だけを読んでいるがせっかくなのでCH1も読めるようにする。

!スレーブセレクトするとすぐにCH0の変換が始まるが、その変換の最中に!スレーブセレクトを解除してすぐにもう一度!スレーブセレクトするとCH1の変換が始まる。

library ieee;
use ieee.std_logic_1164.all;

entity SPI_interface_for_MAX1118_sel_ch is
    port(
        clk, rst: in std_logic;
        rd      : in std_logic;
        MISO    : in std_logic;
        ch      : in std_logic;

        SSn, SCLK    : out std_logic;
        received_data: out std_logic_vector(7 downto 0)
    );
end entity;

architecture rtl of SPI_interface_for_MAX1118_sel_ch is
    signal clk_5MHZ: std_logic;
    signal t, tmax : natural range 0 to 37;
    
    type state_type is (idle, pre_conv_a, pre_conv_b, convert, read_data, hold);
    signal pr_state, nx_state: state_type;
    
begin

   -- ソースクロック(48 MHz)を10分周してSPIクロック(約5 MHz)を生成する。
    process(clk)
        variable count: natural range 0 to 4;
    begin
       -- 5クロックサイクルごとにトグルする。
        if rising_edge(clk) then
            if   count = 4 then clk_5MHZ <= not clk_5MHZ; count := 0;
            else                                          count := count + 1;
            end if;
        end if;
    end process;

   -- ステートを保持するためのレジスタ
    process(clk_5MHZ, rst)
    begin
        if    rst                   then pr_state <= idle;
        elsif rising_edge(clk_5MHZ) then pr_state <= nx_state;
        end if;
    end process;
    
   -- ステートを遷移させるための論理回路
    process(all)
    begin
        case pr_state is
            when idle =>
                if rd then
                    if     ch = '0' then nx_state <= convert;
                    elsif  ch = '1' then nx_state <= pre_conv_a; -- CH1の場合はconvertステートへ飛ぶ前に、
                    else                 nx_state <= idle;
                    end if;
                end if;

            when pre_conv_a => nx_state <= pre_conv_b; -- SSnを一旦Lo、
            when pre_conv_b => nx_state <= convert;    -- Hiにしてからconvertステートへ飛ぶ。

            when convert =>
                if t = tmax then nx_state <= read_data;
                else             nx_state <= convert;
                end if;
            when read_data =>
                if t = tmax then nx_state <= hold;
                else             nx_state <= read_data;
                end if;
            when hold =>
                if   not rd then nx_state <= idle;
                else             nx_state <= hold;
                end if;
        end case;
    end process;

   -- ステートマシン出力を決めるための論理回路
   -- テキストと違うがロジアナでうまくデコードできないためSSnをレジスタに通して半拍遅らせた。
    process(all)
        variable SSn_tmp: std_logic;
    begin
        case pr_state is
            when idle       => SSn_tmp:= '1'; SCLK <= '1';

            when pre_conv_a => SSn_tmp:= '0'; SCLK <= '1';      -- 'CH1'のときは一旦スレーブセレクトして、
            when pre_conv_b => SSn_tmp:= '1'; SCLK <= '1';      -- 一旦すぐに解除して、

            when convert    => SSn_tmp:= '0'; SCLK <= '1';      -- スレーブセレクト
            when read_data  => SSn_tmp:= '0'; SCLK <= clk_5MHZ;
            when hold       => SSn_tmp:= '1'; SCLK <= '1';      -- スレーブセレクトを解除
        end case;
        
        if falling_edge(clk_5MHz) then
                SSn <= SSn_tmp;
        end if;
    end process;

   -- SPIクロックに同期してビット7からビット0まで順番に読み出す。
    process(clk_5MHZ)
    begin
        if rising_edge(clk_5MHZ) then
            if pr_state = read_data then
                received_data(7 - t) <= MISO;
            end if;
        end if;
    end process;

   -- タイマー
    process(all)
    begin
        case pr_state is
            when convert   => tmax <= 37; -- 変換中タイマーのトップ値(約200 ns * 38 = 7.5 us)
            when read_data => tmax <= 7;  -- 読み出し中タイマーのトップ値
            when others    => tmax <= 0;
        end case;

        if rising_edge(clk_5MHZ) then
            if    pr_state /= nx_state then t <= 0;
            elsif        t /= tmax     then t <= t + 1;
            end if;
        end if;
    end process;

end architecture;

CH1を1回読んだようす。!スレーブセレクトが2回連続して繰り返されている。
f:id:ti-nspire:20210514131226p:plain
f:id:ti-nspire:20210514132033p:plain:w500