exLeafでI2Cアクセス

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

このサンプルには一つのI2Cマスタが含まれていて,J2の0番ポートがSCL,J2の1番ポートがSDAに割り当てられています.どちらもFPGAのI/O機能によりプルアップされています.

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

7bitアドレスのI2Cデバイスの読み書きに対応していて,4バイトまでのバースト読み書きができます.

#

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

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

ネットワークパラメタ

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

操作方法

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

バイト ビット 説明
0 [7:6]
[3:0]
J2の2,3番ポートの出力値
J3に出力する値
1 予約
2 I2Cアクセス時のレジスタアドレス[15:8]
3 I2Cアクセス時のレジスタアドレス[7:0]
4〜7 I2Cに出力するデータ 1バイト目〜4バイト目
8 [7]
[5]
[4]
[3]
[2]
[1:0]
I2Cアクセスを実行するフラグ('1'をセットする)
リード時にストップコンディションを発行するかどうかのフラグ('1'で発行)
レジスタアドレスが16bitの時に立てるフラグ('1'のとき16bitが有効.'0'なら下位8bitだけが使われる)
レジスタアドレスを指定しないかどうかのフラグ('1'のときレジスタアドレスを用いない)
R/Wフラグ('1'のとき書き込み,'0'のとき読み出し)
データ長さ(0のとき1バイト読み書き)
9 [6:0] アクセスするI2Cデバイスのスレーブのアドレス(RWフラグを含まない7bitアドレス)


たとえばアナログデバイス社の温度センサADT7410のID(アドレス0x0Bの値)を読み出す場合には,↓のようなRubyスクリプトでアクセスできます.

#!/usr/bin/ruby
require "socket"
udp = UDPSocket.open()
sockaddr = Socket.pack_sockaddr_in(16384, "10.0.0.132")
d = [0x00, 0x00, # gpio output
     0x00, 0x0B, # register address
     0x00, 0x00, 0x00, 0x00, # i2c data_in
     0x80 | 0x00 | 0x00, # req, command, len=1
     0x48, # i2c slave addr
    ]
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("I2C Q:%02x %02x %02x %02x", dd[2], dd[3], dd[4], dd[5]))
udp.close

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


使用事例(1) : 温度センサの出力の読み出し

たとえば,次のようなRubyスクリプトでアナログデバイス社の温度センサADT7410をネットワーク経由で読み出すことができます.

#!/usr/bin/ruby
require "socket"
udp = UDPSocket.open()
sockaddr = Socket.pack_sockaddr_in(16384, "10.0.0.132")
while true
  temp = 0
  d = [0x00, 0x00,
       0x00, 0x00, # レジスタ0を読む
       0x00, 0x00, 0x00, 0x00,
       0x80 | 0x00 | 0x00, # 実行フラグ, リードモード,長さ1バイト
       0x48 # スレーブアドレス(A0とA1のピンの設定によるので注意)
      ]
  udp.send(d.pack('C*'), 0, sockaddr) # exLeafにパケットを送信
  recv = udp.recv(2048); # 読み出した値を受信
  dd = recv.unpack('C*')
  temp = (dd[5] & 0x7F) << 8
  temp_sign = (dd[5] & 0x80) != 0
  d = [0x00, 0x00,
       0x00, 0x01, # レジスタ1を読む
       0x00, 0x00, 0x00, 0x00,
       0x80 | 0x00 | 0x00, # 実行フラグ,リードモード,長さ1バイト
       0x48 # スレーブアドレス(A0とA1のピンの設定によるので注意)
      ]
  udp.send(d.pack('C*'), 0, sockaddr)
  recv = udp.recv(2048);
  dd = recv.unpack('C*')
  temp = (temp + dd[5]) >> 3
  temp = temp * -1 if temp_sign
  puts("temp. = #{temp*0.0625}") # データシートを参照のこと
  sleep(1)
end
udp.close


使用事例(2) : I2C制御なキャラクタ液晶ディスプレイに文字を表示

データを書き出す例として,秋月電子のI2C接続小型キャラクタLCDモジュール 8x2行に文字を表示する例を紹介します.

#!/usr/bin/ruby
require "socket"
@udp = UDPSocket.open()
@sockaddr = Socket.pack_sockaddr_in(16384, "10.0.0.132")
def i2c_write1b(i2c_addr, addr, data)
  d = [0x00, 0x00, # I/O
       0x00, addr & 0xFF, # addr
       data & 0xff, 0x00, 0x00, 0x00, # value
       0x80 | 0x04 | 0x00, # req, write, len=1
       i2c_addr
      ]
  @udp.send(d.pack('C*'), 0, @sockaddr)
  recv = @udp.recv(2048);
end

def lcd_cmd(v)
  i2c_write1b(0x3e, 0, v) # コマンド発行時はCO=0, RS=0に相当する0x00へのアクセスとみなせる
end

def lcd_data(v)
  i2c_write1b(0x3e, 0x40, v) # 文字データの書き込み時はC0=0,RS=1に相当する0x40へのアクセスとみなせる
end

def put_command_list(lst)
  lst.each{|d| lcd_cmd(d); sleep(0.2)}
end

def put_string(str)
  str.split("").each{|d| lcd_data(d.ord)}
end

# データシートのサンプルより
put_command_list([0x38, # function set
                  0x39, # function set
                  0x14, # osc freq.
                  0x70, # contrast
                  0x56, # power/icon/contrast
                  0x6c, # follower
                  0x38, # function set
                  0x0c, # display on/off
                  0x01  # clear display
                 ])
sleep(1)
lcd_cmd(0x80) # カーソルを1行目の先頭に
put_string("Hello,")
lcd_cmd(0xC0) # カーソルを2行目の先頭に
put_string("exLeaf")
@udp.close()

実行すると,キャラクタLCDモジュールに"Hello, exLeaf"という文字が表示されます.

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

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

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

たとえば,

#!/usr/bin/ruby
require "socket"
udp = UDPSocket.open()
sockaddr = Socket.pack_sockaddr_in(16385, "10.0.0.132")
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にセットすることができます.