Estoy trabajando con un chip expansor de E/S SPI MCP23S17 en un proyecto VHDL en mi Basys 2 .
A primera vista, pensé que esto era solo una interfaz SPI simple donde puse la selección de chip baja y me dará los datos en la línea MISO, pero parece que es un poco más complicado con los comandos y la inicialización necesaria.
Agregué algunos bits de configuración ("0100" y "000" y "1") que aparecen en la línea MOSI una vez cuando intenta leer datos. pero nada ha cambiado. Parece que hay muchos registros para mantener la configuración, pero no tengo ni idea de cómo configurarlos.
Aquí hay un diagrama de cómo lo tengo todo conectado. La E/S de prueba es solo para asegurarme de que tengo algunos bits conocidos que deberían aparecer si la transacción se realiza correctamente. Usaré el lado B del chip, así que si algo especial necesita suceder para leer eso, explíquelo.
¿Qué debe suceder 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 hablando de 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:
Muy bien, después de trabajar en lo que dijo Dave Tweed. Puedo enviar y producir los comandos en MOSI, pero no vuelve nada en la línea MISO. Tenga en cuenta que el FPGA debe obtener los datos y tengo un analizador lógico que mostrará los bits si sale algo y mi código FPGA está mal.
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 datos correctos.*
Y de un analizador lógico en el mundo real:
Aquí está el código de actualización 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;
El MCP23S17 realmente está diseñado para conectarse a un microcontrolador. Lo he usado con éxito en un proyecto basado en Blackfin. Tiene varios 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 cambio de interrupción. También hay un registro de configuración global.
De forma predeterminada, todas las entradas se activan, por lo que 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 peculiar característica de tener dos mapas de direcciones diferentes para los registros, según la configuración del bit "BANCO". Estudie 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 a ambos, debe realizar dos ciclos SPI de 24 horas:
CS: 1111000000000000000000000000111111110000000000000000000000001111
MOSI: xxxx0100aaa10000110000000000xxxxxxxx0100aaa10000110100000000xxxx
MISO: xxxx0000000000000000AAAAAAAAxxxxxxxx0000000000000000BBBBBBBBxxxx
Tenga en cuenta que todo es MSB primero.
¿Ha simulado su VHDL para verificar que está haciendo lo que espera?
Su FPGA debería actuar como el maestro SPI pero no genera la señal SPI CLK. El proceso VHDL también está cronometrado desde la misma señal SPI CLK (sck) y debido a que no hay reloj en esta señal, su proceso no hace nada.
multinivel
multinivel
david tweed
multinivel