CH32V003两线通信总线(I2C)介绍
内部集成电路总线(I2C)广泛用在微控制器和传感器及其他片外模块的通讯上,它本身支持多主多从模式,仅仅使用两根线(SDA 和 SCL)就能以 100KHz(标准)和 400KHz(快速)两种速度通讯。I2C 总线还兼容 SMBus 协议,不仅支持 I2C 的时序,还支持仲裁、定时和 DMA,拥有 CRC 校验功能。
主要特性
- 支持主模式和从模式
- 支持 7 位或 10 位地址
- 从设备支持双 7 位地址
- 支持两种速度模式:100KHz 和 400KHz
- 多种状态模式,多种错误标志
- 支持加长的时钟功能
- 2 个中断向量
- 支持 DMA
- 支持 PEC
- 兼容 SMBus
概述
I2C 是半双工的总线,它同时只能运行在下列四种模式中之一:主设备发送模式、主设备接收模式、从设备发送模式和从设备接收模式。I2C 模块默认工作在从模式,在产生起始条件后,会自动地切换到主模式,当仲裁丢失或产生停止信号后,会切换到从模式。I2C 模块支持多主机功能。工作在主模式时,I2C 模块会主动发出数据和地址。数据和地址都以 8 位为单位进行传输,高位在前,低位在后,在起始事件后的是一个字节(7 位地址模式下)或两个字节(10 位地址模式下)地址,主机每发送 8 位数据或地址,从机需要回复一个应答 ACK,即把 SDA 总线拉低,如下图所示。
I2C 时序图

为了正常使用必须给 I2C 输入正确的时钟,其中标准模式下,输入时钟最低为 2MHz,在快速模式下,输入时钟最低为 4MHz。
I2C 模块功能框图

主模式
主模式时,I2C 模块主导数据传输并输出时钟信号,数据传输以开始事件开始,以结束事件结束。使用主模式通讯的步骤为:
- 在控制寄存器 2(R16_I2Cx_CTLR2)和时钟控制寄存器(R16_I2Cx_CKCFGR)中设置正确的时钟;
- 在上升沿寄存器(R16_I2Cx_RTR)设置合适的上升沿;
- 在控制寄存器(R16_I2Cx_CTLR1)中置 PE 位启动外设; * 在控制寄存器(R16_I2Cx_CTLR1)中置 START 位,产生起始事件。在置 START 位后,I2C 模块会自动切换到主模式,MSL 位会置位,产生起始事件,在产生起始事件后,SB 位会置位,如果 ITEVTEN 位(在 R16_I2Cx_CTLR2)被置位,则会产生中断。此时应该读取状态寄存器 1 (R16_I2Cx_STAR1),写从地址到数据寄存器后,SB 位会自动清除;
如果是使用 10 位地址模式,那么写数据寄存器发送头序列(头序列为 11110xx0b,其中的 xx位是 10 位地址的最高两位)。在发送完头序列之后,状态寄存器的 ADD10 位会被置位,如果ITEVTEN 位已经置位,则会产生中断,此时应读取 R16_I2Cx_STAR1 寄存器后,写第二个地址字节到数据寄存器后,清除 ADD10 位。然后写数据寄存器发送第二个地址字节,在发送完第二个地址字节后,状态寄存器的 ADDR 位会被置位,如果 ITEVTEN 位已经置位,则会产生中断,此时应读取R16_I2Cx_STAR1 寄存器后再读一次 R16_I2Cx_STAR2 寄存器以清除 ADDR 位;如果使用的是 7 位地址模式,那么写数据寄存器发送地址字节,在发送完地址字节后,状态寄存器的 ADDR 位会被置位,如果 ITEVTEN 位已经置位,则会产生中断,此时应读取 R16_I2Cx_STAR1 寄存器后再读一次R16_I2Cx_STAR2 寄存器以清除 ADDR 位;在 7 位地址模式下,发送的第一个字节为地址字节,头 7位代表的是目标从设备地址,第 8位决定了后续报文的方向,0 代表是主设备写入数据到从设备,1代表是主设备向从设备读取信息。在 10 位地址模式下,如图 13-3 所示,在发送地址阶段,第一个字节为 11110xx0,xx 为 10 位地址的最高 2 位,第二个字节为 10 位地址的低 8 位。若后续进入主设备发送模式,则继续发送数据;若后续准备进入主设备接收模式,则需要重新发送一个起始条件,跟随发送一个字节为 11110xx1,然后进入主设备接收模式。
10 位地址时主机收发数据示意图

