Ich arbeite mit einem MCP23S17 SPI I/O-Expander-Chip in einem VHDL-Projekt auf meinem Basys 2 .
Auf den ersten Blick dachte ich, dies sei nur eine einfache SPI-Schnittstelle, bei der ich die Chipauswahl auf niedrig stelle und sie mir die Daten auf der MISO-Leitung liefert, aber es sieht so aus, als wäre sie mit den erforderlichen Befehlen und Initialisierungen etwas komplizierter.
Ich habe einige Setup-Bits ("0100" & "000" & "1") hinzugefügt, die einmal auf der MOSI-Leitung erscheinen, wenn Sie versuchen, Daten zu lesen. aber es hat sich nichts geändert. Es scheint viele Register zu geben, um Einstellungen zu speichern, aber ich habe keine Ahnung, wie ich diese einstellen soll.
Hier ist ein Diagramm, wie ich alles angeschlossen habe. Die Test-E/A dient nur dazu, sicherzustellen, dass ich einige bekannte Bits habe, die angezeigt werden sollten, wenn die Transaktion erfolgreich ist. Ich werde die B-Seite des Chips verwenden. Wenn also etwas Besonderes passieren muss, um das zu lesen, dann erklären Sie es bitte.
Was muss passieren, um Daten vom Chip zu lesen?
Hier ist das SPI-Modul (SPI.vhd), das ich bisher geschrieben habe.
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;
Ich habe nicht viele Artikel gefunden, die über diesen Chip mit Code sprechen. Ich habe einige Arduino-Sachen gefunden, aber sie verwenden alle die SPI-Bibliothek, die nicht hilft zu erklären, was genau passiert. Hier sind die wenigen Links, die ich gefunden habe:
In Ordnung, nachdem ich an dem gearbeitet habe, was Dave Tweed gesagt hat. Ich kann die Befehle auf MOSI senden und produzieren, aber auf der MISO-Leitung kommt nichts zurück. Denken Sie daran, dass das FPGA die Daten erhalten sollte und ich einen Logikanalysator habe, der die Bits anzeigt, wenn etwas herauskommt und mein FPGA-Code falsch ist.
CS: 1111000000000000000000000000
MOSI: xxxx0100aaa10000110000000000
MISO: xxxxxxxxxxxxxxxxxxxxxxxxxxxx
Hier ist die Simulation in ISim. **Dies gibt keine Daten auf MISO zurück, da es sich nur um eine Simulation ohne Chip handelt, um tatsächlich die richtigen Daten zurückzusenden.*
Und von einem Logikanalysator in der realen Welt:
Hier ist der Update-SPI.vhd-Modulcode:
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;
Der MCP23S17 ist eigentlich für den Anschluss an einen Mikrocontroller gedacht. Ich habe es erfolgreich in einem Blackfin-basierten Projekt eingesetzt. Es verfügt über eine Reihe interner Register, genau wie die GPIO-Ports eines typischen Mikrocontrollers. Jeder 8-Bit-Port hat ein Richtungsregister, ein Eingangsregister und ein Ausgangsregister sowie Register für Eingangspolarität und Interrupt-on-Change. Es gibt auch ein globales Konfigurationsregister.
Beim Einschalten werden standardmäßig alle Eingänge verwendet. Wenn das alles ist, was Sie brauchen, müssen Sie nur eine Zustandsmaschine erstellen, die die beiden Eingangsregister liest. Beachten Sie, dass Sie für jeden Lesezyklus sowohl ein Chip-Adress-Byte als auch ein Register-Adress-Byte bereitstellen müssen.
Außerdem müssen Sie sich darüber im Klaren sein, dass dieser Chip die funky Eigenschaft hat, zwei verschiedene Adresszuordnungen für die Register zu haben, abhängig von der Einstellung des "BANK"-Bits. Studieren Sie diesen Teil sorgfältig; es ist ziemlich verwirrend.
Das BANK-Bit ist beim Einschalten Null, sodass die beiden gewünschten Register GPIOA und GPIOB an den Adressen 12 bzw. 13 zu finden sind. Um beide zu lesen, müssen Sie daher zwei 24-Takt-SPI-Zyklen durchführen:
CS: 1111000000000000000000000000111111110000000000000000000000001111
MOSI: xxxx0100aaa10000110000000000xxxxxxxx0100aaa10000110100000000xxxx
MISO: xxxx0000000000000000AAAAAAAAxxxxxxxx0000000000000000BBBBBBBBxxxx
Beachten Sie, dass alles MSB-first ist.
Haben Sie Ihr VHDL simuliert, um zu überprüfen, ob es das tut, was Sie erwarten?
Ihr FPGA sollte als SPI-Master fungieren, erzeugt jedoch kein SPI-CLK-Signal. Der VHDL-Prozess wird auch von demselben SPI CLK-Signal (sck) getaktet, und da dieses Signal keinen Takt hat, tut Ihr Prozess nichts.
MLM
MLM
David Tweed
MLM