Interfaz de un MCP23S17 (SPI) con un FPGA

1

Estoy trabajando con un chip de expansión de E / S SPI MCP23S17 en un proyecto VHDL en mi Basys 2 .

A primera vista, pensé que esto era solo una simple interfaz SPI en la que ponía baja la selección de chip y me proporcionaría los datos en la línea MISO, pero parece que es un poco más complicado con los comandos y la inicialización necesarios.

Agregué algunos bits de configuración ("0100" & "000" & "1") que aparecen en la línea MOSI una vez cuando intentas leer los datos. pero nada ha cambiado. Parece que hay muchos registros para mantener la configuración, pero no tengo idea de cómo configurarlos.

Aquíhayundiagramadecómolotengotodoconectado.LapruebadeE/Sessoloparaasegurarsedequetengoalgunosbitsconocidosquedeberíanaparecersilatransacciónserealizacorrectamente.UsaréelladoBdelchip,asíquesitienequepasaralgoespecialparaleerlo,porfavorexplique.

¿Qué debe pasar para leer los datos del chip?

Aquí está el módulo SPI (SPI.vhd) que he escrito hasta ahora.

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

-- Uncomment the following library declaration if using
-- arithmetic functions with Signed or Unsigned values
--use IEEE.NUMERIC_STD.ALL;

-- Uncomment the following library declaration if instantiating
-- any Xilinx primitives in this code.
--library UNISIM;
--use UNISIM.VComponents.all;

entity SPI is
    Generic (   
        dataWidthN : positive := 8
    );
    port(
        sck: in std_logic; -- clock
        mosi: out std_logic; -- data going into slave
        miso: in std_logic; -- data coming out of slave
        cs: in std_logic; -- chip select

        address: in std_logic_vector(2 downto 0); -- 0 - 7

        data: out std_logic_vector(dataWidthN-1 downto 0);

        debug: out std_logic_vector(1 downto 0)
    );
end SPI;

architecture Behavioral of SPI is
    signal data_reg : STD_LOGIC_VECTOR (dataWidthN-1 downto 0);

begin

    data <= data_reg;

    process (sck)
        variable isSetup: std_logic := '0';
        variable setupBits: std_logic_vector(7 downto 0) := "0100" & address & "1";
        variable setupBitCount: natural := 0;
    begin
        if rising_edge(sck) then  -- rising edge of SCK
            if (cs = '0') then -- SPI CS must be selected

                if (isSetup = '0' and setupBitCount < 7) then
                    mosi <= setupBits(7-setupBitCount);
                    setupBitCount := setupBitCount + 1;
                else
                    isSetup := '1';
                    setupBitCount := 0;
                end if;

                if isSetup = '1' then
                    debug <= "11";

                    -- shift serial data into dat_reg on each rising edge
                    -- of SCK, MSB first
                    data_reg <= data_reg(dataWidthN-2 downto 0) & miso;
                else
                    debug <= "10";
                end if;

            end if;
        end if;
    end process;

end Behavioral;

No he encontrado muchos artículos que hablen sobre este chip usando código. Encontré algunas cosas de Arduino pero todas usan la biblioteca SPI que no ayuda a explicar qué está sucediendo exactamente. Aquí están los pocos enlaces que he encontrado:

Editar:

Bien, después de trabajar en lo que Dave Tweed dijo que hiciera. Soy capaz de enviar y producir los comandos en MOSI pero nada vuelve en la línea MISO. Tenga en cuenta que el FPGA debería obtener los datos y tengo un analizador lógico que mostrará los bits si algo sale y mi código de FPGA es incorrecto.

CS:   1111000000000000000000000000
MOSI: xxxx0100aaa10000110000000000
MISO: xxxxxxxxxxxxxxxxxxxxxxxxxxxx

Aquí está la simulación en ISim. ** Esto no devolverá datos en MISO porque es solo una simulación sin chip para enviar los datos correctos. *

Ydesdeunanalizadorlógicoenelmundoreal:

Aquí está la actualización del código del módulo SPI.vhd:

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

entity SPI is
    Generic (   
        dataWidthN : integer := 8
    );
    port(
        sck: in std_logic; -- clock
        mosi: out std_logic; -- data going into slave
        miso: in std_logic; -- data coming out of slave
        cs: in std_logic; -- chip select

        address: in std_logic_vector(2 downto 0); -- 0 - 7

        data: out std_logic_vector(dataWidthN-1 downto 0);

        debug: out std_logic_vector(1 downto 0)
    );
