1. I2C 框架结构
1.1 I2C 硬件框架
I2C 总线拓扑图
⚫ 在一个芯片 (SoC) 内部,有一个或多个 I2C 控制器
⚫ 在一个 I2C 控制器上,可以连接一个或多个 I2C 设备
⚫ I2C 总线只需要 2 条线:时钟线 SCL 、数据线 SDA
⚫ 在 I2C 总线的 SCL 、 SDA 线上,都有上拉电阻
1.2 I2C 软件框架
以 I2C 接口的存储设备 AT24C02 为例:
⚫ APP :
◼ 提出要求:把字符串 "www.100ask.net" 写入 AT24C02 地址 16 开始的地方
◼ 它是大爷,不关心底层实现的细节
◼ 它只需要调用设备驱动程序提供的接口
⚫ AT24C02 驱动:
◼ 它知道 AT24C02 要求的地址、数据格式
◼ 它知道发出什么信号才能让 AT24C02 执行擦除、烧写工作
◼ 它知道怎么判断数据是否烧写成功
◼ 它构造好一系列的数据,发给 I2C 控制器
⚫ I2C 控制器驱动
◼ 它根据 I2C 协议发出各类信号: I2C 设备地址、 I2C 存储地址、数据
◼ 它根据 I2C 协议判断
1.3 我们讲什么
1.3.1 对于 Linux I2C 结构
从上到下:
⚫ 先讲 I2C 协议
⚫ APP 可以通过两类驱动程序访问设备
◼ I2C 设备自己的驱动程序
◼ 内核自带的 i2c-dev.c 驱动程序,它是 i2c 控制器驱动程序暴露给用户空间的驱动程序(i2c-dev.c)
⚫ I2C Device Driver
◼ I2C 设备自己的驱动程序
◼ 内核自带的 i2c-dev.c 驱动程序,它是 i2c 控制器驱动程序暴露给用户空间的驱动程序(i2c-dev.c)
⚫ I2C Controller Driver
◼ 芯片 I2C 控制器的驱动程序 ( 称为 adapter)
◼ 使用 GPIO 模拟的 I2C 控制器驱动程序 (i2c-gpio.c)
1.3.2 对于单片机/裸机
从上到下:
⚫ 先讲 I2C 协议
⚫ APP
⚫ I2C Device Driver
⚫ I2C Controller Driver( 也被称为 adapter)
2 I2C 协议
2.1 硬件连接
I2C 在硬件上的接法如下所示,主控芯片引出两条线 SCL,SDA 线,在一条 I2C 总线上可以接很多 I2C 设备,我们还会放一个上拉电阻(放一个上拉电阻的原因以后我们再说)。
2.2 传输数据类比
怎么通过 I2C 传输数据,我们需要把数据从主设备发送到从设备上去,也需要把数据从从设备传送到主设备上去,数据涉及到双向传输。
举个例子:
体育老师:可以把球发给学生,也可以把球从学生中接过来。
⚫ 发球:
◼ 老师:开始了 (start)
◼ 老师: A !我要发球给你! ( 地址 / 方向 )
◼ 学生 A :到! ( 回应 )
◼ 老师把球发出去(传输)
◼ A 收到球之后,应该告诉老师一声(回应)
◼ 老师:结束(停止)
⚫ 接球:
◼ 老师:开始了 (start)
◼ 老师: B !把球发给我! ( 地址 / 方向 )
◼ 学生 B :到!
◼ B 把球发给老师(传输)
◼ 老师收到球之后,给 B 说一声,表示收到球了(回应)
◼ 老师:结束(停止)
我们就使用这个简单的例子,来解释一下 IIC 的传输协议:
⚫ 老师说开始了,表示开始信号 (start)
⚫ 老师提醒某个学生要发球,表示发送地址和方向 (address/read/write)
⚫ 老师发球 / 接球,表示数据的传输
⚫ 收到球要回应:回应信号 (ACK)
⚫ 老师说结束,表示 IIC 传输结束 (P)
2.3 IIC 传输数据的格式
2.3.1 写操作
⚫ 主芯片要发出一个 start 信号
⚫ 然后发出一个设备地址 ( 用来确定是往哪一个芯片写数据 ) ,方向 ( 读 / 写, 0 表示写,1 表示读 )
⚫ 从设备回应 ( 用来确定这个设备是否存在 ) ,然后就可以传输数据
⚫ 主设备发送一个字节数据给从设备,并等待回应
⚫ 每传输一字节数据,接收方要有一个回应信号(确定数据是否接受完成 ) ,然后再传输下一个数据。
⚫ 数据发送完之后,主芯片就会发送一个停止信号。
⚫ 下图:白色背景表示 " 主 → 从 " ,灰色背景表示 " 从 → 主 "
2.3.2 读操作
流程如下:
⚫ 主芯片要发出一个 start 信号
⚫ 然后发出一个设备地址 ( 用来确定是往哪一个芯片写数据 ) ,方向 ( 读 / 写, 0 表示写,1 表示读 )
⚫ 从设备回应 ( 用来确定这个设备是否存在 ) ,然后就可以传输数据
⚫ 从设备发送一个字节数据给主设备,并等待回应
⚫ 每传输一字节数据,接收方要有一个回应信号(确定数据是否接受完成 ) ,然后再传输下一个数据。
⚫ 数据发送完之后,主芯片就会发送一个停止信号。
下图:白色背景表示 " 主 → 从 " ,灰色背景表示 " 从 → 主 "
3 I2C 信号
I2C 协议中数据传输的单位是字节,也就是 8 位。但是要用到 9 个时钟:前面 8 个时钟用来传输 8 数据,第 9 个时钟用来传输回应信号。传输时,先传输最高位(MSB) 。
⚫ 开始信号( S ): SCL 为高电平时, SDA 由 高电平向低电平跳变,开始传送数据。
⚫ 结束信号( P ): SCL 为高电平时, SDA 由低电平向高电平跳变,结束传送数据。
⚫ 响应信号 (ACK) :接收器在接收到 8 位数据后,在第 9 个时钟周期,(从设备)拉低 SDA
⚫ SDA 上传输的数据必须在 SCL 为高电平期间保持稳定, SDA 上的数据只能在SCL 为低电平期间变化
I2C 协议信号如下(前半部分数据是发送地址位和方向,后半部分是数据,发送原理一样):
在 SCL 为高电平时,SDA 拉高,读取到 1;在SCL为高电平时,SDA拉低,读取到0。
4 协议细节
⚫ 如何在 SDA 上实现双向传输?
◼ 主芯片通过一根 SDA 线既可以把数据发给从设备,也可以从 SDA 上读取数据,连接 SDA 线的引脚里面必然有两个引脚(发送引脚 / 接受引脚)。
⚫ 主、从设备都可以通过 SDA 发送数据,肯定不能同时发送数据,怎么错开时间?在 9 个时钟里:
◼ 前 8 个时钟由主设备发送数据的话,第 9 个时钟就由从设备发送数据;
◼ 前 8 个时钟由从设备发送数据的话,第 9 个时钟就由主设备发送数据。
⚫ 双方设备中,某个设备发送数据时,另一方怎样才能不影响 SDA 上的数据?
◼ 设备的 SDA 中有一个三极管,使用开极 / 开漏电路 ( 三极管是开极, CMOS 管是开漏,作用一样) ,如下图: