2015年3月15日日曜日

Physical Computing (特に Intel Edison) における I2C による通信について(その3)

 前回 Physical Computing (特に Intel Edison) における I2C による通信について(その2)として,Linux での I2C 通信の例について書いた。 今回はその続きで,mraa ライブラリでの I2C 通信について書いてみよう。
 項目番号は前回からの続きということで (7) から始まる。

(7) mraa ライブラリでの I2C 通信:Linux の実装における SMBus 通信を使った I2C 通信(書込み
  次に mraa ライブラリでの I2C 通信の処理について見てみよう。 mraa ライブラリでは I2C 通信は,mraa/src/i2c/i2c.c に記載されている。 そこでは,SMBus 通信を使って I2C 通信を行っている。以下具体的に見てみよう。

 まずは書込みルーチンについて見てみよう。 下記に mraa/src/i2c/i2c.c の中の書込みルーチンのうちの2個を載せておく。
mraa_result_t mraa_i2c_write_byte(mraa_i2c_context dev, const uint8_t data)
{
  if (mraa_i2c_smbus_access(dev->fh, I2C_SMBUS_WRITE, data, I2C_SMBUS_BYTE, NULL) < 0) {
    syslog(LOG_ERR, "i2c: Failed to write");
    return MRAA_ERROR_INVALID_HANDLE;
  }
  return MRAA_SUCCESS;
}

mraa_result_t mraa_i2c_write_byte_data(mraa_i2c_context dev, const uint8_t data, const uint8_t command)
{
  i2c_smbus_data_t d;
  d.byte = data;
  if (mraa_i2c_smbus_access(dev->fh, I2C_SMBUS_WRITE, command, I2C_SMBUS_BYTE_DATA, &d) < 0) {
    syslog(LOG_ERR, "i2c: Failed to write");
    return MRAA_ERROR_INVALID_HANDLE;
  }
  return MRAA_SUCCESS;
}
 これらは共に SMBus を使った I2C 機器への書込み処理を表している。 どちらも mraa_i2c_smbus_access ルーチンを使っている。 違いは,mraa_i2c_write_byte_data ルーチンの方は command&d の2つある部分が,mraa_i2c_write_byte ルーチンの方は command の代わりに data 1個のみになり,&d の部分は NULL となっている。 また,第4引数が「I2C_SMBUS_BYTE」と「I2C_SMBUS_BYTE_DATA」で異なっている。 これは含まれるデータの数を表しており,/usr/include/linux/i2c-dev-user.h の中で定義されている。

 そこで,上記2ルーチンの中で使われている mraa_i2c_smbus_access ルーチンについて見てみよう。 これは mraa/src/i2c/i2c.c の中のはじめの方に記載がある。 ここでは mraa_i2c_smbus_access ルーチン内で使われる i2c_smbus_ioctl_data_t 型についての定義も載せておく。
typedef struct i2c_smbus_ioctl_data_struct
{
  uint8_t read_write;     ///< operation direction
  uint8_t command;        ///< ioctl command
  int size;               ///< data size
  i2c_smbus_data_t *data; ///< data
} i2c_smbus_ioctl_data_t;

int mraa_i2c_smbus_access(int fh, uint8_t read_write, uint8_t command, int size, i2c_smbus_data_t *data)
{
  i2c_smbus_ioctl_data_t args;

  args.read_write = read_write;
  args.command = command;
  args.size = size;
  args.data = data;

  return ioctl(fh, I2C_SMBUS, &args);
}

 mraa_i2c_smbus_access ルーチンでは,最後に ioctl を呼び出しているが,第2引数が I2C_SMBUS となっており,SMBus を使った書込みということを示している。 また,送り出すデータの構造体は,I2C_RDWR を使う場合とは異なってる。 その構造体 i2c_smbus_ioctl_data_t は,
   ・read_write
   ・command
   ・size
   ・*data
から成っている。 read_write は書込みか,読込みかの方向を表している。 command はスレーブ機器へのコマンドであり,内部レジスタがある場合は内部レジスタを示すと考えれば良い。 もし内部レジスタの指定がなく,1バイトのみを書込みたい時には,この command の部分にデータを入れれば良い事になる。 size はデータサイズであり,最後に書込みデータがある。

 ここで気になるのが,この i2c_smbus_ioctl_data_t 型の中にはスレーブアドレスを入れる部分がない,という点である。 実は,スレーブアドレスの指定は,この mraa_i2c_smbus_access ルーチンを呼び出す直前に別ルーチンを呼び出すことで行う。具体的には,以下のようにして書込みを行う。
mraa_i2c_address(i2c, HMC5883L_I2C_ADDR);     // スレーブアドレス指定
mraa_i2c_write_byte(i2c, HMC5883L_DATA_REG);  // 書込み処理
 ここで,mraa_i2c_address ルーチンがスレーブアドレスの指定を行うルーチンである。 つまり SMBus を使う手順では,まず,受け取るスレーブ機器のアドレスを指定し, その直後に対象となるスレーブ機器に対してデータのやりとりをする,という感じである。
 言い換えると,I2C では送受信するデータの先頭にアドレスをくっつけるが, Linux の実装の SMBus 通信では受取側アドレスの指定とデータの送受信が別個になっている。

 せっかくなので,mraa/src/i2c/i2c.c の中の別の書込みチールンを書いておこう。
mraa_result_t mraa_i2c_write(mraa_i2c_context dev, const uint8_t* data, int length)
{
  i2c_smbus_data_t d;
  int i;
  uint8_t command = data[0];

  data = &data[1];
  length = length-1;
  if (length > I2C_SMBUS_I2C_BLOCK_MAX) {
    length = I2C_SMBUS_I2C_BLOCK_MAX;
  }

  for (i=1; i<=length; i++) {
    d.block[i] = data[i-1];
  }
  d.block[0] = length;

  return mraa_i2c_smbus_access(dev->fh, I2C_SMBUS_WRITE, command, I2C_SMBUS_I2C_BLOCK_DATA, &d);
}
 このルーチンは data 配列を書き込むのだが,内部レジスタがある場合は,data 配列の先頭 data[0] に内部レジスタを入れ,その後に書き込むデータを入れたものを data として渡すようになっている。 しかし,ルーチンの中ではわざわざ data[0] を取り出して,mraa_i2c_smbus_access ルーチンに渡している…。 なんかいまいちな感じやなぁ…。

 このルーチンを使った例は以下のようになる。
mraa_i2c_address(i2c, HMC5883L_I2C_ADDR);  // スレーブアドレス指定
rx_tx_buf[0] = HMC5883L_MODE_REG;   // 内部レジスタの指定
rx_tx_buf[1] = HMC5883L_CONT_MODE;  // 書込みたいデータ
mraa_i2c_write(i2c, rx_tx_buf, 2);  // 書込み処理


(8) mraa ライブラリでの I2C 通信(読込み
  mraa ライブラリでの I2C の読込みルーチンは書込みルーチンとほぼ同じであり, 特に説明の必要はないと思う。 是非,自分で mraa/src/i2c/i2c.c を読んでみて欲しい。 例えば,
   ・mraa_i2c_read_byte(mraa_i2c_context dev):dev というファイルハンドラを持つデバイスから1バイト読み込む
   ・mraa_i2c_read_byte_data(mraa_i2c_context dev, uint8_t command):devcommand を書き込んでから1バイト読み出す。
           内部レジスタがある場合は,command の部分に内部レジスタ番号を書き込めばよい。
   ・mraa_i2c_read_word_data(mraa_i2c_context dev, uint8_t command):command の後に dev から2バイト(1ワード)読み込む。
などがある。

 多少,趣きが異なる読込みルーチンとして,
   ・mraa_i2c_read_bytes_data(mraa_i2c_context dev, uint8_t command, uint8_t* data, int length)
がある。これは,I2C_RDWR を使った読み書きのところに書いた「読込みルーチン」とほぼ同じ処理をしている。 つまり,内部レジスタ指定のために1バイトのデータを書込み,通信を切らないままデータを読込む,というルーチンである。 上記に示したルーチンとの違いは,1バイトや1ワードにかぎらず,もっと長いデータも読み込めるルーチン,という点である。

注意libmraa0ver. 0.6.1には,この mraa_i2c_read_bytes_data ルーチンは含まれていない。 github の mraa の commmit 情報を見ると,このルーチンは 2015/3/4 に追加されている。 しかし libmraa0v 0.6.1 のリリースは 2015/2/27 であり,この追加はまだ反映されていない。 今後のリリースで反映されるものと思われる。
追記) 2015/4/14 付で libmraa0ver. 0.6.2 にバージョンアップされていた。 この ver. 0.6.2 には上記の mraa_i2c_read_bytes_data ルーチンが含まれている。

0 件のコメント: