17

i2c一般的なLinuxi2cドライバーを使用して単純な読み取り/書き込み機能を実装するためのコードを書いていますlinux/i2c-dev.h

私は混乱していioctlます:I2C_SLAVE

カーネルのドキュメントには次のように記載されています。

read(2) および write(2) 呼び出しを使用して、単純な i2c トランザクションを実行できます。アドレス バイトを渡す必要はありません。代わりに、デバイスにアクセスする前に ioctl I2C_SLAVE で設定してください

ただし、を使用しioctl I2C_RDWRてスレーブアドレスを再度設定する場所を使用していi2c_msg.addrます。

カーネルのドキュメントには、次のことも記載されています。

一部の ioctl() 呼び出しは管理タスク用であり、i2c-dev によって直接処理されます。例には I2C_SLAVE が含まれます

を使用する必要がありioctl I2C_SLAVEますか?もしそうなら、一度だけ、または読み書きを実行するたびに設定する必要がありますか?

もし私がi2cデバイスを持っていれば、デバイス上でコードをテストしただけで、皆さんを悩ませることはなかったでしょうが、残念ながら私は今のところ持っていません。

助けてくれてありがとう。

4

4 に答える 4

25

ユーザー空間から i2c デバイスと通信するには、主に 3 つの方法があります。

1.IOCTL I2C_RDWR

この方法では、同時の読み取り/書き込みが可能で、連続したメッセージのシーケンスを送信できます。すべての i2c デバイスがこの方法をサポートしているわけではありません。

I2C_FUNCSこのメソッドで i/o を実行する前に、ioctl操作を使用して、デバイスがこのメソッドをサポートしているかどうかを確認する必要があります。

この方法を使用すると、ioctl 操作を実行する必要はありませんI2C_SLAVE。メッセージに埋め込まれた情報を使用して、舞台裏で実行されます。

2. IOCTL SMBUS

この I/O の方法はより強力ですが、結果のコードはより冗長になります。この方法は、デバイスがこの方法をサポートしていない場合に使用できますI2C_RDWR

この方法を使用する場合、ioctl操作 (または、デバイスがビジーの場合は操作) を実行する必要があります。I2C_SLAVEI2C_SLAVE_FORCE

3.SYSFS I/O

この方法では、基本的なファイル i/o システム コールread()write(). この方法では、連続した連続操作はできません。この方法は、デバイスがこの方法をサポートしていない場合に使用できますI2C_RDWR

この方法を使用する場合、ioctl操作 (または、デバイスがビジーの場合は操作) を実行する必要があります。I2C_SLAVEI2C_SLAVE_FORCE

チップをファイルのように扱う必要がない限り、この方法が他の方法よりも好ましい状況は考えられません。


完全な IOCTL の例

この例はまだテストしていませんが、i2c デバイスへの書き込みの概念的なフローを示していますI2C_RDWR。ioctl と smbus のどちらの手法を使用するかを自動的に検出します。

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

#include <errno.h>
#include <string.h>

#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

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

#define I2C_ADAPTER "/dev/i2c-0"
#define I2C_DEVICE  0x00

int i2c_ioctl_write (int fd, uint8_t dev, uint8_t regaddr, uint16_t *data, size_t size)
{
    int i, j = 0;
    int ret;
    uint8_t *buf;
    // the extra byte is for the regaddr
    size_t buff_size = 1 + size;

    buf = malloc(buff_size);
    if (buf == NULL) {
        return -ENOMEM;
    }

    buf[j ++] = regaddr;
    for (i = 0; i < size / sizeof(uint16_t); i ++) {
        buf[j ++] = (data[i] & 0xff00) >> 8;
        buf[j ++] = data[i] & 0xff;
    }

    struct i2c_msg messages[] = {
        {
            .addr = dev,
            .buf = buf,
            .len = buff_size,
        },
    };

    struct i2c_rdwr_ioctl_data payload = {
        .msgs = messages,
        .nmsgs = sizeof(messages) / sizeof(messages[0]),
    };

    ret = ioctl(fd, I2C_RDWR, &payload);
    if (ret < 0) {
        ret = -errno;
    }

    free (buf);
    return ret;
}

