1. I2C

本章参考资料:《STM32F10X-中文参考手册》I2C 章节及《I2C 总线协议》。

1.1. I2C 协议简介

I2C 通讯协议 (Inter - Integrated Circuit) 是由 Phiilps 公司开发的,由于它引脚少,硬件实现简单, 可扩展性强,不需要 USART、CAN 等通讯协议的外部收发设备,现在被广泛地使用在系统内多个集成电路 (IC) 间的通讯。

在计算机科学里,大部分复杂的问题都可以通过分层来简化。如芯片被分为内核层和片上外设; STM32HAL库与标准库则是在寄存器与用户代码之间的软件层。对于通讯协议,我们也以分层的方式来理解, 最基本的是把它分为物理层和协议层。物理层规定通讯系统中具有机械、电子功能部分的特性, 确保原始数据在物理媒体的传输。协议层主要规定通讯逻辑,统一收发双方的数据打包、解包标准。 简单来说物理层规定我们用嘴巴还是用肢体来交流,协议层则规定我们用中文还是英文来交流。

下面我们分别对 I2C 协议的物理层及协议层进行讲解。

1.1.1. I2C 物理层

I2C通讯设备间的常用连接方式见图1 - 1,

iic_1

图1 - 1常见的I2C通讯系统

它的物理层有如下特点:

  1. 它是一个支持设备的总线。“总线”指多个设备共用的信号线。在一个 I2C 通讯总线中,可连接多个 I2C 通讯设备,支持多个通讯主机及多个通讯从机。

  2. 一个 I2C 总线只使用两条总线线路,一条双向串行数据线 (SDA) ,一条串行时钟线 (SCL)。数据线即用来表示数据,时钟线用于数据收发同步。

  3. 每个连接到总线的设备都有一个独立的地址,主机可以利用这个地址进行不同设备之间的访问。

  4. 总线通过上拉电阻接到电源。当 I2C 设备空闲时,会输出高阻态,而当所有设备都空闲,都输出高阻态时,由上拉电阻把总线拉成高电平。

  5. 多个主机同时使用总线时,为了防止数据冲突,会利用仲裁方式决定由哪个设备占用总线。

  6. 具有三种传输模式:标准模式传输速率为 100kbit/s ,快速模式为 400kbit/s ,高速模式下可达 3.4Mbit/s,但目前大多 I2C 设备尚不支持高速模式。

  7. 连接到相同总线的 IC 数量受到总线的最大电容 400pF 限制。

1.1.2. 协议层

I2C 的协议定义了通讯的起始和停止信号、数据有效性、响应、仲裁、时钟同步和地址广播等环节。

1.1.2.1. I2C 基本读写过程

先看看 I2C 通讯过程的基本结构,它的通讯过程见图1 - 2,1 - 3,1 - 4。

iic_2

图1 - 2主机写数据到从机

iic_3

图1 - 3主机由从机中读数据

iic_4

图1 - 4 I2C通讯复合格式

图例:

iic_图例_1

数据由主机传输至从机

S :传输开始信号

SLAVE_ADDRESS: 从机地址

iic_图例_2

数据由从机传输至主机

R/W- :传输方向选择位,1 为读,0 为写

A/A- :应答 (ACK) 或非应答 (NACK) 信号

P :停止传输信号

这些图表示的是主机和从机通讯时,SDA 线的数据包序列。

其中 S 表示由主机的 I2C 接口产生的传输起始信号 (S),这时连接到 I2C 总线上的所有从机都会接收到这个信号。

起始信号产生后,所有从机就开始等待主机紧接下来广播的从机地址信号 (SLAVE_ADDRESS)。在I2C 总线上, 每个设备的地址都是唯一的,当主机广播的地址与某个设备地址相同时,这个设备就被选中了, 没被选中的设备将会忽略之后的数据信号。根据 I2C 协议,这个从机地址可以是 7 位或 10 位。

在地址位之后,是传输方向的选择位,该位为 0 时,表示后面的数据传输方向是由主机传输至从机,即主机向从机写数据。该位为 1 时,则相反,即主机由从机读数据。

从机接收到匹配的地址后,主机或从机会返回一个应答 (ACK) 或非应答 (NACK) 信号,只有接收到应答信号后,主机才能继续发送或接收数据。

写数据 若配置的方向传输位为“写数据”方向,即第一幅图的情况,广播完地址,接收到应答信号后, 主机开始正式向从机传输数据 (DATA),数据包的大小为 8 位,主机每发送完一个字节数据, 都要等待从机的应答信号 (ACK),重复这个过程,可以向从机传输 N 个数据,这个 N 没有大小限制。 当数据传输结束时,主机向从机发送一个停止传输信号 (P),表示不再传输数据。