发送模式时,主设备内部的移位寄存器将数据从数据寄存器发送到 SDA 线上,当主设备接收到ACK 时,状态寄存器 1(R16_I2Cx_STAR1)的 TxE 被置位,如果 ITEVTEN 和 ITBUFEN 被置位,还会产生中断。向数据寄存器写入数据将会清除 TxE 位。如果 TxE 位被置位且上次发送数据之前没有新的数据被写入数据寄存器,那么 BTF 位会被置位,在其被清除之前,SCL 将保持低电平,读R16_I2Cx_STAR1 后,向数据寄存器写入数据将会清除 BTF 位。而在接收模式时,I2C 模块会从 SDA线接收数据,通过移位寄存器写进数据寄存器。在每个字节之后,如果 ACK 位被置位,那么 I2C 模块将会发出一个应答低电平,同时RxNE 位会被置位,如果 ITEVTEN和 ITBUFEN被置位,还会产生中断。如果RxNE被置位且在新的数据被接收前,原有的数据没有被读出,则BTF位将被置位,在清除BTF 之前,SCL 将保持低电平,读取 R16_I2Cx_STAR1 后,再读取数据寄存器将会清除 BTF 位。
主设备在结束发送数据时,会主动发一个结束事件,即置 STOP 位。在接收模式时,主设备需要在最后一个数据位的应答位置 NAK。注意,产生 NAK 后,I2C 模块将会切换至从模式。
从模式
从模式时,I2C 模块能识别它自己的地址和广播呼叫地址。软件能控制开启或禁止广播呼叫地址的识别。一旦检测到起始事件,I2C 模块将 SDA 的数据通过移位寄存器与自己的地址(位数取决于 ENDUAL 和 ADDMODE)或广播地址(ENGC 置位时)相比较,如果不匹配将会忽略,直到产生新的起始事件。如果与头序列相匹配,则会产生一个 ACK 信号并等待第二个字节的地址;如果第二字节的地址也匹配或 7 位地址情况下全段地址匹配,那么:首先产生一个 ACK 应答;ADDR 位被置位,如果ITEVTEN 位已经置位,那么还会产生相应的中断;如果使用的是双地址模式(ENDUAL 位被置位),还需要读取 DUALF 位来判断主机唤起的是哪一个地址。
从模式默认是接收模式,在接收的头序列的最后一位为 1,或 7 位地址最后一位为 1 时(取决于第一次接收到头序列还是普通的 7 位地址),I2C 模块将进入到发送器模式,TRA 位将指示当前是接收器还是发送器模式。
发送模式时,在清除 ADDR 位后,I2C 模块将字节从数据寄存器通过移位寄存器发送到 SDA 线上。在收到一个应答 ACK 后,TxE 位将被置位,如果设置了 ITEVTEN 和 ITBUFEN,还会产生一个中断。如果 TxE 被置位但在下一个数据发送结束前没有新的数据被写入数据寄存器时,BTF 位将被置位。在清除 BTF 前,SCL 将保持低电平,读取状态寄存器 1(R16_I2Cx_STAR1)后,再向数据寄存器写入数据将会清除 BTF 位。
接收模式时,在 ADDR 被清除后,I2C 模块将 SDA 上的数据通过移位寄存器存进数据寄存器,在每接收到一个字节后,I2C模块都会置一个ACK位,并置RxNE位,如果设置了ITEVTEN和ITBUFEN,还会产生一个中断。如果RxNE被置位,且在接收到新的数据前旧的数据没有被读出,那么BTF会被置位。在清除 BTF 位之前 SCL 会保持低电平。读取状态寄存器 1(R16_I2Cx_STAR1)并读取数据寄存器里的数据会清除 BTF 位。
当 I2C 模块检测到停止事件时,将置 STOPF 位,如果设置了 ITEVFEN 位,还会产生一个中断。用户需要读取状态寄存器(R16_I2Cx_STAR1)再写控制寄存器(比如复位控制字 SWRST)来清除。
错误
总线错误 BERR
在传输地址或数据期间,I2C 模块检测到外部的起始或停止事件时,将产生一个总线错误。产生总线错误时,BERR 位被置位,如果设置了 ITERREN 还会产生一个中断。在从模式下,数据被丢弃,硬件释放总线。如果是起始信号,硬件会认为是重启信号,开始等待地址或停止信号;如果是停止信号,则提前按正常的停止条件操作。在主模式下,硬件不会释放总线,同时不影响当前传输,由用户代码决定是否中止传输。
应答错误 AF
当 I2C 模块检测到一个字节后没有应答时,会产生应答错误。产生应答错误时:AF 会被置位,如果设置了 ITERREN 还会产生一个中断;遇到 AF 错误,如果 I2C 模块工作在从模式,硬件必须释放总线,如果处于主模式,软件必须生成一个停止事件。
仲裁丢失 ARLO
当 I2C 模块检测到仲裁丢失时,产生仲裁丢失错误。产生仲裁丢失错误时:ARLO 位被置位,如果设置了 ITERREN 还会产生一个中断;I2C 模块切换到从模式,并不再响应针对它的从地址发起的传输,除非有主机发起新的起始事件;硬件会释放总线。
过载/欠载错误 OVR
在从机模式下,如果禁止时钟延长,I2C 模块正在接收数据,如果已经接受到一个字节的数据,但是上一次接收到数据还没有被读出,则会产生过载错误。发生过载错误时,最后收到的字节将被丢弃,发送方应当重发最后一次发送的字节。
在从模式下,如果禁止时钟延长,I2C 模块正在发送数据,如果在下一个字节的时钟到来之前新的数据还没有被写入到数据寄存器,那么将产生欠载错误。在发生欠载错误时,前一次数据寄存器里的数据将被发送两次,如果发生欠载错误,那么接收方应该丢弃重复收到的数据。为了不产生欠载错误,I2C 模块应当在下一个字节的第一个上升沿之前将数据写入数据寄存器。
时钟延长
如果禁止时钟延长,那么就存在发生过载/欠载错误的可能。但如果使能了时钟延长:
- 在发送模式下,如果 TxE 置位且 BTF 置位,SCL 将一直为低,一直等待用户读取状态寄存器,并向数据寄存器写入待发送的数据;
- 在接收模式下,如果 RxNE 置位且 BTF 置位,那么 SCL 在接收到数据后将保持低,直到用户读取状态寄存器,并读取数据寄存器。
由此可见,使能时钟延长可以避免出现过载/欠载错误。
SMBus
SMBus 也是一种双线接口,它一般应用在系统和电源管理之间。SMBus 和 I2C 有很多相似的地方,例如 SMBus 使用和 I2C 一样的 7 位地址模式,以下是 SMBus 和 I2C 的共同点:
1) 主从通信模式,主机提供时钟,支持多主多从;
2) 两线通讯结构,其中 SMBus 可选一个警示线;
3) 都支持 7 位地址格式。
同时 SMBus 和 I2C 也存在区别:
1) I2C 支持的速度最高 400KHz,而 SMBus 支持的最高是 100KHz,且 SMBus 有最小 10KHz 的速度限制;
2) SMBus 的时钟为低超过 35mS 时,会报超时,但 I2C 无此限制;
3) SMBus 有固定的逻辑电平,而 I2C 没有,取决于 VDD;
4) SMBus 有总线协议,而 I2C 没有。
SMBus 还包括设备识别、地址解析协议、唯一的设备标识符、SMBus 提醒和各种总线协议,具体请参考 SMBus 规范 2.0 版本。当使用 SMBus 时,只需要置控制寄存器的 SMBus 位,按需配置 SMBTYPE位和 ENAARP 位。
中断
每个 I2C 模块都有两种中断向量,分别是事件中断和错误中断。两种中断支持下图的中断源。
I2C 中断请求