int i2c_ioctl_smbus_write (int fd, uint8_t dev, uint8_t regaddr, uint16_t *data, size_t size)
{
    int i, j = 0;
    int ret;
    uint8_t *buf;

    buf = malloc(size);
    if (buf == NULL) {
        return -ENOMEM;
    }

    for (i = 0; i < size / sizeof(uint16_t); i ++) {
        buf[j ++] = (data[i] & 0xff00) >> 8;
        buf[j ++] = data[i] & 0xff;
    }

    struct i2c_smbus_ioctl_data payload = {
        .read_write = I2C_SMBUS_WRITE,
        .size = I2C_SMBUS_WORD_DATA,
        .command = regaddr,
        .data = (void *) buf,
    };

    ret = ioctl (fd, I2C_SLAVE_FORCE, dev);
    if (ret < 0)
    {
        ret = -errno;
        goto exit;
    }

    ret = ioctl (fd, I2C_SMBUS, &payload);
    if (ret < 0)
    {
        ret = -errno;
        goto exit;
    }

exit:
    free(buf);
    return ret;
}

int i2c_write (int fd, uint8_t dev, uint8_t regaddr, uint16_t *data, size_t size)
{
    unsigned long funcs;

    if (ioctl(fd, I2C_FUNCS, &funcs) < 0) {
        return -errno;
    }

    if (funcs & I2C_FUNC_I2C) {
        return i2c_ioctl_write (fd, dev, regaddr, data, size);
    } else if (funcs & I2C_FUNC_SMBUS_WORD_DATA) {
        return i2c_ioctl_smbus_write (fd, dev, regaddr, data, size);
    } else {
        return -ENOSYS;
    }
}

int parse_args (uint8_t *regaddr, uint16_t *data, size_t size, char *argv[])
{
    char *endptr;
    int i;

    *regaddr = (uint8_t) strtol(argv[1], &endptr, 0);
    if (errno || endptr == argv[1]) {
        return -1;
    }

    for (i = 0; i < size / sizeof(uint16_t); i ++) {
        data[i] = (uint16_t) strtol(argv[i + 2], &endptr, 0);
        if (errno || endptr == argv[i + 2]) {
            return -1;
        }
    }

    return 0;
}

void usage (int argc, char *argv[])
{
    fprintf(stderr, "Usage: %s regaddr data [data]*\n", argv[0]);
    fprintf(stderr, "  regaddr   The 8-bit register address to write to.\n");
    fprintf(stderr, "  data      The 16-bit data to be written.\n");
    exit(-1);
}

int main (int argc, char *argv[])
{
    uint8_t regaddr;
    uint16_t *data;
    size_t size;
    int fd;
    int ret = 0;

    if (argc < 3) {
        usage(argc, argv);
    }

    size = (argc - 2) * sizeof(uint16_t);
    data = malloc(size);
    if (data == NULL) {
        fprintf (stderr, "%s.\n", strerror(ENOMEM));
        return -ENOMEM;
    }

    if (parse_args(&regaddr, data, size, argv) != 0) {
        free(data);
        usage(argc, argv);
    }

    fd = open(I2C_ADAPTER, O_RDWR | O_NONBLOCK);
    ret = i2c_write(fd, I2C_DEVICE, regaddr, data);
    close(fd);

    if (ret) {
        fprintf (stderr, "%s.\n", strerror(-ret));
    }

    free(data);

    return ret;
}
于 2016-07-14T19:26:46.283 に答える
2

ioctl I2C_RDWR を使用していないため、これが役立つかどうかはわかりませんが、次のコードを使用して成功しています。

int fd;
fd = open("/dev/i2c-5", O_RDWR);
ioctl(fd, I2C_SLAVE_FORCE, 0x20);
i2c_smbus_write_word_data(fd, ___, ___);
i2c_smbus_read_word_data(fd, ___);

最初に I2C_SLAVE_FORCE を 1 回設定するだけで、その後は好きなだけ読み書きできます。

PS - これは単なるコード サンプルであり、明らかにこれらすべての関数の戻り値を確認する必要があります。このコードを使用して、デジタル I/O チップと通信しています。2 つの i2c_* 関数は、ioctl(fd, I2C_SMBUS, &args); を呼び出す単なるラッパーです。ここで、args は struct i2c_smbus_ioctl_data 型です。

于 2012-04-23T19:42:29.317 に答える
1

read()とメソッドを使用する場合は、一度write()呼び出すだけで十分です。デバイスがすでに使用されている場合にも使用できます。ioctlI2C_SLAVEI2C_SLAVE_FORCE

ただし、メソッドを使用してすべてのデバイスの特定のレジスタを読み取る一貫した方法をまだ見つけていませんread()/write()

于 2012-04-02T17:11:44.300 に答える