读数据 若配置的方向传输位为“读数据”方向,即第二幅图的情况,广播完地址,接收到应答信号后,从机开始向主机返回数据 (DATA), 数据包大小也为 8 位,从机每发送完一个数据,都会等待主机的应答信号 (ACK),重复这个过程, 可以返回 N 个数据,这个 N 也没有大小限制。当主机希望停止接收数据时,就向从机返回一个非应答信号 (NACK),则从机自动停止数据传输。

读和写数据 除了基本的读写,I2C 通讯更常用的是复合格式,即第三幅图的情况,该传输过程有两次起始信号 (S)。一般在第一次传输中,主机通过 SLAVE_ADDRESS 寻找到从设备后, 发送一段“数据”,这段数据通常用于表示从设备内部的寄存器或存储器地址 (注意区分它与 SLAVE_ADDRESS 的区别);在第二次的传输中, 对该地址的内容进行读或写。也就是说,第一次通讯是告诉从机读写地址,第二次则是读写的实际内容。

1.1.2.2. 通讯的起始和停止信号

前文中提到的起始 (S) 和停止 (P) 信号是两种特殊的状态,见图1 - 5。当 SCL 线是高电平时 SDA 线从高电平向低电平切换,这个情况表示通讯的起始。当 SCL 是高电平时 SDA 线由低电平向高 电平切换,表示通讯的停止。起始和停止信号一般由主机产生。

iic_5

图1 - 5起始和停止信号

1.1.2.3. 数据有效性

I2C 使用 SDA 信号线来传输数据,使用 SCL 信号线进行数据同步。见图 23_6。SDA 数据线在SCL 的每个时钟周期传输一位数据。 传输时,SCL 为高电平的时候 SDA 表示的数据有效,即此时的 SDA 为高电平时表示数据“1”,为低电平时表示数据“0”。当 SCL 为低电平时, SDA 的数据无效,一般在这个时候 SDA 进行电平切换,为下一次表示数据做好准备。

iic_6

图1 - 6数据有效性

每次数据传输都以字节为单位,每次传输的字节数不受限制

1.1.2.4. 地址及数据方向

I2C 总线上的每个设备都有自己的独立地址,主机发起通讯时,通过 SDA 信号线发送设备地址(SLAVE_ADDRESS) 来查找从机。 I2C 协议规定设备地址可以是 7 位或 10 位,实际中 7 位的地址应用比较广泛。紧跟设备地址的一个数据位用来表示数据传输方向, 它是数据方向位 (R/),第 8位或第 11 位。数据方向位为“1”时表示主机由从机读数据,该位为“0”时表示主机向从机写数据。见图1 - 7。

iic_7

图1 - 7 设备地址 (7 位) 及数据传输方向

读数据方向时,主机会释放对 SDA 信号线的控制,由从机控制 SDA 信号线,主机接收信号,写数据方向时,SDA 由主机控制,从机接收信号。

1.1.2.5. 响应

I2C 的数据和地址传输都带响应。响应包括“应答 (ACK)”和“非应答 (NACK)”两种信号。作为数据接收端时,当设备 (无论主从机) 接收到 I2C 传输的一个字节数据或地址后, 若希望对方继续发送数据,则需要向对方发送“应答 (ACK)”信号,发送方会继续发送下一个数据;若接收端希望结束数据传输, 则向对方发送“非应答 (NACK)”信号,发送方接收到该信号后会产生一个停止信号,结束信号传输。见图1 - 8。

iic_8

图1 - 8 响应与非响应信号

传输时主机产生时钟,在第 9 个时钟时,数据发送端会释放 SDA 的控制权,由数据接收端控制SDA,若 SDA 为高电平,表示非应答信号 (NACK),低电平表示应答信号 (ACK)。

1.2. STM32 的 I2C 特性及架构

如果我们直接控制 STM32 的两个 GPIO 引脚,分别用作 SCL 及 SDA,按照上述信号的时序要求,直接像控制 LED 灯那样控制引脚的输出 (若是接收数据时则读取 SDA 电平), 就可以实现 I2C 通讯。同样,假如我们按照 USART 的要求去控制引脚,也能实现 USART 通讯。所以只要遵守协议,就是标准的通讯,不管您如何实现它, 不管是 ST 生产的控制器还是 ATMEL 生产的存储器,都能按通讯标准交互。

