Interfaçage d’un MCP23S17 (SPI) avec un FPGA

MLM

Interfaçage d’un MCP23S17 (SPI) avec un FPGA


Je travaille avec une puce d’extension d’E / S SPI MCP23S17 dans un projet VHDL sur mon Basys 2 .

À première vue, je pensais que ce n’était qu’une simple interface SPI où je mettais la sélection de puce à un niveau bas et cela me donnerait les données sur la ligne MISO, mais il semble que c’est un peu plus compliqué avec les commandes et l’initialisation nécessaires.

J’ai ajouté quelques bits de configuration (« 0100 » & « 000 » & « 1 ») qui apparaissent une fois sur la ligne MOSI lorsque vous essayez de lire des données. mais rien n’a changé. Il semble y avoir beaucoup de registres pour conserver les paramètres, mais je n’ai aucune idée de la façon de les définir.

Format d'octet de contrôle SPI

Voici un schéma de la façon dont j’ai tout branché. Les E / S de test visent uniquement à garantir que j’ai des bits connus qui devraient apparaître si la transaction réussit. J’utiliserai le côté B de la puce, donc si quelque chose de spécial doit se produire pour le lire, veuillez expliquer. Configuration de la puce

Que doit-il se passer pour lire les données de la puce?

Voici le module SPI (SPI.vhd) que j’ai écrit jusqu’à présent.

 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 ; 

Je n’ai pas trouvé beaucoup d’articles parlant de cette puce en utilisant du code. J’ai trouvé des trucs Arduino mais ils utilisent tous la bibliothèque SPI qui n’aide pas à expliquer ce qui se passe exactement. Voici les quelques liens que j’ai trouvés:

Éditer:

Très bien après avoir travaillé sur ce que Dave Tweed a dit de faire. Je peux envoyer et produire les commandes sur MOSI mais rien ne revient sur la ligne MISO. Gardez à l’esprit que le FPGA devrait obtenir les données et j’ai un analyseur logique qui montrera les bits si quelque chose sort et que mon code FPGA est incorrect.

 CS :     1111000000000000000000000000 MOSI :  xxxx0100aaa10000110000000000 MISO :  xxxxxxxxxxxxxxxxxxxxxxxxxxxx 

Voici la simulation dans ISim. ** Cela ne renverra pas de données sur MISO car il s’agit simplement d’une simulation sans puce pour renvoyer les données appropriées. * Résultats ISim

Et à partir d’un analyseur logique dans le monde réel: Résultats de l'analyseur logique

Voici le code du module de mise à jour 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 ; 

Réponses


 Dave Tweed

Le MCP23S17 est vraiment destiné à être connecté à un microcontrôleur. Je l’ai utilisé avec succès dans un projet basé sur Blackfin. Il a un certain nombre de registres internes, tout comme les ports GPIO d’un microcontrôleur typique. Chaque port 8 bits possède un registre de direction, un registre d’entrée et un registre de sortie, ainsi que des registres de polarité d’entrée et d’interruption sur changement. Il existe également un registre de configuration global.

Il utilise par défaut toutes les entrées à la mise sous tension, donc si c’est tout ce dont vous avez besoin, il vous suffit de créer une machine d’état qui lit les deux registres d’entrée. Notez que vous devez fournir à la fois un octet d’adresse de puce et ensuite un octet d’adresse de registre pour chaque cycle de lecture.

En outre, vous devez être conscient que cette puce a la fonction géniale d’avoir deux mappages d’adresses différents pour les registres, en fonction du réglage du bit « BANK ». Étudiez soigneusement cette partie; c’est assez déroutant.

Le bit BANK est nul à la mise sous tension, donc les deux registres que vous voulez, GPIOA et GPIOB se trouvent aux adresses 12 et 13, respectivement. Par conséquent, pour les lire tous les deux, vous devez effectuer deux cycles SPI sur 24 heures:

 CS :     1111000000000000000000000000111111110000000000000000000000001111 MOSI :  xxxx0100aaa10000110000000000xxxxxxxx0100aaa10000110100000000xxxx MISO :  xxxx0000000000000000AAAAAAAAxxxxxxxx0000000000000000BBBBBBBBxxxx 
  • « aaa » représente l’adresse de la puce.
  • « AAAAAAAA » représente les données du port A
  • « BBBBBBBB » représente les données du port B

Notez que tout est MSB en premier.

MLM

Pourriez-vous me donner un exemple d’essayer de lire les côtés A et B (ce que je devrais pomper MOSI)? Dois-je effectuer cette commande chaque fois que je veux les données?

MLM

J’ai mis à jour mon code afin de pouvoir diriger ces commandes correctement (fonctionne en simulation et en monde réel – analyseur logique). Vous pouvez voir mes résultats dans l’op. Bien que j’envoie ces commandes sur MOSI, rien ne revient sur MISO.

Dave Tweed ♦

Vous utilisez l’adresse de la puce aaa = 000. Êtes-vous sûr que les broches correspondantes (A2, A1, A0) du 23S17 sont mises à la terre? De plus, il semble que les données MOSI changent sur le front montant de l’horloge. Vous devez modifier les données sur le front descendant, car les temps de configuration et de maintien du 23S17 sont relatifs au front montant.

MLM

Oui, toutes les broches A sont mises à la terre. Aucun changement lors du passage au front descendant: i.imgur.com/ua54TsF.png


 Amoch

Avez-vous simulé votre VHDL pour vérifier qu’il fait ce que vous attendez de lui?

Votre FPGA doit agir en tant que maître SPI mais il ne génère pas le signal SPI CLK. Le processus VHDL est également cadencé à partir du même signal SPI CLK (sck) et comme il n’y a pas d’horloge sur ce signal, votre processus ne fait rien.

MLM

L’horloge est générée sur le module de niveau supérieur puis introduite dans le module SPI ainsi que la ligne spi_sck allant à la puce. La simulation semble correcte. SW0 devient bas et les bits de configuration sortent. i.imgur.com/irxxmrg.png

Amoch

Cette image de simulation ne prouve pas que votre maître SPI sur le FPGA fonctionne. En fait, il ne montre même pas qu’il décale correctement 8 bits, après 4 bits, mosi passe en «U». À mes yeux, votre VHDL semble logiquement incorrect. Je pense que vous avez besoin d’un banc d’essai plus complet pour prouver que votre maître SPI fonctionne.

MLM

Je vois, il semble déplacer les 4 premiers corrects et le débogage montre qu’il est configuré après 8 horloges mais il semble en effet manquer 4 bits. Vous pouvez voir les setupBits dans le code ci-dessus: variable setupBits: std_logic_vector (7 downto 0): = « 0100 » & address & « 1 »;

Dan Mills

Remarque supplémentaire, il existe un errata pour cette partie auquel vous voudrez peut-être prêter attention, l’IIRC a un impact sur l’adresse que vous devez utiliser.

 

avec, d’un, FPGA, Interfaçage, MCP23S17, SPI, un

 

google

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *