I2C_SLAVE ioctl的用途

我正在编写代码来实现一个简单的i2c读/写function,使用一般的linux i2c驱动程序linux/i2c-dev.h

我对ioctlI2C_SLAVE感到困惑

内核文档说明如下:

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

但是,我正在使用ioctl I2C_RDWR ,我再次使用i2c_msg.addr设置从站地址。

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

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

那么是否必须使用ioctl I2C_SLAVE ? 如果是这样,我只需要设置一次或每次执行读取和写入?

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

谢谢您的帮助。

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

1. IOCTL I2C_RDWR

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

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

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

2. IOCTL SMBUS

这种I / O方法功能更强大,但生成的代码更加冗长。 如果器件不支持I2C_RDWR方法,则可以使用此方法。

使用这种方法,您需要执行ioctl I2C_SLAVE操作(或者,如果设备忙,则执行I2C_SLAVE_FORCE操作)。

3. SYSFS I / O

这个方法使用基本的文件I / O系统调用read()write() 。 使用这种方法不可能不间断的顺序操作。 如果器件不支持I2C_RDWR方法,则可以使用此方法。

使用这种方法,您需要执行ioctl I2C_SLAVE操作(或者,如果设备忙,则执行I2C_SLAVE_FORCE操作)。

我想不出任何情况下这种方法比其他方式更好,除非你需要芯片被视为一个文件。


完整的IOCTL示例

我没有测试过这个例子,但它显示了写入一个i2c设备的概念流程 – 自动检测是否使用ioctl I2C_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) { int i, j = 0; int ret; uint8_t *buf; buf = malloc(1 + 2 * (sizeof(data) / sizeof(data[0]))); if (buf == NULL) { return -ENOMEM; } buf[j ++] = regaddr; for (i = 0; i < (sizeof(data) / sizeof(data[0])); i ++) { buf[j ++] = (data[i] & 0xff00) >> 8; buf[j ++] = data[i] & 0xff; } struct i2c_msg messages[] = { { .addr = dev, .buf = buf, .len = sizeof(buf) / sizeof(buf[0]), }, }; 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) { int i, j = 0; int ret; uint8_t *buf; buf = malloc(2 * (sizeof(data) / sizeof(data[0]))); if (buf == NULL) { return -ENOMEM; } for (i = 0; i < (sizeof(data) / sizeof(data[0])); 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) { uint64_t funcs; if (ioctl(fd, I2C_FUNCS, &funcs) < 0) { return -errno; } if (funcs & I2C_FUNC_I2C) { return i2c_ioctl_write (fd, dev, regaddr, data); } else if (funcs & I2C_FUNC_SMBUS_WORD_DATA) { return i2c_ioctl_smbus_write (fd, dev, regaddr, data); } else { return -ENOSYS; } } int parse_args (uint8_t *regaddr, uint16_t *data, 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 < sizeof(data) / sizeof(data[0]); 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; int fd; int ret = 0; if (argc < 3) { usage(argc, argv); } data = malloc((argc - 2) * sizeof(uint16_t)); if (data == NULL) { fprintf (stderr, "%s.\n", strerror(ENOMEM)); return -ENOMEM; } if (parse_args(&regaddr, data, 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; } 

我不太确定这是否有帮助,因为我不使用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类型。

如果使用read()write()方法,则使用I2C_SLAVE调用ioctl就足够了。 如果设备已经在使用,您也可以使用I2C_SLAVE_FORCE

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