ブログ

Armadillo-400シリーズ:Repeated Start Condition対応のI2C通信のデバイスのレジスタからリード

at_kazutaka.bito
2016年5月3日 10時08分

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 ****/