概要
FPGAとラズピコをSPI通信で連携して、ラズピコからFPGAのレジスタ設定を書き換えます。
今回はピコのSPIマスターの実装方法について紹介します。
通信フォーマット
SPI通信はタイミングについては規定があるものの、データの内訳についてはデバイス依存になります。
つまり、FPGAの場合はデータの内訳は自由ということになりますので、以下の通りの通信フォーマットを採用するものとします。
- CSはLow有意
- クロックエッジは立ち上がりでサンプリング
- クロック周波数は1MHz
- データは32bit(アドレス8bit + コマンド4bit + ダミー54bit + データ16bit)
- データはMSBファースト
コード
main.py
from machine import Pin
from machine import SPI
import sys
import select
#================================================================
# SPI親分クラス
#================================================================
class SPI_master():
def __init__(self):
# SPI設定 (SPI0: CLK=GP2, MOSI=GP3, MISO=GP4)
self.spi = SPI(0, baudrate=1000000, polarity=0, phase=0, sck=Pin(2), mosi=Pin(3), miso=Pin(4))
# SPI Chip Selectピン設定
self.cs = Pin(5, Pin.OUT)
self.cs.value(1) # 初期状態はHigh(非アクティブ)にしておく
#-----------------------------------------------------------
# SPI書込み
#-----------------------------------------------------------
def Write(self,adr: int, data: int):
cmd=0x60 # 書込みコマンド
__txData = bytearray([adr & 0xFF, cmd, (data >> 8) & 0xFF, data & 0xFF]) # 4バイトにパッキング
self.cs.value(0) # CSをLowにアサート
self.spi.write(__txData)# パッキングしたデータを送信
self.cs.value(1) # CSをHighにネゲート
print("Write data:", [hex(b) for b in __txData]) # バイト単位で表示
#-----------------------------------------------------------
# SPI読み出
#-----------------------------------------------------------
def Read(self,adr: int):
cmd=0x90 # 読み出しコマンド
__txData = bytearray([adr & 0xFF, cmd, 0, 0])
__rxData = bytearray(len(__txData))
self.cs.value(0)
#self.spi.write(__packet)
#__packet = self.spi.read(4)
self.spi.write_readinto(__txData, __rxData)
self.cs.value(1)
print("Send data:", [hex(b) for b in __txData]) # バイト単位で表示
print("Received data:", [hex(b) for b in __rxData]) # バイト単位で表示
return __rxData
#===========================================================
# 非ブロッキングでキー入力をチェックする関数
def check_key_input():
if select.select([sys.stdin], [], [], 0)[0]:
return sys.stdin.read(1)
return None
#=====================================================================
#
#=====================================================================
spi_master=SPI_master()
while True:
#キー入力を受け取る
user_input = check_key_input()
#キー入力に応じてSPIライト
if user_input == 'a':
spi_master.Write(0x55, 0xFFFF)
if user_input == 'b':
spi_master.Write(0x55, 0x1234)
if user_input == 'c':
spi_master.Write(0x55, 0xEDCB)
if user_input == 'z':
spi_master.Write(0x55, 0x0000)
if user_input == 'r':
r=spi_master.Read(0x55)
余談:SPIのインターバル
データ送信する際にバイトデータをパッキングしないで送ることも可能ですが、インターバル時間が長いのでパックした方が良さげです。
def SpiWrite(self,adr: int, data: int):
cmd=0x60 # 書込みコマンド
self.cs.value(0) # CSをLowにアサート
self.spi.write(bytearray([adr & 0xFF])) # adrを1バイトに変換して送信
self.spi.write(bytearray([cmd])) # cmdを1バイトに変換して送信
self.spi.write(bytearray([(data >> 8) & 0xFF])) # dataの上位Byte送信
self.spi.write(bytearray([data & 0xFF])) # dataの下位Byte送信
self.cs.value(1) # CSをHighにネゲート
おわりに
FPGA側のSPIスレーブについては別記事にして投稿したいと思います。