このページでは,exLeafを使ってSPIモジュールにアクセスするためのサンプルイメージと使用方法を紹介します.

このサンプルには一つのSPIマスタが含まれていて,J2の0番ポートがCS,J2の2番ポートがMOSI,J2の3番ポートがMISO,J2の4番ポートがSCLKに割り当てられています.MISOはFPGAのI/O機能によりプルアップされています.

それ以外のポートについては,J3はディジタル出力,J4とJ5はディジタル入力ポートとして働きます.全て3.3VのI/Oポートです.

32bitまでのSPIレジスタの送受信に対応します.

紹介するサンプルは,exleaf_spi_20170128.jedをダウンロードしてexLeafのフラッシュに書き込むことで利用できます.

サンプルはexLeafを購入した方は自由にご利用いただけます.ただし,イーツリーズ・ジャパンは,サンプルの利用による損害に対する責任を負うことはできませんのでご注意ください.exLeafの詳細はこちらをご覧ください.

ネットワークパラメタ

  • IPアドレス 10.0.0.133 (後述の方法によって動作時に変更可能)
  • ネットマスク 255.0.0.0 (後述の方法によって動作時に変更可能)
  • デフォルトゲートウェイ 10.0.0.1 (後述の方法によって動作時に変更可能)
  • 待ち受けポート 16384番
  • MACアドレス 00-1B-1A-EE-00-D5

操作方法

J2の2,3番ポートおよびJ3に出力する値に続き,I2CアクセスのためのパラメタをデータとするUDPパケットを送ることで各ポートの出力値の設定およびI2Cにアクセスすることができます.パケットフォーマットは次の通りです.

バイト ビット 説明
0 [3:0] J3に出力する値
1〜4 SPIに出力するデータ 1バイト目〜4バイト目.1バイト目のMSBから順に出力される.
5 CSをアサートしてSCLKを’1’にするまでのサイクル数-1(1サイクル20n秒.0で1サイクル=20n秒)
6 SCLKを’1’にするサイクル数-1(1サイクル20n秒.0で1サイクル=20n秒)
7 SCLKを’0’にするサイクル数-1(1サイクル20n秒.0で1サイクル=20n秒)
8 最後のビットをハンドリングした後にCSをディアサートするまでのサイクル数-1(1サイクル20n秒.0で1サイクル=20n秒)
9 [4:0] SCLK操作を有効にするビット長-1(0のとき1bit分のSCLKs操作を行う)
9 8
0
SPI操作実行フラグ
CSを正論理で動作させるフラグ.0ならCSは負論理,1ならCSを正論理で制御する.

たとえばMaximの温度センサMAX31723のステータス(アドレス0x00の値)を読み出す場合には,↓のようなRubyスクリプトでアクセスできます.

#!/usr/bin/ruby
require "socket"
udp = UDPSocket.open()
sockaddr = Socket.pack_sockaddr_in(16384, "10.0.0.133")
d = [0x00, 0x00, # gpio output
     0x00, 0x00, 0x00, 0x00, # マスタ側の32bitレジスタ
     0x10, # CSをアサートした後の待ち時間
     0x10, # SCLKを'1'にする期間
     0x10, # SCLKを'0'にする期間
     0x10, # 全ビット送信後にCSをディアサートするまでの時間
     0x0F, # 送信するビット長(16bit)
     0x81, # SPIを実行,CSを正論理で制御
    ]
udp.send(d.pack('C*'), 0, sockaddr)
recv = udp.recv(2048);
dd = recv.unpack('C*')
puts(format("GPIO: %x %x %x %x", (dd[0] >> 4) & 0x0F, dd[0] & 0x0F, (dd[1] >> 4) & 0x0F, dd[1] & 0x0F))
puts(format("SPI Q:%02x %02x %02x %02x", dd[2], dd[3], dd[4], dd[5]))
udp.close

読み出したデータは,J2〜J5の値の後4Byteに出力されます.

同様にステータスを変更する場合には,↓のようなRubyスクリプトでアクセスできます.

#!/usr/bin/ruby
require "socket"
udp = UDPSocket.open()
sockaddr = Socket.pack_sockaddr_in(16384, "10.0.0.133")
d = [0x00, 0x00, # gpio output
     0x80, 0x16, 0x00, 0x00, # マスタ側の32bitレジスタ.ステータス変更用コマンドに続いてデータを付与する
     0x10, # CSをアサートした後の待ち時間
     0x10, # SCLKを'1'にする期間
     0x10, # SCLKを'0'にする期間
     0x10, # 全ビット送信後にCSをディアサートするまでの時間
     0x0F, # 送信するビット長(16bit)
     0x81, # SPIを実行,CSを正論理で制御
    ]
udp.send(d.pack('C*'), 0, sockaddr)
recv = udp.recv(2048);
dd = recv.unpack('C*')
puts(format("GPIO: %x %x %x %x", (dd[0] >> 4) & 0x0F, dd[0] & 0x0F, (dd[1] >> 4) & 0x0F, dd[1] & 0x0F))
puts(format("SPI Q:%02x %02x %02x %02x", dd[2], dd[3], dd[4], dd[5]))
udp.close

測定された温度を読み出したければ,

#!/usr/bin/ruby
require "socket"
udp = UDPSocket.open()
sockaddr = Socket.pack_sockaddr_in(16384, "10.0.0.133")
d = [0x00, 0x00, # gpio output
     0x02, 0x00, 0x00, 0x00, # マスタ側の32bitレジスタ.温度の整数値部分の読み出し
     0x10, # CSをアサートした後の待ち時間
     0x10, # SCLKを'1'にする期間
     0x10, # SCLKを'0'にする期間
     0x10, # 全ビット送信後にCSをディアサートするまでの時間
     0x0F, # 送信するビット長(16bit)
     0x81, # SPIを実行,CSを正論理で制御
    ]
udp.send(d.pack('C*'), 0, sockaddr)
recv = udp.recv(2048);
dd = recv.unpack('C*')
puts(format("GPIO: %x %x %x %x", (dd[0] >> 4) & 0x0F, dd[0] & 0x0F, (dd[1] >> 4) & 0x0F, dd[1] & 0x0F))
puts(format("SPI Q:%02x %02x %02x %02x", dd[2], dd[3], dd[4], dd[5]))
udp.close

とします.MOSIもMISOも16サイクル分読み書きされますが,温度センサからの出力は最後の8bitですので,dd[5]に読みたいデータが格納されています.

ネットワークパラメタを変更する

IPアドレス,ネットマスク,デフォルトゲートウェイを動作中に書き換えることができます.ただし,設定値はFPGA内のレジスタに格納されるだけですので,電源再投入時には,デフォルトのパラメタに戻ります.

変更するには,IPアドレス,ネットマスク,デフォルトゲートウェイをペイロードとするUDPパケットを16385番ポートに送信してください.

たとえば,

#!/usr/bin/ruby
require "socket"
udp = UDPSocket.open()
sockaddr = Socket.pack_sockaddr_in(16385, "10.0.0.133")
d = [0xc0, 0xa8, 0x00, 0x02, 0xff, 0xff, 0xff, 0x00, 0xc0, 0xa8, 0x00, 0x01]
msg = d.pack('C*')
udp.send(msg, 0, sockaddr)
udp.close

というパケットを送信すれば,ノードのIPアドレス,ネットマスク,デフォルトゲートウェイを192.168.0.2, 255.255.255.0,192.168.0.1にセットすることができます.