Linux I2C驱动--用户态驱动简单示例
Posted 99度的水
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux I2C驱动--用户态驱动简单示例相关的知识,希望对你有一定的参考价值。
1. Linux内核支持I2C通用设备驱动(用户态驱动:由应用层实现对硬件的控制可以称之为用户态驱动),实现文件位于drivers/i2c/i2c-dev.c,设备文件为/dev/i2c-0
2. I2C通用设备驱动以字符设备注册进内核的
static const struct file_operations i2cdev_fops = { .owner = THIS_MODULE, .llseek = no_llseek, .read = i2cdev_read, .write = i2cdev_write, .unlocked_ioctl = i2cdev_ioctl, .open = i2cdev_open, .release = i2cdev_release, }; res = register_chrdev(I2C_MAJOR, "i2c", &i2cdev_fops);
3. 对设备文件进行读写时,可以调用read、write或者ioctl等方法,他们都是通过调用函数i2c_transfer来实现对I2C设备的操作的
int i2c_transfer(struct i2c_adapter * adap, struct i2c_msg *msgs, int num) { int ret; /* REVISIT the fault reporting model here is weak: * * - When we get an error after receiving N bytes from a slave, * there is no way to report "N". * * - When we get a NAK after transmitting N bytes to a slave, * there is no way to report "N" ... or to let the master * continue executing the rest of this combined message, if * that\'s the appropriate response. * * - When for example "num" is two and we successfully complete * the first message but get an error part way through the * second, it\'s unclear whether that should be reported as * one (discarding status on the second message) or errno * (discarding status on the first one). */ if (adap->algo->master_xfer) { #ifdef DEBUG for (ret = 0; ret < num; ret++) { dev_dbg(&adap->dev, "master_xfer[%d] %c, addr=0x%02x, " "len=%d%s\\n", ret, (msgs[ret].flags & I2C_M_RD) ? \'R\' : \'W\', msgs[ret].addr, msgs[ret].len, (msgs[ret].flags & I2C_M_RECV_LEN) ? "+" : ""); } #endif if (in_atomic() || irqs_disabled()) { ret = mutex_trylock(&adap->bus_lock); if (!ret) /* I2C activity is ongoing. */ return -EAGAIN; } else { mutex_lock_nested(&adap->bus_lock, adap->level); } ret = adap->algo->master_xfer(adap,msgs,num); mutex_unlock(&adap->bus_lock); return ret; } else { dev_dbg(&adap->dev, "I2C level transfers not supported\\n"); return -EOPNOTSUPP; } }
4. i2c_transfer通过代码可以看出,i2c_transfer 通过调用相应的 adapter 的 master_xfer 方法实现的,而 master_xfer 主要是根据 struct i2c_msg 类型的msgs来进行处理的。
struct i2c_msg { __u16 addr; /* slave address */ __u16 flags; #define I2C_M_TEN 0x0010 /* this is a ten bit chip address */ #define I2C_M_RD 0x0001 /* read data, from slave to master */ #define I2C_M_NOSTART 0x4000 /* if I2C_FUNC_PROTOCOL_MANGLING */ #define I2C_M_REV_DIR_ADDR 0x2000 /* if I2C_FUNC_PROTOCOL_MANGLING */ #define I2C_M_IGNORE_NAK 0x1000 /* if I2C_FUNC_PROTOCOL_MANGLING */ #define I2C_M_NO_RD_ACK 0x0800 /* if I2C_FUNC_PROTOCOL_MANGLING */ #define I2C_M_RECV_LEN 0x0400 /* length will be first received byte */ __u16 len; /* msg length */ __u8 *buf; /* pointer to msg data */ };
5. I2C用户态驱动简单示例
(1)通过read、write实现对I2C设备的操作
#include <stdio.h> #include <fcntl.h> #include <unistd.h> #include <malloc.h> #include <sys/ioctl.h> #include <string.h> #include <linux/i2c.h> #include <linux/i2c-dev.h> #define SLAVE_ADDRESS 0x50 #define I2C_WRITE_FLAG 0x00 #define I2C_READ_FLAG 0x01 #define USAGE "xxx -r address length\\nxxx -w address data1 data2...\\n" void i2c_write_bytes(int fd, unsigned char address, unsigned char* data, unsigned short len) { unsigned char* write_data = malloc(len + 1); write_data[0] = address; memcpy(&write_data[1], data, len); ioctl(fd, I2C_SLAVE, SLAVE_ADDRESS); ioctl(fd, I2C_TIMEOUT, 1); ioctl(fd, I2C_RETRIES, 1); write(fd, write_data, len + 1); printf("Write data success\\n"); if(write_data != NULL) { free(write_data); write_data = NULL; } } void i2c_read_bytes(int fd, unsigned char address, unsigned char* buf, unsigned short len) { ioctl(fd, I2C_SLAVE, SLAVE_ADDRESS); ioctl(fd, I2C_TIMEOUT, 1); ioctl(fd, I2C_RETRIES, 1); write(fd, &address, 1); read(fd, buf, len); printf("buf[0] = 0x%x\\n", buf[0]); printf("Read data success\\n"); } int main(int argc, char* argv[]) { int opt; int fd = -1; unsigned char address; unsigned short len = 0, i = 0; unsigned char buf[256] = {0}; // Open device file fd = open("/dev/i2c-0", O_RDWR); if(fd < 0) { printf("Open file error\\n"); goto Exit; } while((opt = getopt(argc, argv, "w:r:")) != -1) { switch(opt) { case \'w\': printf("optarg = %s\\n", optarg); printf("optind = %d\\n", optind); printf("argc = %d\\n", argc); printf("argv[optind] = %s\\n", argv[optind]); address = (unsigned char)strtol(optarg, NULL, 0); printf("address = %x\\n", address); for(len = 0; optind < argc; optind++, len++) { buf[len] = (unsigned char)strtol(argv[optind], NULL, 0); } printf("len = %x\\n", len); i2c_write_bytes(fd, address, buf, len); break; case \'r\': printf("optarg = %s\\n", optarg); printf("optind = %d\\n", optind); printf("argv[optind] = %s\\n", argv[optind]); address = (unsigned char)strtol(optarg, NULL, 0); printf("address = 0x%x\\n", address); len = (unsigned short)strtol(argv[optind], NULL, 0); printf("len = 0x%x\\n", len); i2c_read_bytes(fd, address, buf, len); printf("Read content:\\n"); for(i = 0; i < len; i++) { printf("0x%x ", buf[i]); } printf("\\n"); break; default: printf("Invalid parameter\\n"); printf(USAGE); break; } } Exit: close(fd); return 0; }
(2)通过ioctl实现对I2C设备的操作
#include <stdio.h> #include <fcntl.h> #include <unistd.h> #include <malloc.h> #include <sys/ioctl.h> #include <string.h> #include <linux/i2c.h> #include <linux/i2c-dev.h> #define SLAVE_ADDRESS 0x50 #define I2C_WRITE_FLAG 0x00 #define I2C_READ_FLAG 0x01 #define USAGE "xxx -r address length\\nxxx -w address data1 data2...\\n" void i2c_write_bytes(int fd, unsigned char address, unsigned char* data, unsigned short len) { struct i2c_rdwr_ioctl_data e2prom_write_data; e2prom_write_data.nmsgs = 1; e2prom_write_data.msgs = malloc(sizeof(struct i2c_msg) * e2prom_write_data.nmsgs); e2prom_write_data.msgs[0].addr = SLAVE_ADDRESS; e2prom_write_data.msgs[0].flags = I2C_WRITE_FLAG; e2prom_write_data.msgs[0].len = len + 1;//address data e2prom_write_data.msgs[0].buf = malloc(e2prom_write_data.msgs[0].len); e2prom_write_data.msgs[0].buf[0] = address; memcpy(&(e2prom_write_data.msgs[0].buf[1]), data, (size_t)len); // Using ioctl to write data ioctl(fd, I2C_RDWR, (unsigned long)&e2prom_write_data); printf("Write data success\\n"); if(e2prom_write_data.msgs != NULL) { free(e2prom_write_data.msgs); e2prom_write_data.msgs = NULL; } } void i2c_read_bytes(int fd, unsigned char address, unsigned char* buf, unsigned short len) { struct i2c_rdwr_ioctl_data e2prom_read_data; e2prom_read_data.nmsgs = 2;//Need writing address first, then reading e2prom_read_data.msgs = malloc(sizeof(struct i2c_msg) * e2prom_read_data.nmsgs); e2prom_read_data.msgs[0].addr = SLAVE_ADDRESS; e2prom_read_data.msgs[0].flags = I2C_WRITE_FLAG; e2prom_read_data.msgs[0].len = 1; e2prom_read_data.msgs[0].buf = malloc(e2prom_read_data.msgs[0].len); e2prom_read_data.msgs[0].buf[0] = address; e2prom_read_data.msgs[1].addr = SLAVE_ADDRESS; e2prom_read_data.msgs[1].flags = I2C_READ_FLAG; e2prom_read_data.msgs[1].len = len; e2prom_read_data.msgs[1].buf = malloc(e2prom_read_data.msgs[0].len); e2prom_read_data.msgs[1].buf[0] = 0x00; // Using ioctl to read data ioctl(fd, I2C_RDWR, (unsigned long)&e2prom_read_data); printf("e2prom_read_data.msgs[1].buf[0] = 0x%x\\n", e2prom_read_data.msgs[1].buf[0]); memcpy((void*)buf, (void*)(e2prom_read_data.msgs[1].buf), (unsigned int)len); if(e2prom_read_data.msgs != NULL) { free(e2prom_read_data.msgs); e2prom_read_data.msgs = NULL; } } int main(int argc, char* argv[]) { int opt; int fd = -1; unsigned char address; unsigned short len = 0, i = 0; unsigned char buf[256] = {0}; // Open device file fd = open("/dev/i2c-0", O_RDWR); if(fd < 0) { printf("Open file error\\n"); goto Exit; } while((opt = getopt(argc, argv, "w:r:")) != -1) { switch(opt) { case \'w\': printf("optarg = %s\\n", optarg); printf("optind = %d\\n", optind); printf("argc = %d\\n", argc); printf("argv[optind] = %s\\n", argv[optind]); address = (unsigned char)strtol(optarg, NULL, 0); printf("address = %x\\n", address); for(len = 0; optind < argc; optind++,len++) { buf[len] = (unsigned char)strtol(argv[optind], NULL, 0); } printf("len = %x\\n", len); i2c_write_bytes(fd, address, buf, len); break; case \'r\': printf("optarg = %s\\n", optarg); printf("optind = %d\\n", optind); printf("argv[optind] = %s\\n", argv[optind]); address = (unsigned char)strtol(optarg, NULL, 0); printf("address = 0x%x\\n", address); len = (unsigned short)strtol(argv[optind], NULL, 0); printf("len = 0x%x\\n", len); i2c_read_bytes(fd, address, buf, len); printf("Read content:\\n"); for(i = 0; i < len; i++) { printf("0x%x ", buf[i]); } printf("\\n"); break; default: printf("Invalid parameter\\n"); printf(USAGE); break; } } Exit: close(fd); return 0; }
以上是关于Linux I2C驱动--用户态驱动简单示例的主要内容,如果未能解决你的问题,请参考以下文章