I2C通信において、単純なRead、Writeでは、Stop Conditionで通信を終了するまでRead、Writeの方向は固定です。
内部レジスタを持つI2C通信のデバイスで、任意のレジスタをReadする方法として、
単純なRead、Writeを組み合わせで行える場合は、下記のようになります。
例)
I2C通信のデバイスのスレーブアドレス:0x1C
の
レジスタアドレス:0x2A
をReadする場合、下図のように、レジスタアドレス:0x2AをWriteして、Stop Conditionで通信を終了後、Readすることで、
直前にWriteしたレジスタアドレス(0x2A)をReadできます。
(下図において、スレーブアドレス(7bit)、レジスタアドレス(8bit)部分の クロック、データは破線で省略しています。)
一方、I2C通信には、Stop Conditionの代わりにRepeated Start Conditionを使って、 通信を終了することなく、Read、Writeの方向を切り替えて通信する方法があります。
I2C通信のデバイスで内部レジスタをReadする場合に、Repeated Start Conditionを使った通信が必要な場合(※)があります。
※)Repeated Start Condition対応のデバイスには、Stop Conditionで通信を終了すると、 Writeした値を維持しない(Read時のレジスタアドレスとして使用できない)ものもあるようです。
Repeated Start Conditionを使って任意のレジスタをReadする場合は、下記のようになります。
例)
I2C通信のデバイスのスレーブアドレス:0x1C
の
レジスタアドレス:0x2A
をReadする場合、下図のように、レジスタアドレス(0x2A)をWriteして、Repeated Start Conditionを挿入後、Readすることで、
Repeated Start Conditionの直前にWriteしたレジスタアドレス(0x2A)をReadできます。
(下図において、スレーブアドレス(7bit)、レジスタアドレス(8bit)部分のクロック、データは破線で省略しています。)
ここでは、Armadillo-440とI2C通信の3軸加速度センサーMMA8452Qの組み合わせで、
Repeated Start Conditionを使って、MMA8452QのレジスタをReadしましたサンプルプログラムを添付します。
i2c_acc_mma8452.tar.gz
i2c_acc_mma8452.tar.gzを解凍すると、i2c_acc_mma8452ディレクトリに Makefile acc_mma8452.c exitfail.h mma8452.c mma8452.h というファイルが展開されます。
このサンプルプログラムは、 Armadillo実践開発ガイド第3部 2.2. I2C接続A/Dコンバーター 2.2.5. サンプルプログラム をベースに、Repeated Start Conditionを使って、WriteからReadに切り替える機能を追加して、 MMA8452Qからデータを取得して標準出力に表示するように改造したものです。
下記では、添付のサンプルプログラムについて、Repeated Start Conditionに関する箇所のみ説明します。
(3軸加速度センサー(MMA8452Q)から加速度を取得する一連の手順については、
Armadillo-400シリーズ:3軸加速度センサー(MMA8452Q)から加速度を取得するサンプルプログラム
を参考ください。)
mma8452.c
Repeated Start Conditionに関する箇所は、mma8452.cの
/**** sample: for I2C SMBUS: begin ****/
/**** sample: for I2C SMBUS: end ****/
で囲んだ下記の箇所になります。
// Repeated Start Conditionで通信するために、ioctlでI2C_SMBUSを指定できるようにi2c.hをインクルード。 /**** sample: for I2C SMBUS: begin ****/ #include <linux/i2c.h> /**** sample: for I2C SMBUS: end ****/
// mma8452_read_smbus_byteは、"start_addr"で指定したレジスタアドレスから1Byte分Readして、"*digit"に格納する関数。 /**** sample: for I2C SMBUS: begin ****/ int mma8452_read_smbus_byte(struct mma8452 *acc, uint8_t start_addr, uint8_t *digit) { union i2c_smbus_data data; struct i2c_smbus_ioctl_data args; if(acc == NULL || digit == NULL || start_addr > MMA8452_REG_MAX) { errno = EINVAL; return -1; } args.read_write = I2C_SMBUS_READ; // I2C_SMBUS_READはReadを意味する。 args.command = start_addr; // ここでは、レジスタアドレスを設定。 args.size = I2C_SMBUS_BYTE_DATA; // ReadするByte数。I2C_SMBUS_BYTE_DATAは1Byteを意味する。 args.data = &data; // Readしたデータの格納先。 if (ioctl(acc->fd, I2C_SMBUS, &args)) // Repeated Start Conditionで通信する場合は、ioctl関数でI2C_SMBUSを指定。 return -1; else *digit = 0xFF & data.byte; return 0; } /**** sample: for I2C SMBUS: end ****/
acc_mma8452.c
acc_mma8452.cでは、上記、mma8452_read_smbus_byteを使って、 WHO_AM_Iレジスタ(レジスタアドレス:0x0D)および全レジスタをReadをしています。
/**** MMA8452: SMBUS Read Register(WHO_AM_I: 0x0d): begin ****/ ret = mma8452_read_smbus_byte(acc, 0x0d, digital_code); // mma8452_read_smbus_byteを使って、レジスタアドレス:0x0DをRead。 if(ret !=0) exitfail_errno("mma8452_read_smbus_byte"); printf("-------- WHO_AM_I --------\n"); printf("WHO_AM_I(0x%02X): 0x%02X\n", 0x0d, digital_code[0]); /**** MMA8452: SMBUS Read Register(WHO_AM_I: 0x0d): end ****/ /**** MMA8452: SMBUS Read Register(All: 0x00-0x31): begin ****/ printf("-------- Read All Registers --------\n"); for(i = 0; i < 0x31 + 1; i++) { ret = mma8452_read_smbus_byte(acc, i, digital_code); // mma8452_read_smbus_byteを使って、全レジスタ(i=0x00~0x31)をRead。 if(ret !=0) exitfail_errno("mma8452_read_smbus_byte"); printf("0x%02X: 0x%02X\n", i, digital_code[0]); } /**** MMA8452: SMBUS Read Register(0x00-0x31): end ****/