FPGA + Arduino (FPGArduino)でSPI通信
1. はじめに
FPGA + Arduino でSPI通信をやってみました。結構つまったところもあるので、備忘録として残しておきます。
まず、使用したFPGAは3.3V駆動で、使用したArduino Nano(Atmega328P搭載)が5V駆動なので、両者のあいだに双方向レベル変換モジュールを噛ました。1素子で4線分の変換をまかなえるやつだが、すべて同じ素子上に変換配線を設けるとお互いに信号が干渉し合う可能性があるかもしれない?というような記事をネットでみかけたので、安全策をとって2素子用いた。
そしてブレッドボード上で配線し回路を作ったのだけど、ジャンパワイヤの信号線では少し長すぎるようでノイズを多く拾ってしまうかもしれないので、市販のアルミホイルを巻いて簡易的に導線をシールドした。見た目が不恰好になった.....。要不要はわからない....。効果のほどもわからない。おそらく要らないだろう。
Arduinoの発振器が16MHzで、これをスケッチでは1/8に分周し、つまり2MHzの周波数で最終的には通信することにした。
※Arduino のGNDとFPGAのGNDを共通にすることを忘れない。
2. VHDLコード(slave側)
library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; entity spi0529 is port ( CLK : in std_logic ; -- 50MHz -- SPI SCK : in std_logic ; nSS : in std_logic ; MOSI : in std_logic ; MISO : out std_logic ; -- register select SEL : in std_logic ; LED1 : buffer std_logic ); end spi0529 ; architecture RTL of spi0529 is -- BUS interface signal iSS : std_logic ; signal iMOSI : std_logic ; -- synchronizer signal iSS_SFT : std_logic_vector(2 downto 0) ; signal iSTRG : std_logic ; -- internal shift register signal iMOSI_SFT : std_logic_vector(7 downto 0) ; signal CNT : integer range 0 to 2499; signal TM : std_logic; signal nRESET : std_logic; begin -- input iSS <= not nSS ; iMOSI <= MOSI ; nRESET <= '1'; -- output MISO <= iMOSI_SFT(7) ; --クロック生成(2MHz), 50MHz / 2MHz = 25 process(CLK) begin if( CLK'event and CLK = '1' ) then if( CNT < 25 ) then TM <= '1'; CNT <= 0; else CNT <= CNT + 1 ; TM <= '0'; end if; end if; end process; -- synchronizer process (nRESET,TM) begin if ( nRESET = '0' ) then iSS_SFT <= "000" ; elsif rising_edge(TM) then iSS_SFT <= iSS_SFT(1 downto 0) & iSS ; end if ; end process ; -- internal register process (nRESET,SCK) begin if ( nRESET = '0' ) then iMOSI_SFT <= X"00" ; elsif rising_edge(SCK) then if ( iSS = '1' ) then iMOSI_SFT <= iMOSI_SFT(6 downto 0) & iMOSI ; if(iMOSI = '1') then LED1 <= '1'; else LED1 <= '0'; end if; end if ; end if ; end process ; end RTL;
3. ピンアサイン
4. Arduinoコード(master側)
/* * SPIマスタ * SS - Pin10 * MOSI - Pin11 * MISO - Pin12 * SCK - Pin13 */ #include <SPI.h> #define SSPin 10 void setup() { Serial.begin (9600); Serial.println("Master"); pinMode(SS,OUTPUT); //SSピンを出力設定 SPI.setBitOrder(MSBFIRST); SPI.setClockDivider(SPI_CLOCK_DIV8); // 16MHz/8 = 2MHz SPI.setDataMode(SPI_MODE2); SPI.begin(); } int t = 1; void loop() { char snd; char rcv; //UARTから読み込み snd = Serial.read(); //データがあれば送信 if(snd != -1){ SetSSPin(LOW); delayMicroseconds(t); SPI.transfer(snd); delayMicroseconds(t); SetSSPin(HIGH); delay(100); SetSSPin(LOW); delayMicroseconds(t); rcv = SPI.transfer(0); delayMicroseconds(t); SetSSPin(HIGH); //データ確認 Serial.print(rcv); Serial.print(" : "); Serial.print(rcv,DEC); Serial.print(" : "); Serial.println(rcv,BIN); } } void SetSSPin(int val) { digitalWrite(SSPin, val); }
5. 実験結果
Arduino(マスタ)側からシリアルモニタで連続した文字列"Hello World!"を送信すると、ちゃんとFPGA(スレーブ)側からデータが返ってきた。