17

我正在编写使用通用 linux驱动程序实现简单i2c读/写功能的代码i2clinux/i2c-dev.h

我对以下内容感到困惑ioctlI2C_SLAVE

内核文档说明如下:

您可以使用 read(2) 和 write(2) 调用来执行普通的 i2c 事务。您不需要传递地址字节;而是在尝试访问设备之前通过 ioctl I2C_SLAVE 设置它

但是我正在使用ioctl I2C_RDWRwhere 我再次使用i2c_msg.addr.

内核文档还提到以下内容:

一些 ioctl() 调用用于管理任务,由 i2c-dev 直接处理。示例包括 I2C_SLAVE

那么一定要使用ioctl I2C_SLAVE吗?如果是这样,我只需要设置一次还是每次执行读写时都需要设置它?

如果我有一i2c台设备,我可以只测试设备上的代码,不会打扰你们,但不幸的是我现在没有。

谢谢您的帮助。

4

4 回答 4

25

从用户空间与 i2c 设备通信有三种主要方法。

1.IOCTL I2C_RDWR

此方法允许同时读/写和发送不间断的消息序列。并非所有 i2c 设备都支持此方法。

I2C_FUNCS在使用此方法执行 i/o 之前,您应该使用 ioctl操作检查设备是否支持此方法。

使用这种方法,您不需要执行 ioctlI2C_SLAVE操作——它是在幕后使用消息中嵌入的信息完成的。

2.IOCTL SMBUS

这种 i/o 方法更强大,但生成的代码更冗长。如果设备不支持该方法,可以使用该I2C_RDWR方法。

使用此方法,您确实需要执行 ioctlI2C_SLAVE操作(或者,如果设备忙,则执行I2C_SLAVE_FORCE操作)。

3. SYSFS I/O

此方法使用基本文件 i/o 系统调用read()write(). 使用此方法无法进行不间断的顺序操作。如果设备不支持该方法,可以使用该I2C_RDWR方法。

使用此方法,您确实需要执行 ioctlI2C_SLAVE操作(或者,如果设备忙,则执行I2C_SLAVE_FORCE操作)。

我想不出这种方法比其他方法更可取的任何情况,除非您需要将芯片视为文件。


完整的 IOCTL 示例

我没有测试过这个例子,但它显示了写入 i2c 设备的概念流程。自动检测是使用 ioctlI2C_RDWR还是 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 一次,之后我可以随心所欲地读写。

PS - 这只是一个代码示例,显然您应该检查所有这些函数的返回。我正在使用此代码与数字 I/O 芯片进行通信。这两个 i2c_* 函数只是调用 ioctl(fd, I2C_SMBUS, &args); 的包装器。其中 args 是 struct i2c_smbus_ioctl_data 类型。

于 2012-04-23T19:42:29.317 回答
1

如果你使用read()andwrite()方法,调用一次就足够了ioctl。如果设备已在使用中,I2C_SLAVE您也可以使用。I2C_SLAVE_FORCE

但是,我还没有找到一种一致的方法来使用这些read()/write()方法读取每个设备的特定寄存器。

于 2012-04-02T17:11:44.300 回答
0

对于感兴趣的人,当有问题的设备已经由内核驱动程序管理时,使用 SLAVE_FORCE。(i2cdetect 将显示该地址的 UU)

于 2019-06-25T19:04:46.430 回答