目录:
1 通讯协议概述
1.1 总线特点
1.2 总线结构
1.3 总线协议
2 时序关系
3 寄存器操作流程
4 I2C设备驱动框架
4.1 I2C设备层
4.1.2 I2C设备
4.1.2 I2C设备驱动
4.1.3 I2C设备和驱动的关系
4.2 I2C总线层
4.2.1 总线适配器
4.2.2 适配器驱动
4.3 设备层与驱动层关系
5 I2C设备驱动的书写
5.1 I2C设备驱动书写流程
正文:
1 通讯协议概述
I2C是Philips公司开发的,支持一个主机(master)连多个从机(Slave),且主从可切换的两线式串行总线通讯协议。
设备连接到总线的方式: 开漏(OC门)或集电级开路的“线与”逻辑,空闲时由上拉电阻维持sda,scl的高电平状态。
1.1 总线特点:
(1).每一个连接到总线上的设备都可以通过唯一的设备地址进行访问.
(2).串行的8位双向数据传输,位速率在标准模式下可达100kb/s,在快速模式下可达400kb/s;高速模式下可以达到3.4M每秒.
(3).总线长度最长7.6米左右.
(4).片上滤波器可以增加抗干扰能力,保证数据完成传输.
(5).连接到一条I2C总线上的设备数量只受到最大电容400pf的限制.
(6).多主机系统,通过冲突检测和延时等待来保护数据不被破坏.
1.2 总线结构:
1.3 总线协议:
2 时序关系
(1) 空闲状态:SCL时钟线,SDA数据线 均为高电平
(2) 开始判断:SCL保持高电平时,SDA从高电平向低电平切换 ---主设备产生
(3) 结束判断:SCL保持高电平时,SDA从低电平向高电平切换 ---主设备产生
(4) 数据变化:SDA必须在SCL的高电平周期保持稳定,在SCL的低电平周期才可改变电平.发送的数据位的顺序,是从高位开始(MSB),还是从低
位开始(LSB).
(5) 应答机制:在ACK应答确认周期时, 主机释放SDA,从机把SDA拉低,并在SCLK高电平时保持住. 如果从机来不及处理,可以一直拉低SDA,使
得主机等待.
(6) 总线仲裁(争抢):当多个I2C设备同时拉低SDA,争抢主机所有权时。可通过后续它们发送的Slave address 做为判断.
第一次发送地址位为低电平的设备获得主机所有权.
3 寄存器操作流程:
(1) Master发送I2C addr(7bit)和rw操作1(1bit),等待ACK
(2) Slave发送ACK
(3) Master发送reg addr(8bit),等待ACK
(4) Slave发送ACK
(5) Master发起START
(6) Master发送I2C addr(7bit)和r操作1(1bit),等待ACK
(7) Slave发送ACK
(8) Slave发送data(8bit),即寄存器里的值
(9) Master发送ACK
(10) 第8步和第9步可以重复多次,即顺序读多个寄存器
4 I2C设备驱动框架:
I2C设备驱动可以分为: 设备层,总线层。
I2C设备驱动程序相关数据结构:i2c_driver(设备层),i2c_client(设备层),i2c_algorithm(总线层),i2c_adapter(总线层).
注意:一个系统中可以有多个总线层,也就是包含多个总线控制器,也可以由多个设备层,包含不同的i2c设备.
4.1 I2C设备层:
I2C设备层由I2C设备和驱动程序组成。
4.1.1 I2C设备:
一个I2C设备由一个i2c_client数据结构进行描述。
-
--/include/linux/i2c.h
-
-
struct i2c_client {
-
unsigned short flags; /* 标志位:I2C_CLIENT_TEN 表示设备使用10位芯片地址
I2C_CLIENT_PEC 表示设备使用SMBus Packet Error Checking */
-
unsigned short addr; /* 设备地址,低7位存储芯片地址 */
-
-
char name[I2C_NAME_SIZE]; /* 设备名称 */
-
struct i2c_adapter *adapter; /* 依附的适配器,适配器指明所属总线 */
-
struct i2c_driver *driver; /* 设备驱动程序指针 */
-
struct device dev; /* 设备结构体 */
-
int irq; /* 设备申请的中断号 */
-
struct list_head detected; /* 已被发现的设备链表 */
-
}
设备地址:
7---------------------------------------------------------------------------0
| R/W'(1 bit) | 器件类型(4 bit) | 自定义地址(3 bit) |
----------------------------------------------------------------------------
(1) 由于rw的不同,所以i2c设备存在读地址、写地址两个地址。
(2) 器件类型已经由器件厂商固化,自定义地址通常由引脚电平来确定,因此同一i2c总线上相同类型的设备最多挂8个。
(3) 如果在两条不同的i2c总线上挂接来两块类型和地址相同的芯片,那么这两块芯片的地址是相同的,这显然时冲突的,解决办法是为总线适配器指定一个id,那么新的芯片地址就是总线适配器的id和设备地址两部分组成。
4.1.2 I2C设备驱动:
I2C设备驱动使用i2c_driver进行描述。
-
--/include/linux/i2c.h
-
-
struct i2c_driver {
-
-
unsigned int class; /* 驱动类型 */
-
-
int (*attach_adapter)(struct i2c_adapter *); /* 检测到适配器时调用的函数 */
-
int (*detach_adapter)(struct 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); /* 设备命令控制,类似ioctl */
-
-
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; /* 指向驱动支持的设备 */
-
};
说明:
若设备不支持probe、remove则需实现attach_* 函数。 两者只选一个,否则出现“i2c-core:driver driver-name is confused”的警告。
4.1.3 I2C设备和驱动的关系:
通过指针互联。
I2C client I2C client I2C client
4.2 I2C总线层:
I2C驱动层由总线适配器和适配器驱动组成。
4.2.1 总线适配器:
I2C总线适配器就是一个I2C总线控制器,在物理上连接若干个II设备。
I2C总线适配器本质上是一个物理设备,其主要功能是完成I2C总线控制器相关的数据通信。
-
--/include/linux/i2c.h
-
-
struct i2c_adapter {
-
struct module *owner; /* 模块计数 */
-
unsigned int id; /* alogorithm类型,在i2c_id.h中 */
-
unsigned int class; /* 允许探测驱动程序的类型 */
-
const struct i2c_algorithm *algo; /* 适配器驱动程序指针 */
-
void *algo_data; /* 适配器的私有数据 */
-
-
struct rt_mutex bus_lock; /* 对总线进行操作时,将获得总线锁 */
-
-
int timeout; /* 超时 in jiffies */
-
int retries; /* 超时次数 */
-
struct device dev; /* 适配器 */
-
-
int nr;
-
char name[48]; /* 适配器的名字 */
-
struct completion dev_released; /* 用于同步的完成量,表示适配器是否在被其他程序使用 */
-
-
struct list_head userspace_clients; /* 连接到总线上的设备的链表 */
-
};
4.2.2 适配器驱动:
-
--/include/linux/i2c.h
-
-
struct i2c_algorithm {
-
-
int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs, int num); /* 传输函数的指针,指向实现IIC总线通信协议的函数 */
-
int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr, /* smbus传输函数指针,指向smbus通信协议的函数 */
-
unsigned short flags, char read_write, /* smbus协议基于i2c总线协议,也是由2线制通线 */
-
u8 command, int size, union i2c_smbus_data *data); /* smbus和i2c可以通过软件方式兼容,但此处一般为空*/
-
-
u32 (*functionality) (struct i2c_adapter *); /* 返回适配器支持哪些传输类型 */
-
};
4.3 设备层与驱动层关系:
5 I2C设备驱动的书写
I2C驱动框架在内核中算是比较庞大和复杂的,内核为其建立的分层分工架构使得其移植性和健壮性得到保证。但面对内核如此庞大的代码量,我们首先应该弄清楚哪些工作由内核提供,哪些工作需要针对设备进行书写,即明白自己都该做些什么。
5.1 I2C设备驱动书写流程
在书写I2C驱动之前,应先了解驱动调用过程,以及再i2c子系统的构成。
i2c设备驱动流程:
-------------------
| xxx_init | i2c设备初始化
-------------------
|
V
-------------------------
| i2c_add_driver | i2c子系统中加入驱动
------------------------
|
V
-------------------------------
| xxx_attach_adapter | 适配器和驱动程序挂接
-------------------------------
|
V
--------------------------------
| i2c_probe | i2c设备探测函数
--------------------------------
|
V
---------------------------------
| xxx_detect | i2c设备侦测函数
---------------------------------
|
V
-----------------------------------
| i2c_client init | i2c设备初始化
-----------------------------------
|
V
----------------------------------
| i2c_attach_client | 将设备添加到i2c子系统
----------------------------------
|
V
----------------------------------
| xxx_init_client | 初始化i2c设备
----------------------------------