I2C 接口进入 Busy 状态不能退出.pdf T官方 I2C BUG分析解决文档,分析了一个用户在使用STM32硬件 I2C与E2PROM通讯时的BUSY位被置位卡死的情况 最近死磕了5天的STM32F1硬件I2C从机的程序,天天早上8点到凌晨,几乎全程心流状态。终于在结合各方资料及自己的思考后,做出了稳定的硬件I2C代码(这个文章中应该是目前为止能查到的最详述可用的硬件I2C代码),经过I2C主机发出的各种奇怪的信号蹂躏后,通讯都可以恢复正常,不会被卡死。证明该方案拥有极高稳定性。 需要注意我这次使用的是 STM32F103C8T6 的兼容型号 GD32F103C8T6 。要问他的兼容性有多强,连I2C bug都能做到一样,哈哈。我当初用GD想着硬件I2C应该能舒服用了,万万没想到,兆易连i2c 硬件BUG都复制了。 大家不要纠结于单片机的型号,我推测应该STM32FXXX 家族硬件I2C应该都是这个样子,具体情况我也无法一一测试,如果大家看了文章在自己的系统上测试成功后别忘了留个言,说下自己系统的配置,方便后来人~ 相信很多接触STM32的用户在尝试使用I2C代码时都被警告过stm32 硬件I2C有bug,那么这个bug具体是什么,又是如何发生的呢,我们具体来分析一下,给我们之后代码解决这个问题来做个铺垫。 首先我们来看一下STM32 I2C的一个神奇的寄存器,SR2 的 BUSY 位,具体他是做什么的参考手册中已经很清晰的描述,我就不多说了,我直接说它的问题。 当STM32 硬件I2C模块在通讯(无论做主机还是从机)中遇到总线被占用时,使得 STM32 在接管总线时发生总线仲裁失败,进而失去对总线的控制,导致BUSY位被置位,且无法通过使用官方驱动库自动清除。而后即表现为锁死状态。 该情况多会出现在I2C通讯错误,或者从机程序接收到了非预定的I2C指令时产生。 由于我使用的是硬件I2C从机,所以以下内容均以I2C从机为主要内容讲述,(如果你使用的是主机也请耐心看完,因为主机也可以按照该思路修改后解决)也可能之后我会补充主机内容(主要是I2C做主机时,从机大多时候都成熟器件,想要人为产生BUG情况也蛮困难的) 首先我们来看一段我用逻辑分析仪抓取的硬件系统的波形, 和造成该波形的测试代码(以下I2C写指令由主机发出,主机为其他公司的单片机承担,不建议使用两个STM32 硬件I2C对传测试,否则都出BUG找问题都找不到) 我们可以看到,由于我的代码中只有对一个I2C写指令接收的内容,而后就进入了延时等待状态,从而错过了主机连着发送的第二条写指令,我们的单片机甚至都没有对其做ACK响应。此时我们用调试器查看STM32寄存器,就会发现 BUSY进入了锁死状态。第二次进入的HAL_I2C_Slave_Receive_IT() 函数中会在等待 BUSY 清零的查询操作中超时退出,而后在之后的循环中再次循环这个过程。 而如果我们把循环中的延时去掉,我们会发现I2C的通讯就会变得正常了。即, 由于我们没能对主机发出的第二条的I2C指令及时处理,STM32 I2C就会出问题。而这种情况在使用中是不可能避免的,主机任何一次的误操作或误编程都可能会导致我们的I2C锁死。 放上代码 如代码中注释,我们在检测 HAL_I2C_Slave_Receive_IT() 返回超时或错误时,使用 i2c_reset() 函数来复位I2C硬件模块以重启I2C,使得STM32可以在主机恢复发送正常设定的I2C命令后可以恢复通讯。 i2c_reset() 函数核心操作思路如下: 配置 对应IO为开漏输出,以关闭I2C模块的硬件输入通道,防止后续的通讯继续触发BUG,并尝试将总线拉高。 尝试让从机释放总线,或等待主机释放总线(此步骤根据你的实际系统为准,最好查一下波形,看看具体故障表现是什么,如果STM32是从机,那么主机在通讯失败后,大部分会在一定时间后超时并释放总线。所以我的代码中只是在一定时间内检测总线是否被释放。如果你是主机的话则可以按照 《2C 接口进入 Busy 状态不能退出.pdf》 文档中的方法,在SCL线上发送脉冲来使得从机释放I2C总线)还是一句话:该步骤根据你的实际系统为准,我们在这步中核心需求就是让总线被释放。 将引脚配置回I2C的模式,将总线归还STM32的I2C模块 将CR1寄存器的SWRST置位后再清零,以复位I2C模块,在此过程中I2C除DR寄存器外所有寄存器都会被清空,包括BUSY位 (此处记忆有些模糊,不确定DR寄存器是否会被清空。DR为I2C收发数据缓存寄存器) 重新初始化I2C,原因是我们上一步的复位操作清空了I2C的配置 加入这些代码后,我们可以再次使用逻辑分析仪观察波形,或通过串口打印来检测程序运行。(由于逻辑分析仪波形太长,图片放不下就不做展示了)我们可以看到,不管主机如何发送诱发错误的信息,我们的代码都能让 stm32在i2c复位后的第一次接收时工作正常。而如果主机按照预定协议,间隔发送指令时,通讯就会完全恢复正常,不会触发i2c_reset() 函数。 如果使用 HAL_I2C_Slave_Receive_IT() 函数接收了主机发送的读取指令,并不会触发BUG,主机会读取回 0x00的数据。 例子中我使用了 RT-thread 实时操作系统,所以延时不大一样。使用RTOS的延时时还可以让我们在等待时,将CPU调度到其他线程使用,防止一个I2C占用全部CPU周期。 有了故障处理程序后,我们就可以使用 HAL 库中自带的 I2C_TwoBoards_AdvComIT 例程来处理收发数据了。我使用的官方HAL库例程路径: i2c_reset() 核心操作思路的步骤顺序有严格要求,随意变更1~5操作的前后顺序会导致BUG再次触发 IIC总线调试具有特殊性,我们最好还是准备一个逻辑分析仪来抓取波形,结合DEBUG时对寄存器的查看来分析解决故障 接上一条,在调试IIC前,我们应该确保自己对STM IIC的各个寄存器和位功能以及I2C波形时序的熟悉,以求在出现问题时能够找到问题所在,新版的HAL库IIC内容很清晰,只要了解寄存器,通过DEBUG+查看波形 的方法可以很快定位解决错误。 示例代码仅提供思路,具体使用需要修改为你的配置。 注意一定要使用新版本的HAL库,我已经被旧版的有明显错误的HAL I2C库坑过了(居然直接对只读的标志位赋值,来想要清除标志位) STM32的IIC模块确实存在BUG, 具体表现就是在我们代码没有处理预设之外的IIC指令或数据时会发生由于总线仲裁失败,导致BUSY位锁死的问题。出现这种问题,只要我们能够用代码监测到此情况的发生,并使用上述的 i2c_reset()核心操作思路就可以解决,从而让我们在实际工程应用中使用。毕竟软件也许可以方便的模拟100khz的IIC主机,但对400khz I2c通讯来说,无论是主机还是从机都很难实现(从机的软件模拟技巧性很强,且CPU消耗也大)。 如果这个文章,解决了你的问题,请留个言交流下,因为我目前精力有限也只是在自己的硬件上测试了这个内容,让我知道你的问题被解决会让我很开心,也会让之后的朋友不在为这个问题困扰~ 因为目前我的工程涉及到自己公司的产品,不方便直接发出,之后有空了,我会做一个示例工程挂到 github 和 gitee 上,方便大家修改使用。
前言
STM32 硬件I2C BUG简述
关于这个情况,在官方的一篇 《2C 接口进入 Busy 状态不能退出.pdf》 文档中也有描述,也正是这篇文档给了我思路,让我解决了这个问题。
BUG实例
while (1) { while(HAL_I2C_Slave_Receive_IT(&hi2c1, &i2c_buff, 1) != HAL_OK) { } while (HAL_I2C_GetState(&hi2c1) != HAL_I2C_STATE_READY) { } delay_ms(10); }
解决方案
void i2c_reset() { /* 开漏输出,关闭I2C输入通道,并尝试将总线拉高 */ GPIO_InitTypeDef GPIO_InitStruct; GPIO_InitStruct.Pin = GPIO_PIN_8 | GPIO_PIN_9; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8 | GPIO_PIN_9, GPIO_PIN_SET); // SCL PB8 拉高 for (uint8_t i = 0; i < 10; i++) { if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_8) == GPIO_PIN_SET) { rt_kprintf("retry %dn", i); break; } /* 该延时循环的周期和时长,请根据你的实际主机对I2C通讯出错的处理来修改 */ rt_thread_mdelay(10); } /* 归还总线控制权 */ GPIO_InitStruct.Mode = GPIO_MODE_AF_OD; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); /* 复位I2C */ hi2c1.Instance->CR1 |= I2C_CR1_SWRST; hi2c1.Instance->CR1 &= ~I2C_CR1_SWRST; /* 重新初始化I2C */ MX_I2C1_Init(); } /* * I2C最大的问题在于如果主机随意发送内容,而stm32作为从机时, 接收到程序意图之外的通讯内容后会卡死BUSY位。 * 发生该情况后,可配置SCL SDA为开漏输出,关闭I2C输入通道防止错误再次被触发,而后尝试拉高SCL SDA线,或者 * 等待总线被主机释放,这里的情况依据具体主机的不同可根据实际操作判断,最终我们需要等待I2C总线为空闲即SCL * SDA线为高电平保持时。将SCL SDA配回IIC,释放总线控制权 ,使用 SWRST复位I2C,清除全部I2C寄存器内容, * (BUSY位也会在该操作中被清除) 并重新初始化I2C总线。 */ void i2c_salve_thread(void *parameter) { while (1) { if(HAL_I2C_Slave_Receive_IT(&hi2c1, &i2c_buff, 1) != HAL_OK) { // I2C设备出现故障无法开启接收 i2c_reset(); } // 检测标志位,防止I2C被二次开启,导致BUG while (HAL_I2C_GetState(&hi2c1) != HAL_I2C_STATE_READY) { rt_thread_mdelay(1); } rt_thread_mdelay(10); } } void HAL_I2C_AddrCallback(I2C_HandleTypeDef *hi2c, uint8_t TransferDirection, uint16_t AddrMatchCode) { direction = TransferDirection; }
小计
D:STSTM32CubeRepositorySTM32Cube_FW_F1_V1.7.0ProjectsSTM32F103RB-NucleoExamplesI2CI2C_TwoBoards_AdvComIT
总结
最后
本网页所有视频内容由 imoviebox边看边下-网页视频下载, iurlBox网页地址收藏管理器 下载并得到。
ImovieBox网页视频下载器 下载地址: ImovieBox网页视频下载器-最新版本下载
本文章由: imapbox邮箱云存储,邮箱网盘,ImageBox 图片批量下载器,网页图片批量下载专家,网页图片批量下载器,获取到文章图片,imoviebox网页视频批量下载器,下载视频内容,为您提供.
阅读和此文章类似的: 全球云计算