Linux应用层的i2c读写

i2c通道在开发过程中使用是非常的方便的,主要是由于其简单的结构,i2c主要有SCL,SDA两条通道,一个时钟线,一个数据线,这也是i2c之所以应用如此普遍的原因。i2c的时序如下:

实现上述时序,便可以轻松通过i2c读写寄存器了,接下来看看i2c下在Linux应用层中实现读写,主要是通过O_RDWR IOTCL读写i2c设备,写函数如下:

static int iic_write(int i2c_fd, unsigned int reg_address ,unsigned int reg_val)
{
struct i2c_rdwr_ioctl_data work_queue;
int ret;

work_queue.nmsgs = 2;
work_queue.msgs = (struct i2c_msg *)malloc(work_queue.nmsgs * sizeof(struct i2c_msg));
if(!work_queue.msgs)
{
printf("msgs memery alloc error\n");
close(i2c_fd);
return 0;
}
if ((work_queue.msgs[0].buf = (unsigned char *)malloc(2 * sizeof(unsigned char))) == NULL)
{
printf("buf memery alloc error...\n");
close(i2c_fd);
return 0;
}


(work_queue.msgs[0]).len = 2;
(work_queue.msgs[0]).flags = 0;
(work_queue.msgs[0]).addr = slave_address;
(work_queue.msgs[0]).buf[0] = reg_address;
(work_queue.msgs[0]).buf[1] = reg_val;


work_queue.nmsgs = 1;


ioctl(i2c_fd, I2C_RDWR, (unsigned long) &work_queue);
if(ret < 0)
{
printf("Error during I2C_RDWR ioctl with error code: %d\n", ret);
return 0;
}
free(work_queue.msgs[0].buf);
free(work_queue.msgs);

}

上述贴出的代码分析 :

i2c_rdwr_ioctl_data结构体的源码如下

struct i2c_rdwr_ioctl_data {   
 /* pointers to i2c_msgs                 */ 
   struct i2c_msg __user *msgs;   
 /* number of i2c_msgs                   */
    __u32 nmsgs;
};
这个结构中 nmsgs是指要发送的消息的数量,其次是i2c_msg,这个才是i2c设备的核心结构体,在内核中源码如下
struct i2c_msg
{
unsigned short addr;   //slave IIC device addr 
unsigned short flags; //write read flags 
#define I2C_M_TEN0x0010/* this is a ten bit chip address */
#define I2C_M_RD0x0001/* read data, from slave to master */
#define I2C_M_NOSTART0x4000/* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_REV_DIR_ADDR0x2000/* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_IGNORE_NAK0x1000/* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_NO_RD_ACK0x0800/* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_RECV_LEN0x0400/* length will be first received byte */


unsigned short len;    //写入或者读出数据的个数(字节)
unsigned char *buf;    //写入或者读出数据的地址 buf[0] 
};
在这个结构体中可以清楚的看到i2c的各个参数带表的意思,根据下面的程序可以更好的理解。
我在上述的代码中发送了2条消息,但是细心的朋友应该会注意到代码的 work_queue.nmsgs = 1;这条信息,这是因为实际上我在写函数中只写了一条消息,实际我只需要给一个寄存器只写一条信息即可,已经可以实现我的寄存器写入功能,之所以写着是因为当时在调试过程中我首先是去读寄存器的,读一个寄存器之少需要两条消息,从datasheet可知,i2c读信号如下图所示

 
i2c的实际地址是0x1c,通过 (0x1c<<1) | 0 得到i2c写地址, (0x1c<<1) | 1 得到i2c读地址。在读i2c主设备模式下ST是指开始位,Device Address是指传感器设备地址及0x1c,w写标志位,后面紧跟着寄存器地址,SR是指重写启动i2c地址,所以读i2c至少要两条消息,我在写消息的时候为了保持读写消息的一致,也将写函数中写了两条消息,在运行过程中导致系统内核崩溃。为了解决这个问题,只要在i2c下发消息前,重新给发送的消息赋值即可。接下来实现i2c的读函数,在调试过程中一旦实现了i2c的驱动,首先应该实现的是读函数。

i2c的读函数如下:

static int iic_read(int i2c_fd, unsigned int reg_address)
{
struct i2c_rdwr_ioctl_data work_queue;
unsigned char val;
int ret;

work_queue.nmsgs = 2;
work_queue.msgs = (struct i2c_msg *)malloc(work_queue.nmsgs *sizeof(struct i2c_msg));


if(!work_queue.msgs)
{
printf("Memery alloc error\n");
close(i2c_fd);
return 0;
}


val =(unsigned char)reg_address;

(work_queue.msgs[0]).len = 1;
(work_queue.msgs[0]).flags = 0;
(work_queue.msgs[0]).addr = slave_address;
(work_queue.msgs[0]).buf = &val;

(work_queue.msgs[1]).len = 1;
(work_queue.msgs[1]).flags = 1;
(work_queue.msgs[1]).addr = slave_address;
(work_queue.msgs[1]).buf = &val;


ret = ioctl(i2c_fd, I2C_RDWR, (unsigned long) &work_queue);
if(ret < 0)
{
printf("Error during I2C_RDWR ioctl with error code: %d\n", ret);
return 0;
}



free(work_queue.msgs);

return val;
}

这样就可以直接调用i2c读写函数实现对传感器寄存器的操作了。最后在说明一点,千万不要忘记给i2c_rdwr_ioctl_data结构体中的最重要最重要最重要的结构i2c_msg中的buf分配内存。




你可能感兴趣的:(i2c)