一、 I2C简单介绍
I2C(Inter-Integrated Circuit)总线是一种由 Philips 公司开发的两线式串行总线,用于连接微控制器及其外围设备。I2C 总线最基本的长处就是简单性和有效性,简单体如今接线简单,仅仅有两根线数据线(SCL)和时钟线(SDA),并且 控制简单。所以一些封装较小的器件多使用I2C总线,常见的使用I2C总线的设备有EEPROM、RTC及一些传感器。这里我们介绍下基于linux的I2C设备驱动的编写。
- I2C设备驱动的编写有多种方式
一种是直接操作CPU的I2C控制器,正对于某一个设备写一个字符驱动,这样的驱动相对来说比較直接,不须要太依赖于内核相关配置,可是这类设备驱动依赖CPU,可移植性较差。
一种是基于linux内核I2C子系统完毕设备驱动的编写,一般内核会继承相关CPU的控制器驱动即使没有也能够通过技术支持能够获得,所以我们仅仅须要使用linux下I2C子系统提供的相关接口来构建我们的设备驱动即可了。这样我们的设备驱动并不依赖于某一个特定的CPU,可移植性较好。- I2C总线工作原理
I2C 总线是由数据线 SDA 和时钟 SCL 构成的串行总线,各种被控制器件均
并联在这条总线上,每一个器件都有一个唯一的地址识别,能够作为总线上的一个
发送器件或接收器件 (详细由器件的功能决定 )。 I2C 总线的接口电路结构如图 1 所看到的- I2C 总线的几种信号状态
1. 空暇状态:SDA 和 SCL 都为高电平。
2. 開始条件(S):SCL 为高电平时,SDA 由高电平向低电平跳变,開始传送数据。
3. 结束条件(P):SCL 为高电平时,SDA 由低电平向高电平跳变,结束传送数据。
4. 数据有效:在 SCL 的高电平期间,SDA 保持稳定,数据有效。SDA 的改变仅仅能发生在 SCL 的低电平期间。
5. ACK 信号:传输数据的过程中,接收器件每接收一个字节数据要产生一个 ACK 信号,向发送器件发出特定的低电平脉冲,表示已经收到数据。
- I2C 总线基本操作
I2C 总线必须由主器件(通常为微控制器)控制,主器件产生串行时钟(SCL),同一时候控制总线的传输方向,并产生開始和停止条件。
传输数据中,首先由主器件产生開始条件,随后是器件的控制字节(前七位是从器件的地址,最后一位为读写位)。接下来是读写操作的数据,以及 ACK响应信号。传输数据结束时,主器件产生停止条件。详细的过程如图 2 所看到的。
- I2C驱动层次结构
- I2C 核心(I2C core)
- I2C 控制器驱动(I2C adapter)
- I2C 设备驱动(I2C driver)
- I2C 驱动源代码结构
- I2C 设备驱动一般结构
- 经常使用数据结构解析
struct module *owner; /* 所属模块 */unsigned int id; /* algorithm 的类型,定义于 i2c-id.h,以 I2C_ALGO_開始 */unsigned int class;const struct i2c_algorithm *algo; /* 总线通信方法结构体指针 */void *algo_data; /* algorithm 数据 */struct rt_mutex bus_lock;int timeout; /* 超时时间,以 jiffies 为单位 */int retries; /* 重试次数 */struct device dev; /* 控制器设备 */int nr;char name[48]; /* 控制器名称 */struct completion dev_released; /* 用于同步 */struct mutex userspace_clients_lock;struct list_head userspace_clients;
/* I2C 传输函数指针 */int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs, int num);/* smbus 传输函数指针 */int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr, unsigned short flags, char read_write, u8 command, int size, union i2c_smbus_data *data);/* 返回控制器支持的功能 */u32 (*functionality) (struct i2c_adapter *);
__u16 addr; /* 从设备地址 */__u16 flags; /* 消息类型 */__u16 len; /* 消息长度 */__u8 *buf; /* 消息数据 */
unsigned short flags; /* 标志 */unsigned short addr; /* 低 7 位的芯片地址 */char name[I2C_NAME_SIZE]; /* 设备名称 */struct i2c_adapter *adapter; /* 依附的 i2c_adapter */struct i2c_driver *driver; /* 依附的 i2c_driver */struct device dev;int irq; /* 设备使用的中断号 */struct list_head detected;
unsigned int class;int (*attach_adapter)(struct i2c_adapter *); /* 依附 i2c_adapter 函数指针 */int (*detach_adapter)(struct i2c_adapter *); /* 脱离 i2c_adapter 函数指针 */int (*probe)(struct i2c_client *, const struct i2c_device_id *);int (*remove)(struct i2c_client *);void (*shutdown)(struct i2c_client *);int (*suspend)(struct i2c_client *, pm_message_t mesg);int (*resume)(struct i2c_client *);void (*alert)(struct i2c_client *, unsigned int data);int (*command)(struct i2c_client *client, unsigned int cmd, void *arg);struct device_driver driver;const struct i2c_device_id *id_table; /* 该驱动所支持的设备 ID 表 */int (*detect)(struct i2c_client *, struct i2c_board_info *); /* 设备探測函数 */const unsigned short *address_list; /* 驱动支持的设备地址 */struct list_head clients; /* 挂接探測到的支持的设备 */