IIC协议——i2c-dev的使用

Posted Shemesz

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了IIC协议——i2c-dev的使用相关的知识,希望对你有一定的参考价值。


   在Linux内核代码文件/include/linux/i2c-dev.c中针对每个适配器生成一个主设备号为89的设备节点,实现了文件操作接口,用户空间可以通过i2c设备节点访问i2c适配器。适配器的编号从0开始,和适配器的设备节点的次设备号相同。
  i2c适配器的设备节点是/dev/i2c-x,其中x是数字,代表适配器的编号。由于适配器编号是动态分配的(和注册次序有关),所以想了解哪一个适配器对应什么编号,可以查看/sys/class/i2c-dev/目录下的文件内容。

(1)前期准备

  • 通过代码操控i2c适配器,必须包含以下头文件
#include<linux/i2c-dev.h>
#include<linux/i2c.h>
  • 查看设备节点
    因为适配器编号不固定,所以需要查看具体设备名称
pi@raspberrypi:~ $ cat /sys/class/i2c-dev/i2c-0/name
pi@raspberrypi:~ $ cat /sys/class/i2c-dev/i2c-1/name
pi@raspberrypi:~ $ cat /sys/class/i2c-dev/i2c-2/name
  • 打开设备节点/dev/i2c-1
fd = open(/dev/i2c-1,O_RDWR) //可以打印fd的值,查看是否返回正确

(2)ioctl()控制

查看include/linux/i2c-dev.h文件,可以看到i2c-dev支持的IOCTL命令

i2c-dev IOCTL命令

#define I2C_RETRIES                 0x0701                                     /*设置收不到ACK时的重试次数*/
#define I2C_TIMEOUT                 0x0702                                   /* 设置超时时限的jiffies*/
#define I2C_SLAVE                   0x0703                                   /*设置从机地址*/
#define I2C_SLAVE_FORCE             0x0706                                   /* 强制设置从机地址*/
#define I2C_TENBIT                  0x0704                                   /*选择地址位长:=0 for 7bit , != 0 for 10 bit*/
#define I2C_FUNCS                   0x0705                                   /*获取适配器支持的功能*/
#define I2C_RDWR                    0x0707                                   /*Combined R/W transfer (one STOP only)*/
#define I2C_PEC                     0x0708                                   /* != 0 to use PEC with SMBus*/
#define I2C_SMBUS                   0x0720                                   /*SMBus transfer*/

1. 设置重试次数

ioctl(fd, I2C_RETRIES,m); 
设置适配器收不到ACK时重试的次数为m。默认的重试次数为1

2. 设置超时

ioctl(fd, I2C_TIMEOUT,m); 
设置SMBus的超时时间为m,单位为jiffies。

3. 设置从机地址

ioctl(fd, I2C_SLAVE,addr);

ioctl(fd, #defineI2C_SLAVE_FORCE, addr);

在调用read()write()函数之前必须设置从机地址。
这两行都可以设置从机的地址,区别是:
第一行则只在该地址空闲的情况下成功。由于i2c-dev创建的i2c_client不加入i2c_adapter的client列表,所以不能防止其它线程使用同一地址,也不能防止驱动模块占用同一地址。
第二行无论内核中是否已有驱动在使用这个地址都会成功;

4. 设置地址模式

ioctl(fd,I2C_TENBIT,select)

如果select不等于0选择10比特地址模式,如果等于0选择7比特模式,默认7比特。只有适配器支持I2C_FUNC_10BIT_ADDR,这个请求才是有效的。

5. 获取适配器功能

ioctl(fd,I2C_FUNCS,(unsignedlong *)funcs)

获取的适配器功能保存在funcs中,如下:
/* include/linux/i2c.h */
#define I2C_FUNC_I2C                      0x00000001                                          
#define I2C_FUNC_10BIT_ADDR               0x00000002                     
#define I2C_FUNC_PROTOCOL_MANGLING        0x00000004/*I2C_M_{REV_DIR_ADDR,NOSTART,..}*/
#define I2C_FUNC_SMBUS_PEC                0x00000008
#define I2C_FUNC_SMBUS_BLOCK_PROC_CALL    0x00008000 /* SMBus 2.0                                       */
#define I2C_FUNC_SMBUS_QUICK              0x00010000
#define I2C_FUNC_SMBUS_READ_BYTE          0x00020000
#define I2C_FUNC_SMBUS_WRITE_BYTE         0x00040000
#define I2C_FUNC_SMBUS_READ_BYTE_DATA     0x00080000
#define I2C_FUNC_SMBUS_WRITE_BYTE_DATA    0x00100000
#define I2C_FUNC_SMBUS_READ_WORD_DATA     0x00200000
#define I2C_FUNC_SMBUS_WRITE_WORD_DATA    0x00400000
#define I2C_FUNC_SMBUS_PROC_CALL          0x00800000
#define I2C_FUNC_SMBUS_READ_BLOCK_DATA    0x01000000
#define I2C_FUNC_SMBUS_WRITE_BLOCK_DATA   0x02000000
#define I2C_FUNC_SMBUS_READ_I2C_BLOCK     0x04000000/* I2C-like block xfer                           */
#define I2C_FUNC_SMBUS_WRITE_I2C_BLOCK    0x08000000 /* w/ 1-byte reg. addr.                           */
#define I2C_FUNC_SMBUS_READ_I2C_BLOCK_2   0x10000000 /* I2C-like block xfer                           */
#define I2C_FUNC_SMBUS_WRITE_I2C_BLOCK_2  0x20000000 /* w/ 2-byte reg. addr.                           */

6. I2C层通信
这一行代码可以使用I2C协议和设备进行通信。它进行连续的读写,中间没有间歇。只有当适配器支持I2C_FUNC_I2C此命令才有效。

ioctl(fd,I2C_RDWR,(struct i2c_rdwr_ioctl_data *)msgset);

参数:指向 struct i2c_rdwr_ioctl_data类型的结构体指针

  • i2c_rdwr_ioctl_data
    msgs[] 数组成员包含了指向各自缓冲区的指针。这个函数会根据是否在消息中的flags置位I2C_M_RD来对缓冲区进行读写。从机的地址以及是否使用10比特地址模式记录在每个消息中,忽略之前ioctl设置的结果。
struct i2c_rdwr_ioctl_data {
    struct i2c_msg *msgs;   /* 指向i2c_msgs数组 */
    __u32 nmsgs;            /* 消息的个数 */
};
  • i2c_msgs
struct i2c_msg {  
    __u16 addr;     // 从机地址  
    __u16 flags;    // 读/写(1/0)标志  
    __u16 len;      // 数据长度  
    __u8 *buf;      // 数据指针  
};  

7. 设置SMBus PEC

ioctl(fd,I2C_PEC,(long )select);
  • 如果select不等于0选择SMBus PEC (packet error checking),等于零则关闭这个功能,默认是关闭的。

  • 这个命令只对SMBus传输有效。这个请求只在适配器支持I2C_FUNC_SMBUS_PEC时有效;如果不支持这个命令也是安全的,它不做任何工作。

8. SMBus通信
这个函数和I2C_RDWR类似,参数的指针指向i2c_smbus_ioctl_data类型的变量。

ioctl(fd, I2C_SMBUS, (i2c_smbus_ioctl_data*)msgset);
  • i2c_smbus_ioctl_data
struct i2c_smbus_ioctl_data {
    __u8 read_write;
    __u8 command;
    __u32 size;
    union i2c_smbus_data *data;
};

更多例程请点击了解:《用户空间使用i2c-dev》

以上是关于IIC协议——i2c-dev的使用的主要内容,如果未能解决你的问题,请参考以下文章

荔枝派 V3S Linux 应用层 IIC 使用OLED

[国嵌攻略][155][I2C用户态驱动设计]

Arduino IIC协议笔记

基于STM8的IIC协议--实例篇--时钟模块(DS3231)读取

通信协议:IIC协议的原理和模拟IIC的实现

IIC协议上手