end SPI;

architecture Behavioral of SPI is
    type state_type is (idle, s_readSetup, s_read);

    signal data_reg : STD_LOGIC_VECTOR (dataWidthN-1 downto 0);
begin

    data <= data_reg;

    spi_read: process (sck)
        variable transactionComplete: std_logic := '0';
        variable setupBits: std_logic_vector(15 downto 0);
        variable setupCmdBitCount: natural := 0;  -- setup command is 16 in length
        variable readCmdBitCount: natural := 0;  -- A command is same as dataWidthN

        variable currState: state_type := idle;
    begin
        setupBits := "0100" & address & "1" & "00001100";

        if falling_edge(sck) then  -- rising edge of SCK

            case currState is
            when s_readSetup =>
                if (cs = '0') then -- SPI CS must be selected
                    debug <= "10";

                    mosi <= setupBits(setupBits'length-1-setupCmdBitCount);

                    setupCmdBitCount := setupCmdBitCount + 1;

                    -- Move to the next state
                    if setupCmdBitCount >= setupBits'length then
                        setupCmdBitCount := 0;
                        currState := s_read;
                    end if;

                else
                    currState := idle;
                end if;

            when s_read =>
                if (cs = '0') then -- SPI CS must be selected
                    debug <= "11";

                    -- shift serial data into dat_reg on each rising edge
                    -- of SCK, MSB first
                    data_reg <= data_reg(dataWidthN-2 downto 0) & miso;

                    readCmdBitCount := readCmdBitCount + 1;

                    if readCmdBitCount >= data'length then
                        readCmdBitCount := 0;
                        transactionComplete := '1';
                        currState := idle;
                    end if;

                else
                    currState := idle;
                end if;

            -- Idle state: if the state is unknown then we just go idle
            when others =>
                debug <= "00";

                setupCmdBitCount := 0;
                readCmdBitCount := 0;
                mosi <= '0';

                if cs = '0' and transactionComplete = '0'  then
                    mosi <= setupBits(setupBits'length-1-setupCmdBitCount);
                    setupCmdBitCount := setupCmdBitCount + 1;

                    currState := s_readSetup;

                elsif cs = '1' and transactionComplete = '1' then
                    transactionComplete := '0';
                end if;

            end case;

        end if;



    end process;

end Behavioral;
    
pregunta MLM

2 respuestas

2

El MCP23S17 realmente debe estar conectado a un microcontrolador. Lo he usado con éxito en un proyecto basado en Blackfin. Tiene una serie de registros internos, al igual que los puertos GPIO en un microcontrolador típico. Cada puerto de 8 bits tiene un registro de dirección, un registro de entrada y un registro de salida, además de registros para la polaridad de entrada y la interrupción de cambio. También hay un registro de configuración global.

Se establece de forma predeterminada en todas las entradas en el encendido, por lo tanto, si eso es todo lo que necesita, solo necesita crear una máquina de estado que lea los dos registros de entrada. Tenga en cuenta que debe proporcionar un byte de dirección de chip y luego un byte de dirección de registro para cada ciclo de lectura.

Además, debe tener en cuenta que este chip tiene la característica funky de tener dos mapas de direcciones diferentes para los registros, dependiendo de la configuración del bit "BANK". Estudia esta parte cuidadosamente; es bastante confuso.

El bit BANK es cero en el encendido, por lo que los dos registros que desea, GPIOA y GPIOB se encuentran en las direcciones 12 y 13, respectivamente. Por lo tanto, para leerlos ambos, necesita hacer dos ciclos SPI de 24 horas:

CS:   1111000000000000000000000000111111110000000000000000000000001111
MOSI: xxxx0100aaa10000110000000000xxxxxxxx0100aaa10000110100000000xxxx
MISO: xxxx0000000000000000AAAAAAAAxxxxxxxx0000000000000000BBBBBBBBxxxx
  • "aaa" representa la dirección del chip.
  • "AAAAAAAA" representa los datos del puerto A
  • "BBBBBBBB" representa los datos del puerto B

Tenga en cuenta que todo es MSB primero.

    
respondido por el Dave Tweed
1

¿Has simulado tu VHDL para verificar que está haciendo lo que esperas?

Su FPGA debería estar actuando como el maestro SPI pero no genera la señal SPI CLK. El proceso VHDL también se sincroniza desde la misma señal SPI CLK (sck) y como no hay reloj en esta señal, su proceso no hace nada.

    
respondido por el Amoch

Lea otras preguntas en las etiquetas