I2C协议
1. 概述
1.1 I2C的特征
- 它是一个支持设备的总线。“总线”指多个设备共用的信号线。在一个I2C 通讯总线中,可连接多个I2C 通讯设备,支持多个通讯主机及多个通讯从机。
- 一个I2C 总线只使用两条总线线路,一条双向串行数据线(SDA) ,一条串行时钟线(SCL)。数据线即用来表示数据,时钟线用于数据收发同步。
- 每个连接到总线的设备都有一个独立的地址,主机可以利用这个地址进行不同设备之间的访问。
- 总线通过上拉电阻接到电源。当I2C 设备空闲时,会输出高阻态,而当所有设备都空闲,都输出高阻态时,由上拉电阻把总线拉成高电平。
- 多个主机同时使用总线时,为了防止数据冲突,会利用仲裁方式决定由哪个设备占用总线。
- 具有三种传输模式:标准模式传输速率为100kbit/s ,快速模式为400kbit/s ,高速模式下可达3.4Mbit/s,但目前大多I2C 设备尚不支持高速模式。
- 连接到相同总线的IC 数量受到总线的最大电容400pF 限制。
1.2 引脚定义
# 3个I2C模块
I2C0:
SDA: PC.4、PC.14
SCL: PC.5、PC.13
I2C1:
SDA: PA.3、PE.10
SCL: PA.2、PE.9
I2C2:
SDA: PB.5、PE.13
SCL: PH.5、PE.12
1.3 I2C优缺点
1.由于只需要两根电线,I2C 非常适合有许多设备连接在总线上的电路板。这有助于降低成本和电路的复杂性作为额外的设备添加到系统。
2.由于只有两条连接,因此处理寻址和确认的开销会更加复杂。这在简单的配置中可能效率低下,可能首选 SPI 这样的直接链接接口。
1.4 术语描述
术语 | 描述 |
---|---|
发送器 | 发送数据到总线的器件 |
接收器 | 从总线接收数据的器件 |
主机 | 初始化发送产生时钟信号和终止发送的器件 |
从机 | 被主机寻址的器件 |
多主机 | 同时有多于一个主机尝试控制总线但不破坏报文 |
仲裁 | 是一个在有多个主机同时尝试控制总线但只允许其中一个控制总线并使报文不被破坏的过程 |
同步 | 两个或多个器件同步时钟信号的过程 |
1.5 总体特征
SDA 和 SCL 都是双向线路,都通过一个电流源或上拉电阻连接到正的电源电压,当总线空闲时这两条线路都是高电平连接到总线的器件输
出级必须是漏极开路或集电极开路才能执行线与的功能,I2C 总线上数据的传输速率在标准模式下可达100kbit/s 在快速模式下可400kbit/s,
在高速模式下可达3.4Mbit/s。连接到总线的接口数量只由总线电容是400pF 的限制决定。
2.数据传输
2.1 起始信号和停止信号
起始信号:SCL处于高电平时,SDA由高到低变化,这种信号是起始信号。
停止信号:SCL处于高电平时,SDA由低到高变化,这种信号是停止信号。
2.2 数据有效性
I2C协议对数据的采样发生在SCL高电平期间,除了起始和停止信号,在数据传输期间,SCL为高电平时,SDA必须保持稳定,不允许改变,在SCL低电平时才可以进行变化。
2.3 应答信号与非应答信号
应答信号出现在1个字节传输完成之后,即第9个SCL时钟周期内,此时主机需要释放SDA总线,把总线控制权交给从机
如果总线之前为高电平,从机控制总线从高电平到低电平,则为应答信号
如果总线之前为高电平,从机控制总线持续高电平,则为非应答信号
2.4 发送SLA+W
2.5 上拉电阻
I2C 两条总线为漏极开路结构,支持线与,需要外接上拉电阻
2.5.1 开漏输出情况
I2C为什么要用开漏输出?
我们先来谈谈为什么不能用普通的push-pull,而是一定要用open drain,所谓的push-pull就是由一个PMOS和一个NMOS组成的。
1.当输出高电平时:PMOS打开NMOS关闭;
2.当输出低电平时:PMOS关闭NMOS打开。
如图A,此PMOS和NMOS交替打开和关闭,分别输出方波的高低电平,但是图B的open drain buffer则只有下面的NMOS:
1.当输出高电平时NMOS关闭,输出处于浮空的状态;
2.当输出低电平时NMOS打开即可。(注意Vin是芯片内部的控制逻辑,开漏输出的输入也是高阻的
如此我们看出,开漏输出的输出高一定要借助外力,这就是为什么我们检查I2C的设计时,一定要确保外部有上拉电阻才行。
2.5.2 推挽输出情况
上面第一张图已经解释了,I2C是有很多设备线与连接而成,如果采用push-pull的output buffer的话,难免会出现下面的情况,一个设备输出高,另外设备输出低,也就是左边设备的PMOS打开而右边设备的NMOS打开,这样就在VCC和GND之间形成短路,此时大的电流会把设备烧毁,后果是灾难性的。这种现象还有专门的英文名字叫bus contention。
所以I2C一定要用开漏输出加上拉电阻的方式,上拉电阻的选择电阻值的改变是需要精心计算的,不能太小也不能太大,而是要合理
1.取值太大,会导致两个后果,一是芯片接收端驱动能力不足,而是上升时间不够
2.取值太大,会导致VIL太大,芯片低电平下不来,极端的例子就是上拉电阻为0时,不能产生低电平
2.6 上拉电阻选择
2.6.1 最大值
Vr:即0.3VDD -> 0.7VDD的上升沿时间段
2.6.2 最小值
2.6.3 示例
2.7 信号波形
2.7.1 过冲
1.危害:
1.对器件冲击造成器件损坏。
2.形成干扰源,对其它器件造成串扰。
2.产生原因:
1.器件驱动能力太强。
2.没有匹配或者匹配不当。
3.其它相邻信号串扰。
3.解决办法:
1.选择合适的驱动能力。
2.增加电阻匹配。
3.PCB布线避开干扰源和耦合路径。
2.7.2 边沿过缓
1.危害
上升、下降沿缓慢发生在数据信号线(如串口信号线等)时,会造成数据采样错误
2.产生原因
驱动能力不够,或者负载过大(例如链路阻抗太大,走线太长,有较大寄生电容)
3.解决办法
1.提高驱动能力;涉及到I2C上拉电阻的选择
2.减小负载。–可以加I2C BUFFER
2.8 广播传输
如果I2C 地址寄存器中的呼叫地址确认位置位,从机也将响应广播呼叫地址(00H);否则将不响应广播呼叫地址。
呼叫地址确认位:当此位置1时,呼叫地址可以被识别,否则不能被识别。
2.9 负载电容
1.电容大的时候,电压的上升沿缓慢,会存在数据采样不正确的情况
2.总线的最大电容小于400pF,单个IC的电容小于10pF
2.10 总线仲裁
2.11 从机地址
3.传输模式
3.1 轮询模式
轮询指定是CPU不停的查询外设的状态,在一定的状态下进行相关的操作。
传输模式 | SDA | SCL | 备注 |
---|---|---|---|
主机发送模式 | 输出 | 输出 | 主机发送一系列数据到从机。一个开始条件(S),随后一个从机地址,(SLA)+写控制字(W),表示进入主机发送模式。 |
主机接收模式 | 输入 | 输出 | 主机接收模式中,主机从从机接收一系列数据。一个开始条件(S),随后一个从机地址,(SLA)+读控制字®表示进入主机接收模式。 |
从机接收模式 | 输入 | 输入 | 从机接收模式中,从机从主机接收一系列数据,进入从机模式前,需设置从机地址 |
从机发送模式 | 输出 | 输入 | 从机发送模式中,从机发送一系列数据到主机。进入从机模式前,需设置从机地址 |
3.2 中断模式
中断方式指的是在外设状态发生改变时,即有事件发生后,由中断程序来响应对应的事件。中断方式比轮询方式的优势在于可以减轻CPU的负担。
3.3 DMA传输
DMA 传输数据的方向有三个:从外设到存储器,从存储器到外设,从存储器到存储器。
DMA请求(当被使能时)仅用于数据传输。发送时数据寄存器变空或接收时数据寄存器变满,则产生DMA请求。DMA请求必须在当前字节传输结束之前被响应。当
为相应DMA通道设置的数据传输量已经完成时,DMA控制器发送传输结束信号到I2C接口,并且在中断允许时产生一个传输完成中断
4. 芯片验证
4.1 验证环境
4.2 验证流程
4.3 参数遍历
系统时钟源
APB1时钟频率
I2C速率
12C实例
GPIO管脚
上拉电阻
从机地址
数据內容
数据长度
4.4 Aardvark使用方式
1.aardvark适配器为发送器接口函数
# 1.设置比特率
Set Bitrate (aa_i2c_bitrate)
# 2.从 i2c 从设备读取字节流。
Master Read (aa_i2c_read)
# 3.从具有扩展状态信息的 i2c 从设备读取字节流
Master Read Extended (aa_i2c_read_ext)
# 4.向 i2c 从设备写入字节流。
Master Write (aa_i2c_write)
# 5.向具有扩展状态信息的 i2c 从设备写入字节流。
Master Write Extended (aa_i2c_write_ext)
# 6.向 i2c 从设备写入一个字节流,然后从同一个从设备读取。
Master Write-Read (aa_i2c_write_read)
2.aardvark适配器为接收器接口函数
# 1.启用 Aardvark 适配器作为 i2c 从设备。
Slave Enable (aa_i2c_slave_enable)
# 2.禁用 Aardvark 适配器作为 i2c 从设备。
Slave Disable (aa_i2c_slave_disable)
# 3.在 Aardvark 适配器进入从模式并由主服务器联系的情况下设置从服务器响应。
Slave Set Response (aa_i2c_slave_set_response)
# 4.返回从以前的 Aardvark i2c 从节写入到 i2c 主传输的字节数。
Slave Write Statistics (aa_i2c_slave_write_stats)
# 5.返回从以前的 Aardvark i2c 从服务器写入到具有扩展状态信息的 i2c 主传输的字节数。
Slave Write Statistics Extended (aa_i2c_slave_write_stats_ext)
# 6.从 i2c 从接收机读取字节。
Slave Read (aa_i2c_slave_read)
# 7.从具有扩展状态信息的 i2c 从接收机读取字节。
Slave Read Extended (aa_i2c_slave_read_ext)
3.使用方式
# 1.打开Aardvark端口
handle = aa_open(port)
if handle <= 0:
print("Unable to open Aardvark device on port %d" % port)
print("Error code = %d" % handle)
sys.exit()
# 2.激活/关闭各个子系统(i2c,SPI,gpio)
aa_configure(handle, AA_CONFIG_SPI_I2C)
# 3.启动/关闭 SCL 和 SDA 上的 i2c 上拉电阻器
aa_i2c_pullup(handle, AA_I2C_PULLUP_BOTH)
# Power the EEPROM using the Aardvark adapter's power supply.
# 4.激活/关闭目标电源插脚4和6
aa_target_power(handle, AA_TARGET_POWER_BOTH)
# 5.将 i2c 比特率设置为kHz
bitrate = aa_i2c_bitrate(handle, bitrate)
print("Bitrate set to %d kHz" % bitrate)
# Set the bus lock timeout
# 6.以毫秒为单位设置 i2c 总线锁定超时
bus_timeout = aa_i2c_bus_timeout(handle, BUS_TIMEOUT)
print("Bus lock timeout set to %d ms" % bus_timeout)
# Perform the operation
if command == "write":
_writeMemory(handle, device, addr, length, 0) # 7.向 i2c 从设备写入字节流 地址、字节长度、FLAG、指向数据的指针
print("Wrote to EEPROM")
elif command == "read":
_readMemory(handle, device, addr, length) # 8.从 i2c 从设备读取字节流 地址、字节长度、FLAG、指向数据的指针
信号线:
- SCL
- GND
- SDA
- NC/+5V
- MISO
- NC/+5V
- SCLK
- MOSI
- SS
- GND