LoginSignup
22
16

More than 3 years have passed since last update.

LinuxのC言語でI2Cデバイスと通信する

Posted at

はじめに

Raspberry PiやJetson nanoでI2Cデバイスを使いたい時に、RasPiではpigpioやWiringPiなどのライブラリがありますが、JetsonだとJetson.GPIOぐらいしかなく、C/C++で書きたい時に困ることがあります。(別に困らない??)

そんな時のためにLinuxの汎用的なI2Cドライバを使用してI2Cデバイスと通信する方法を記事にしました。

確認環境

  • Raspberry Pi (Raspbian: Linux kernel 4.19)
  • Jetson nano (Linux kernel 4.9)

ソースコード

I2Cデバイスと通信する方法はいくつかあるみたいですが、このコードでは ioctl I2C_RDWR を使用しています。

i2c_example.c
#include <stdint.h>
// ...
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/i2c-dev.h>
#include <linux/i2c.h>

static const char* dev_name = "/dev/i2c-1";

/*! I2Cスレーブデバイスからデータを読み込む.
 * @param[in] dev_addr デバイスアドレス.
 * @param[in] reg_addr レジスタアドレス.
 * @param[out] data 読み込むデータの格納場所を指すポインタ.
 * @param[in] length 読み込むデータの長さ.
 */
int8_t i2c_read(
    uint8_t dev_addr, uint8_t reg_addr, uint8_t* data, uint16_t length) {
  /* I2Cデバイスをオープンする. */
  int32_t fd = open(dev_name, O_RDWR);
  if (fd == -1) {
    fprintf(stderr, "i2c_read: failed to open: %s\n", strerror(errno));
    return -1;
  }

  /* I2C-Readメッセージを作成する. */
  struct i2c_msg messages[] = {
      { dev_addr, 0, 1, &reg_addr },         /* レジスタアドレスをセット. */
      { dev_addr, I2C_M_RD, length, data },  /* dataにlengthバイト読み込む. */
  };
  struct i2c_rdwr_ioctl_data ioctl_data = { messages, 2 };

  /* I2C-Readを行う. */
  if (ioctl(fd, I2C_RDWR, &ioctl_data) != 2) {
    fprintf(stderr, "i2c_read: failed to ioctl: %s\n", strerror(errno));
    close(fd);
    return -1;
  }

  close(fd);
  return 0;
}

/*! I2Cスレーブデバイスにデータを書き込む.
 * @param[in] dev_addr デバイスアドレス.
 * @param[in] reg_addr レジスタアドレス.
 * @param[in] data 書き込むデータの格納場所を指すポインタ.
 * @param[in] length 書き込むデータの長さ.
 */
int8_t i2c_write(
    uint8_t dev_addr, uint8_t reg_addr, const uint8_t* data, uint16_t length) {
  /* I2Cデバイスをオープンする. */
  int32_t fd = open(dev_name, O_RDWR);
  if (fd == -1) {
    fprintf(stderr, "i2c_write: failed to open: %s\n", strerror(errno));
    return -1;
  }

  /* I2C-Write用のバッファを準備する. */
  uint8_t* buffer = (uint8_t*)malloc(length + 1);
  if (buffer == NULL) {
    fprintf(stderr, "i2c_write: failed to memory allocate\n");
    close(fd);
    return -1;
  }
  buffer[0] = reg_addr;              /* 1バイト目にレジスタアドレスをセット. */
  memcpy(&buffer[1], data, length);  /* 2バイト目以降にデータをセット. */

  /* I2C-Writeメッセージを作成する. */
  struct i2c_msg message = { dev_addr, 0, length + 1, buffer };
  struct i2c_rdwr_ioctl_data ioctl_data = { &message, 1 };

  /* I2C-Writeを行う. */
  if (ioctl(fd, I2C_RDWR, &ioctl_data) != 1) {
    fprintf(stderr, "i2c_write: failed to ioctl: %s\n", strerror(errno));
    free(buffer);
    close(fd);
    return -1;
  }

  free(buffer);
  close(fd);
  return 0;
}

解説

まずはじめにヘッダのインクルードとデバイス名の定義です。

#include <linux/i2c-dev.h>
#include <linux/i2c.h>

static const char* dev_name = "/dev/i2c-1";

RasPiとJetson nanoの I2C Bus1 のピンアサインは共通で、デバイス名も /dev/i2c-1 で同じです。

  • GPIO 2 : SDA I2C Bus1
  • GPIO 3 : SCL I2C Bus1

Read処理

  /* I2C-Readメッセージを作成する. */
  struct i2c_msg messages[] = {
      { dev_addr, 0, 1, &reg_addr },         /* レジスタアドレスをセット. */
      { dev_addr, I2C_M_RD, length, data },  /* dataにlengthバイト読み込む. */
  };
  struct i2c_rdwr_ioctl_data ioctl_data = { messages, 2 };

  /* I2C-Readを行う. */
  if (ioctl(fd, I2C_RDWR, &ioctl_data) != 2) {

Readの時は i2c_msg 構造体が2つ必要になります。
1つ目にレジスタアドレスをセット、
2つ目に読み込むサイズとデータの格納場所を指定しています。

Write処理

  /* I2C-Write用のバッファを準備する. */
  uint8_t* buffer = (uint8_t*)malloc(length + 1);
  buffer[0] = reg_addr;              /* 1バイト目にレジスタアドレスをセット. */
  memcpy(&buffer[1], data, length);  /* 2バイト目以降にデータをセット. */

  /* I2C-Writeメッセージを作成する. */
  struct i2c_msg message = { dev_addr, 0, length + 1, buffer };
  struct i2c_rdwr_ioctl_data ioctl_data = { &message, 1 };

  /* I2C-Writeを行う. */
  if (ioctl(fd, I2C_RDWR, &ioctl_data) != 1) {

Writeの時は『書き込むデータの長さ+1』のバッファが必要になります。
1バイト目にレジスタアドレスをセット、
2バイト目以降にデータをセットします。

i2c_msg 構造体は1つになります。

おわりに

自分はこのコードでRasPiとJetsonNanoの両方でI2Cデバイスと通信が出来ましたが、コードや内容に間違いがあるかもしれませんのでご注意ください。

22
16
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
22
16