由于直接控制 GPIO 引脚电平产生通讯时序时,需要由 CPU 控制每个时刻的引脚状态,所以称之为“软件模拟协议”方式。相对地,还有“硬件协议”方式,STM32 的 I2C 片上外设专门负责实现 I2C 通讯协议,只要配置好该外设,它就会自动根据协议要求产生通讯信号, 收发数据并缓存起来,CPU 只要检测该外设的状态和访问数据寄存器,就能完成数据收发。这种由硬件外设处理 I2C 协议的方式减轻了CPU 的工作,且使软件设计更加简单。

1.2.1. STM32 的 I2C 外设简介

STM32 的 I2C 外设可用作通讯的主机及从机,支持 100Kbit/s 和 400Kbit/s 的速率,支持 7 位、10位设备地址,支持 DMA 数据传输, 并具有数据校验功能。它的 I2C 外设还支持 SMBus2.0 协议,SMBus 协议与 I2C 类似,主要应用于笔记本电脑的电池管理中,本教程不展开, 感兴趣的读者可参考《SMBus20》文档了解。

1.2.2. STM32 的 I2C 架构剖析

iic_9

图1 - 9 I2C 架构图

1.2.2.1. 通讯引脚

I2C 的所有硬件架构都是根据图中左侧 SCL 线和 SDA 线展开的 (其中的 SMBA 线用于 SMBUS 的警告信号,I2C 通讯没有使用)。STM32 芯片有多个 I2C 外设, 它们的 I2C 通讯信号引出到不同的GPIO 引脚上,使用时必须配置到这些指定的引脚。关于 GPIO 引脚的复用功能,以规格书为准。这里以STM32F10X的I2C引脚为例,

STM32F10X的I2C引脚

引脚

I2C1

I2C2

SCL

PB5 / PB8(重映射)

PB10

SDA

PB6 / PB9(重映射)

PB11

1.2.2.2. 时钟控制逻辑

SCL 线的时钟信号,由 I2C 接口根据时钟控制寄存器 (CCR) 控制,控制的参数主要为时钟频率。配置 I2C 的 CCR 寄存器可修改通讯速率相关的参数:

  1. 可选择 I2C 通讯的“标准/快速”模式,这两个模式分别 I2C 对应 100/400Kbit/s 的通讯速率。

  2. 在快速模式下可选择 SCL 时钟的占空比,可选 Tlow/Thigh=2 或 Tlow/Thigh=16/9 模式,我们知道 I2C 协议在 SCL 高电平时对 SDA 信号采样,SCL 低电平时 SDA 准备下一个数据,修改 SCL 的高低电平比会影响数据采样,但其实这两个模式的比例差别并不大,若不是要求非常严格,这里随便选就可以了。

  3. CCR 寄存器中还有一个 12 位的配置因子 CCR,它与 I2C 外设的输入时钟源共同作用,产生SCL 时钟,STM32 的 I2C 外设都挂载在 APB1 总线上,使用 APB1 的时钟源 PCLK1,SCL信号线的输出时钟公式如下:

标准模式:

Thigh=CCR*TPCKL1 Tlow = CCR*TPCLK1

快速模式中 Tlow/T:sub:high=2 时:

Thigh = CCR*TPCKL1 Tlow = 2*CCR*TPCKL1

快速模式中 Tlow/T:sub:high=16/9 时:

Thigh = 9*CCR*TPCKL1 Tlow = 16*CCR*TPCKL1

例如,我们的 PCLK1=36MHz,想要配置 400Kbit/s 的速率,计算方式如下:

PCLK 时钟周期:TPCLK1 = 1/36000000

目标 SCL 时钟周期:TSCL = 1/400000

SCL 时钟周期内的高电平时间:THIGH = TSCL/3

SCL 时钟周期内的低电平时间:TLOW = 2*TSCL/3

计算 CCR 的值:CCR = THIGH/TPCLK1 = 30

计算结果得出 CCR 为 30,向该寄存器位写入此值则可以控制 IIC 的通讯速率为 400KHz,其实即使配置出来的 SCL 时钟不完全等于标准的 400KHz,IIC 通讯的正确性也不会受到影响,因为所有数据通讯都是由 SCL 协调的,只要它的时钟频率不远高于标准即可。

1.2.2.3. 数据控制逻辑

