LIFE LOG(ここにはあなたのブログ名)

作業日誌 う靴

気ままにコンピュータ関連の備忘録などを書きます...。

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(スレーブ)側からデータが返ってきた。