系列文章目录
1. stm32之I2C通信协议
2. stm32之软件I2C读写MPU6050陀螺仪、加速度传感器应用案例
3. stm32之硬件I2C读写MPU6050陀螺仪、加速度传感器应用案例
文章目录
- 系列文章目录
- 前言
- 一、I2C通信外设
- 1.1 I2C外设简介
- 1.2 软件模拟和硬件外设对比
- 二、I2C外设电路结构
- 2.1 I2C框图
- 2.2 I2C基本结构
- 三、I2C外设时序
- 3.1 主机发送
- 3.1.1 起始信号生成 (EV5)
- 3.1.2 发送从机地址和方向位 (EV6)
- 3.1.3 清除ADDR标志位 (EV8_1)
- 3.1.4 发送数据 (EV8)
- 3.1.5 发送最后一个字节数据
- 3.1.6 生成停止信号 (EV8_2)
- 3.2 主机接收
- 3.2.1 起始信号生成 (EV5)
- 3.2.2 发送从机地址和方向位 (EV6)
- 3.2.3 清除ADDR标志位 (EV6_1)
- 3.2.4 接收数据 (EV7)
- 3.2.5 接收最后一个字节数据
- 3.2.6 停止信号生成 (EV7_1)
前言
提示:本文主要用作在学习江科大自化协STM32入门教程后做的归纳总结笔记,旨在学习记录,如有侵权请联系作者
本文主要探讨stm32之I2C外设相关概念以及基本结构等,最后详细地分析了I2C外设的读写时序。
一、I2C通信外设
1.1 I2C外设简介
STM32内部集成了硬件I2C收发电路,能够自动完成时钟生成、起始和终止条件生成、应答位收发以及数据传输等功能,从而大幅减轻CPU的负担。它支持多主机模式,并兼容7位和10位地址模式。此外,I2C通信支持多种速度选项,包括标准模式(最高100 kHz)和快速模式(最高400 kHz)。STM32还支持通过DMA进行数据传输,并且兼容SMBus协议。
以STM32F103C8T6为例,其硬件I2C资源包括I2C1和I2C2。
1.2 软件模拟和硬件外设对比
特性 | 软件模拟 I2C | 硬件外设 I2C |
---|---|---|
灵活性 | 高,可以使用任意 GPIO 引脚 | 低,仅限于特定的硬件引脚 |
效率 | 低,需占用 CPU 资源 | 高,硬件自动管理,释放 CPU 资源 |
时序精度 | 较低,受软件控制,易受中断影响 | 高,硬件管理,时序精确稳定 |
调试难度 | 低,容易插入调试信息 | 高,硬件时序难以直接干预 |
实现复杂度 | 简单,容易实现和修改 | 复杂,需理解和配置硬件寄存器 |
支持 DMA | 不支持 | 支持,可实现高效数据传输 |
多主设备支持 | 手动实现,难度较高 | 硬件支持,自动处理仲裁 |
可靠性 | 较低,易受中断和其他任务干扰 | 高,时序稳定,错误检测能力强 |
软件模拟 I2C: 适用于需要高灵活性、硬件资源有限或者对 I2C 速率要求不高的场合。特别适用于简单的 I2C 通信或者开发阶段需要频繁调试和修改时序的场景。
硬件外设 I2C: 适用于需要高效、稳定和可靠的 I2C 通信,特别是在多任务系统或需要高速数据传输的场合。如果 MCU 有空闲的 I2C 外设,优先选择硬件实现。
二、I2C外设电路结构
2.1 I2C框图
下图所示为STM32内部 I2C外设结构图。
首先,下图所示左边这里是这个外设的通信引脚SDA和SCL,红圈部分就是数据控制电路,这里数据收发的核心部分是这里的数据寄存器和移位寄存器。当我们需要发送数据时,可以把一个字节数据写到数据寄存器DR,当移位寄存器没有数据移位时,这个数据寄存器的值就会进一步转到移位寄存器里。在移位的过程中我们就可以直接把下一个数据放到数据寄存器里等着了,一旦前一个数据移位完成,下一个数据就可以无缝衔接继续发送。当数据由数据寄存器转到移位寄存器时就会置状态寄存器的TXE位为1表示发送寄存器为空,这就是发送的流程。
那在接收时也是这一路,输入的数据一位一位地从引脚移入到移位寄存器里,当一个字节的数据收齐之后,数据就整体从移位寄存器转到数据寄存器,同时置标志位RXNE表示接收寄存器非空,这时我们就可以把数据从数据寄存器读出来了。
ok,那到这里数据的收发就讲完了,至于什么时候收什么时候发就需要我们写入控制寄存器对应的位来进行操作了。对于起始条件、终止条件以及应答位什么的这里也都有控制电路可以完成,至于具体实现细节这里也没画出来,大家知道有电路帮我们完成这些工作就行了。
简而言之就是,对于SDA收发数据这块内容,我们只需要掌握数据寄存器和移位寄存器配合的这部分就行了。
然后我们继续看下面SCL这部分。时钟控制,是用来控制SCL线的,至于控制的细节这里也没画你就把它当作一个黑盒子就行了,在这个时钟控制寄存器写对应的位电路就会执行对应的功能。控制逻辑电路也是黑盒子,写入控制寄存器可以对整个电路进行控制,读取状态寄存器可以得知电路的工作状态。然后是中断,当内部有一些标志位置1之后可能事件比较紧急就可以申请中断,如果我们开启了这个中断,那当这个事件发生后程序就可以跳到这个中断函数来处理这个事件了。最后是DMA请求与响应。在进行很多字节的收发时可以配合DMA来提高效率。
2.2 I2C基本结构
接下来我们看一下I2C的基本结构图。
首先,移位寄存器和数据寄存器DR的配合是通信的核心部分。因为I2C是高位先行,所以这个移位寄存器是向左移位,在发送的时候最高位先移出去,然后是次高位等等。一个SCL时钟移位一次,移位8次就能把一个字节由高位到低位依次放到SDA线上了。那在接收的时候,数据通过GPIO口从右依次移进来,最终移8次,一个字节就接收完成了。
GPIO口这里,使用硬件I2C的时候这两个对应的GPIO口都要配置成复用开漏输出的模式。复用,就是GPIO口的状态是交由片上外设来控制的,开漏输出,这是I2C协议要求的端口配置。
然后SCL这里,时钟控制器通过GPIO去控制时钟线,上图简化成一主多从的模型了,所以时钟这里只画了输出的方向。
再看一下SDA这里,输出数据通过GPIO输出到端口,输入数据也是通过GPIO输入到移位寄存器。
然后是数据控制器,这是黑盒模型,没啥可说的。
最后,还是有个开关控制,也就是I2C_Cmd,配置好了就使能外设,外设就能正常工作了。
三、I2C外设时序
接下来,我们来看一下硬件I2C的操作流程,在这里我们只讨论主机发送和主机接收的流程,从机的部分就暂时先不讨论了。
在STM32F103C8T6中,当使用I2C外设进行7位地址模式下的主发送时,通信过程遵循特定的时序,并在各关键步骤产生相应的事件,下面我们就来分析一下主发送模式下的时序以及各个事件的触发过程吧。
3.1 主机发送
3.1.1 起始信号生成 (EV5)
- 步骤:主机通过设置I2C_CR1寄存器中的START位生成起始信号(Start condition)。
- 事件 (EV5):当起始条件在I2C总线上生成后,I2C_SR1寄存器中的SB(Start Bit)标志位会被置位。此时,表示总线处于活动状态,起始条件已经成功发送。
EV5事件描述:SB=1,读SR1然后将地址写入DR寄存器将清除该事件
3.1.2 发送从机地址和方向位 (EV6)
- 步骤:在EV5事件发生后,主机将7位从机地址与方向位(RW=0表示写)发送到I2C总线上。
- 事件 (EV6):当从机地址和方向位成功发送并且从机发送应答(ACK)信号时,I2C_SR1寄存器中的ADDR标志位会被置位,标志着地址传输已完成,从机已响应。
EV6事件描述:ADDR=1,读SR1然后读SR2将清除该事件
3.1.3 清除ADDR标志位 (EV8_1)
- 步骤:为了继续通信,必须读取I2C_SR1寄存器,然后读取I2C_SR2寄存器来清除ADDR标志位。清除该标志后,通信流程可以继续,准备发送数据。
- 事件 (EV8_1):EV8_1事件的产生其实就是为了清除EV6事件,为接下来写入数据作准备,写入DR寄存器该事件将被清除。
EV8_1事件描述:TxE=1,移位寄存器空,数据寄存器空,写DR寄存器
3.1.4 发送数据 (EV8)
- 步骤:主机将要发送的数据字节写入I2C_DR寄存器中。
- 事件 (EV8):每当I2C_DR寄存器中的数据被移送至移位寄存器并且I2C_DR寄存器空闲时,I2C_SR1寄存器中的TXE(Transmit Data Register Empty)标志位会被置位。这表明可以写入下一个字节的数据。
EV8事件描述:TxE=1,移位寄存器非空,数据寄存器空,写入DR寄存器将清除该事件
3.1.5 发送最后一个字节数据
- 步骤:如果启用了自动应答功能(即ACK使能),并且是最后一个数据字节,那么在发送最后一个字节时,BTF(Byte Transfer Finished)标志位会被置位,表示字节传输已经完成,且所有数据已经发送。
3.1.6 生成停止信号 (EV8_2)
- 步骤:在所有数据发送完毕后,主机可以通过设置I2C_CR1寄存器中的STOP位来生成停止条件(Stop condition),从而结束通信并释放总线。
- 事件 (EV8_2):在生成停止条件后,I2C通信结束,总线释放。
EV8_2事件描述:TxE=1,BTF=1,请求设置停止位,TxE和BTF位由硬件在产生停止条件时清除
3.2 主机接收
同样,这里有7位地址和10位地址的主接收,我们只讲7位地址的。
3.2.1 起始信号生成 (EV5)
- 步骤:主机通过设置I2C_CR1寄存器中的START位来生成起始信号(Start condition)。
- 事件(EV5):当起始条件在总线上生成后,I2C_SR1寄存器中的SB(Start Bit)标志位会被置位。这标志着起始信号已经成功发送,并且总线处于活动状态。
EV5事件描述:SB=1,读SR1然后将地址写入DR寄存器将清除该事件
3.2.2 发送从机地址和方向位 (EV6)
- 步骤:在EV5事件发生后,主机将从机地址和方向位(读/写)发送到I2C总线上。在7位地址模式下,从机地址左移一位,并将最低位设置为1以指示读方向。
- 事件 (EV6):当从机地址成功发送并且从机发回应答(ACK)时,I2C_SR1寄存器中的ADDR标志位会被置位。这标志着从机地址传输已完成,并且从机已经响应。
EV6事件描述:ADDR=1,读SR1然后读SR2将清除该事件
3.2.3 清除ADDR标志位 (EV6_1)
- 步骤:为了继续通信,必须读取I2C_SR1和I2C_SR2寄存器来清除ADDR标志位。清除后,通信流程可以继续,准备接收数据。
- 事件 (EV6_1):清除ADDR标志位后准备接收数据。
EV6_1事件描述:没有对应的事件标志,只适于接收一个字节的情况,恰好在EV6之后(即清除了ADDR之后),要清除响应和停止条件的产生位
3.2.4 接收数据 (EV7)
- 步骤:主机开始接收从机发送的数据。每接收到一个字节的数据,I2C_SR1寄存器中的RXNE(Receive Data Register Not Empty)标志位会被置位。
- 事件 (EV7):RXNE标志位被置位时,表示I2C_DR寄存器中的数据已准备好被读取。此时,主机可以读取I2C_DR寄存器来获取接收到的数据。
EV7事件描述:RxNE=1,读DR寄存器清除该事件
3.2.5 接收最后一个字节数据
- 步骤:如果当前接收的是最后一个字节数据,主机需要在接收前将I2C_CR1寄存器中的ACK位清除(ACK=0),这会导致主机在接收完最后一个字节后发送一个非应答(NACK)信号给从机,表示数据接收结束。
3.2.6 停止信号生成 (EV7_1)
- 步骤:当所有数据接收完毕后,主机需要生成停止信号(Stop condition)来释放I2C总线。通过设置I2C_CR1寄存器中的STOP位,主机会自动生成停止条件。
- 事件 (EV7_1):在产生停止条件后,I2C通信结束,I2C总线释放。
EV7_1事件描述:RxNE=1,读DR寄存器清除该事件,设置ACK=0和STOP请求