尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

江科大STM32入门:硬件I2C外设结构与通信时序深度解析

江科大STM32入门:硬件I2C外设结构与通信时序深度解析 我的STM32学习专栏[点击直达 | 持续更新中]开发环境Keil5 标准库前置知识I2C通信协议基础一、前言上一篇文章我们聊了I2C协议的软件模拟实现。这种方式虽然灵活但有个致命问题CPU得一直盯着总线啥也干不了。而且代码写起来像绣花一个时序错了整个通信就崩。好在STM32内部早就给我们集成了一套硬件I2C外设它就像个专职秘书时钟生成、起始/停止条件、应答位收发这些脏活累活全包了CPU只需要读写几个寄存器就行。这篇文章我们就来扒一扒这个硬件I2C的五脏六腑把它的结构、时序和波形彻底讲透。二、STM32硬件I2C外设资源一览STM32的I2C外设不是简单的引脚翻转器它内部有一套完整的通信状态机。先来看看它到底能干啥硬件I2C的几大核心能力特性说明自动时序控制硬件自动产生SCL时钟、起始/停止条件、应答位无需CPU干预多主机支持支持总线仲裁多个主机可以共存地址模式支持7位和10位设备地址通讯速率标准模式100kHz快速模式400kHzDMA传输大批量数据传输时可以用DMA解放CPUSMBus兼容兼容系统管理总线协议硬件资源STM32F103C8T6 有I2C1和I2C2两组独立外设我的理解软件I2C就像你自己用手摇发电机发电电压不稳还得一直摇硬件I2C就是直接插插座220V稳得很。而且硬件I2C的自动应答功能特别香——收到一个字节硬件自动回ACKCPU连中断都不用进。三、I2C外设内部结构剖析知道它能干啥之后得看看内部是怎么连的。STM32参考手册里的I2C外设结构框图3.1 数据通路半双工注意结构图里有个关键点数据寄存器DR和数据移位寄存器是共用的。因为I2C是半双工通信同一时刻要么发要么收所以STM32干脆让收发共用一套数据通路通过方向控制来切换。数据流向是这样的发送CPU写数据到DR→ 进入移位寄存器左移高位先行→ 从SDA引脚逐位发出接收SDA引脚逐位进入移位寄存器左移→ 满8位后整体搬进DR→ CPU读DR3.2 时钟控制CCR时钟控制寄存器决定了SCL的频率。你往CCR里写一个分频值硬件就按照这个频率自动产生时钟不需要你在代码里手动翻转GPIO。3.3 控制与状态CR1/CR2控制寄存器使能I2C、配置ACK、DMA、中断等SR1/SR2状态寄存器存放各种事件标志位SB、ADDR、TxE、RxNE、BTF等也就是我们后面要讲的EVx事件3.4 GPIO引脚配置I2C外设要通过GPIO的复用功能接到外部默认引脚I2C1 用PB6(SCL)和PB7(SDA)重映射引脚I2C1 可以重映射到PB8(SCL)和PB9(SDA)⚠️ I2C的GPIO必须配置为复用开漏输出AF_OD因为I2C总线需要线与特性——多个设备共用SDA任何一个设备拉低整条线就是低电平。如果用推挽输出两个设备一个输出高一个输出低就会短路。我的理解这个开漏就像一群人共用一个水龙头每个人手里只有关水龙头的能力拉低想开水龙头拉高只能靠外部上拉电阻。这样大家就不会打架实现了总线的线与逻辑。四、主机发送与接收时序详解重点这是硬件I2C编程的核心也是最容易踩坑的地方。STM32用一系列EVx事件来标记通信状态我们的代码本质上就是等事件 → 处理数据 → 清标志的循环。4.1 主机发送时序写数据主机发送以7位地址发送为例完整流程如下[START] → [发送设备地址写] → [收到ACK] → [发送数据1] → [收到ACK] → ... → [STOP] ↑ ↑ ↑ ↑ EV5 EV6 EV8 EV8_2事件标志位含义软件操作EV5SB1起始条件已发送读SR1然后写地址到DR清除SBEV6ADDR1地址已发送且收到ACK读SR1再读SR2清除ADDREV8_1TxE1DR空移位寄存器空直接写数据到DREV8TxE1DR空移位寄存器正在发写DR清除该事件准备下一个字节EV8_2TxE1, BTF1数据全部发送完毕产生STOP信号我的理解你可以把EV5理解成喂我已经在总线上喊START了EV6就是从机听到地址并举手应答了EV8_2就是所有数据都发出去了可以挂电话了。这些事件不是中断虽然可以配成中断而是状态标志位轮询检测就行。4.2 主机接收时序读数据主机接收读数据稍微复杂一点因为要在最后一个字节之前关闭ACK并提前请求STOP[START] → [发送设备地址写] → [指定寄存器地址] → [重复START] → [发送设备地址读] → [接收数据] → [STOP] ↑ ↑ ↑ ↑ ↑ ↑ EV5 EV6 EV8_2 EV5 EV6 EV7/EV7_1事件标志位含义软件操作EV5SB1起始/重复起始已发送读SR1写地址到DREV6ADDR1读模式已建立读SR1、SR2清ADDREV7RxNE1接收数据寄存器非空读DR即可清除EV7_1RxNE1接收最后一个字节先设ACK0再请求STOP最后读DR⚠️ EV7_1是接收单字节或最后一个字节时的关键操作顺序必须是①关闭ACK → ②请求STOP → ③读DR。如果顺序错了总线可能锁死SDA一直被拉低。我的理解读数据时关闭ACK就像你跟快递员说这是最后一个包裹后面没有了然后请求STOP就是挂电话。如果你先读数据再挂电话快递员可能以为你还要继续发就一直等着。五、软件I2C vs 硬件I2C波形对比很多人好奇软件模拟的和硬件生成的波形到底差在哪直接上图第一个为软件波形第二个为硬件波形从波形上可以明显看出对比项软件I2C硬件I2C波形规整度周期、占空比不一致有抖动非常规整每个时钟周期几乎一样产生原因GPIO操作软件延时受中断影响硬件状态机精确控制对通信的影响I2C是同步时序只要SCL和SDA相对关系正确影响不大完美符合协议规范CPU占用高必须忙等极低等待期间CPU可以干别的适用场景引脚灵活、没有硬件I2C的MCU有硬件I2C的STM32强烈推荐使用我的理解软件I2C的波形就像手写毛笔字每笔粗细不一但也能看硬件I2C就像印刷体整整齐齐。虽然I2C是同步协议对时钟绝对精度要求不高但硬件I2C的稳定性在复杂项目里是碾压级的——你的CPU可以安心去跑RTOS、处理中断不用在I2C时序里坐牢。七、个人理解 / 心得自己写以下内容为个人学习心得供参考建议根据自己的理解修改刚开始学硬件I2C的时候我被那一堆EV事件搞懵了——什么EV5、EV6、EV8_2看起来比软件I2C还复杂。但后来我换个角度想软件I2C是我们自己演时序硬件I2C是硬件演我们只需要看提示词。EV事件其实就是硬件给我们发的 stage cue舞台提示EV5亮了 导演说Action你可以报地址了EV6亮了 对方演员接话了你可以念台词了EV8_2亮了 这场戏拍完了可以喊Cut了所以写硬件I2C代码核心就一句话等灯亮干活灭灯等下一个灯。只要把这个逻辑理顺了代码结构就非常清晰。另外关于开漏输出和上拉电阻我之前一直不太理解为什么I2C非要外接上拉。后来看了结构图才明白开漏模式下STM32只能把引脚拉低对地导通想让它变高只能靠外部电阻把电平拉上去。这就像一群人玩抢答器每个人都可以按下去拉低但松开拉高是靠弹簧上拉电阻自动恢复的。这样设计就避免了两个人同时按和松导致的短路。八、踩坑记录读数据时总线锁死SDA一直被拉低现象第二次通信时START发不出去SDA线为低。原因单字节读取时先读了DR再请求STOP导致从机以为还要传数据一直拉低SDA等时钟。解决单字节读取必须遵循ACK0 → STOP1 → 读DR的顺序多字节最后一个字节同理。九、总结表格最后用一个表格把硬件I2C的核心知识点串起来模块关键内容注意事项GPIO配置PB6/PB7复用开漏(AF_OD)50MHz必须外接上拉电阻(4.7kΩ)时钟控制CCR寄存器控制SCL频率标准模式100kHz快速400kHz数据通路DR ↔ 移位寄存器 ↔ SDA半双工高位先行收发共用DR发送事件EV5→EV6→EV8→EV8_2等事件后再操作及时清标志接收事件EV5→EV6→EV7(→EV7_1)最后一字节先关ACK再STOP再读DR防卡死所有等待加Timeout超时总线故障时不死循环波形对比硬件波形规整软件波形有抖动复杂项目优先用硬件I2C十、结语写这篇文章的时候我把江科大的视频又回顾了一遍对照参考手册把I2C外设的结构图和时序图重新梳理了一次。说实话硬件I2C的EV事件确实比软件模拟要抽象一些但一旦理解了事件驱动的编程模型你会发现它的代码结构反而更清晰——该等的等该发的发不用自己数微秒、不用手动翻引脚。如果你正在从软件I2C过渡到硬件I2C建议先用逻辑分析仪抓一波波形对照着图3的EV事件看代码执行到哪一步、对应波形上的哪个位置这样理解起来会快很多。开发环境Keil5 STM32F103C8T6最小系统板 ST-Link参考资料江科大STM32入门教程、《STM32F10xxx参考手册》前置文章江科大STM32入门软件I2C驱动MPU6050代码逐行解析如果本文对你有帮助欢迎点赞 收藏 ⭐ 有任何问题欢迎在评论区交流一起学习进步
返回列表