I2C 的 SDA 信号主要连接到数据移位寄存器上,数据移位寄存器的数据来源及目标是数据寄存器 (DR)、地址寄存器 (OAR)、PEC 寄存器以及 SDA 数据线。 当向外发送数据的时候,数据移位寄存器以“数据寄存器”为数据源,把数据一位一位地通过 SDA 信号线发送出去;当从外部接收数据的时候, 数据移位寄存器把 SDA 信号线采样到的数据一位一位地存储到“数据寄存器”中。若使能了数据校验,接收到的数据会经过 PCE 计算器运算, 运算结果存储在“PEC 寄存器”中。当 STM32 的 I2C 工作在从机模式的时候,接收到设备地址信号时,数据移位寄存器会把接收到的地址与 STM32 的自身的“I2C 地址寄存器”的值作比较, 以便响应主机的寻址。STM32 的自身 I2C 地址可通过修改“自身地址寄存器”修改,支持同时使用两个 I2C 设备地址,两个地址分别存储在 OAR1 和 OAR2 中。

1.2.2.4. 整体控制逻辑

整体控制逻辑负责协调整个 I2C 外设,控制逻辑的工作模式根据我们配置的“控制寄存器(CR1/CR2)”的参数而改变。 在外设工作时,控制逻辑会根据外设的工作状态修改“状态寄存器 (SR1 和 SR2)”,我们只要读取这些寄存器相关的寄存器位, 就可以了解 I2C 的工作状态。除此之外,控制逻辑还根据要求,负责控制产生 I2C 中断信号、DMA 请求及各种 I2C 的通讯信号(起始、停止、响应信号等)。

1.2.3. 通讯过程

使用 I2C 外设通讯时,在通讯的不同阶段它会对“状态寄存器 (SR1 及 SR2)”的不同数据位写入参数,我们通过读取这些寄存器标志来了解通讯状态。

1.2.3.1. 主发送器

见图1 - 10 。图中的是“主发送器”流程,即作为 I2C 通讯的主机端时,向外发送数据时的过程。

iic_10

图1 - 10 I2C 主发送器通讯过程

主发送器发送流程及事件说明如下:

① 控制产生起始信号 (S),当发生起始信号后,它产生事件“EV5”,并会对 SR1 寄存器的“SB”位置 1,表示起始信号已经发送;

② 紧接着发送设备地址并等待应答信号,若有从机应答,则产生事件“EV6”及“EV8”,这时 SR1 寄存器的“ADDR”位及“TXE”位被置 1,ADDR 为 1 表示地址已经发送,TXE 为 1 表示数据寄存器为空;

③ 以上步骤正常执行并对 ADDR 位清零后,我们往 I2C 的“数据寄存器 DR”写入要发送的数据,这时 TXE 位会被重置 0,表示数据寄存器非空,I2C 外设通过 SDA 信号线一位位把数据发送出去后,又会产生“EV8”事件,即 TXE 位被置 1,重复这个过程,就可以发送多个字节数据了;

④ 当我们发送数据完成后,控制 I2C 设备产生一个停止信号 (P),这个时候会产生 EV8_2 事件,SR1 的 TXE 位及 BTF 位都被置 1,表示通讯结束。

假如我们使能了 I2C 中断,以上所有事件产生时,都会产生 I2C 中断信号,进入同一个中断服务函数,到 I2C 中断服务程序后,再通过检查寄存器位来判断是哪一个事件。

1.2.3.2. 主接收器

再来分析主接收器过程,即作为 I2C 通讯的主机端时,从外部接收数据的过程,见图1 - 11。

iic_11

图1 - 11 I2C 主接收器过程

主接收器接收流程及事件说明如下:

① 同主发送流程,起始信号 (S) 是由主机端产生的,控制发生起始信号后,它产生事件“EV5”,并会对 SR1 寄存器的“SB”位置 1,表示起始信号已经发送;

② 紧接着发送设备地址并等待应答信号,若有从机应答,则产生事件“EV6”这时 SR1 寄存器的“ADDR”位被置 1,表示地址已经发送。

③ 从机端接收到地址后,开始向主机端发送数据。当主机接收到这些数据后,会产生“EV7”事件,SR1 寄存器的 RXNE 被置 1,表示接收数据寄存器非空, 我们读取该寄存器后,可对数据寄存器清空,以便接收下一次数据。此时我们可以控制 I2C 发送应答信号 (ACK) 或非应答信号(NACK),若应答,则重复以上步骤接收数据,若非应答,则停止传输;

④ 发送非应答信号后,产生停止信号 (P),结束传输。

在发送和接收过程中,有的事件不只是标志了我们上面提到的状态位,还可能同时标志主机状态之类的状态位,而且读了之后还需要清除标志位, 比较复杂。我们可使用 STM32HAL 库函数来直接检测这些事件的复合标志,降低编程难度。