DMA
可以使用 DMA 来进行批量数据的收发。使用 DMA 时不能对控制寄存器的 ITBUFEN 位进行置位。
通过将 CTLR2 寄存器的 DMAEN 位置位可以激活 DMA 模式。只要 TxE 位被置位,数据将由 DMA 从设定的内存装载进 I2C 的数据寄存器。需要进行以下设定来为 I2C 分配通道。
1) 向 DMA_PADDRx 寄存器设置 I2Cx_DATAR 寄存器地址,DMA_MADDRx 寄存器中设置存储器地址,这样在每个 TxE 事件后,数据将从存储器送至 I2Cx_DATAR 寄存器。
2) 在 DMA_CNTRx 寄存器中设置所需的传输字节数。在每个 TxE 事件后,此值将被递减。
3) 利用 DMA_CFGRx 寄存器中的 PL[0:1]位配置通道优先级。
4) 设置 DMA_CFGRx 寄存器中的 DIR 位,并根据应用要求可以配置在整个传输完成一半或全部完成时发出中断请求。
5) 通过设置 DMA_CFGRx 寄存器上的 EN 位激活通道。
当 DMA 控制器中设置的数据传输数目已经完成时,DMA 控制器给 I2C 接口发送一个传输结束的EOT/ EOT_1 信号。在中断允许的情况下,将产生一个 DMA 中断。
-
利用 DMA 接收
置位 CTLR2 寄存器的 DMAEN 后即可进行 DMA 接收模式。使用 DMA 接收时,DMA 将数据寄存器里的数据传送到预设的内存区域。需要以下步骤来为 I2C 分配通道。
1) 向 DMA_PADDRx 寄存器设置 I2Cx_DATAR 寄存器地址,DMA_MADDRx 寄存器中设置存储器地址,这样在每个 RxNE 事件后,数据将从 I2Cx_DATAR 寄存器写入存储器。
2) 在 DMA_CNTRx 寄存器中设置所需的传输字节数。在每个 RxNE 事件后,此值将被递减。
3) 用 DMA_CFGRx 寄存器中的 PL[0:1]配置通道优先级。
4) 清除 DMA_CFGRx 寄存器中的 DIR 位,根据应用要求可以设置在数据传输完成一半或全部完成时发出中断请求。
5) 设置 DMA_CFGRx 寄存器中的 EN 位激活该通道。
当 DMA 控制器中设置的数据传输数目已经完成时,DMA 控制器给 I2C 接口发送一个传输结束的EOT/EOT_1 信号。在中断允许的情况下,将产生一个 DMA 中断。
包校验错误
包错误校验(PEC)是为了提供传输的可靠性而增加一项 CRC8 校验的步骤,使用以下多项式对每一位串行数据进行计算:
C=X8+X2+X+1
PEC 计算是由控制寄存器的 ENPEC 位激活,对所有信息字节进行计算,包括地址和读写位在内。在发送时,启用 PEC 会在最后一字节数据之后加上一个字节的 CRC8 计算结果;而在接收模式,在最后一字节被认为是 CRC8 校验结果,如果和内部的计算结果不符合,就会回复一个 NAK,如果是主接收器,无论校验结果正确与否,都会回复一个 NAK。