解决STM32 I2C接口死锁在BUSY状态的方法讨论
关于STM32的I2C接口死锁在BUSY状态无法恢复的现象,网上已有很多讨论,看早几年比较老的贴子,有人提到复位MCU也无法恢复、只有断电才行的状况,那可是相当严重的问题。类似复位也无法恢复的情况是存在的,技术支持矢口否认问题存在,并不是正确面对问题的态度。比如我用这款F439芯片的SDRAM控制器,在错误操作后进入HardFault状态,复位无法恢复,JTAG也无法联机,只能断电重来,官方的Erratasheet里也提到了。
如果I2C接口无法可靠工作,那么所做的设计将存在严重隐患,不可能要求用户用断电的方法恢复系统。如果像某些网友提到弃用硬件I2C,转为GPIO模拟I2C时序,那么首先I2C时钟频率不易确定,因为STM32的时钟频率可以动态调节;此外不用硬件I2C,无法用中断、DMA等高级模式,会严重降低ARM内核效率。所以务须确认和解决这个问题。
一.问题存在
我用STM32F439IGT,为了确定问题存在,让I2C控制器作Master,先人为产生I2C总线故障。产生I2C总线故障的方法简单而粗暴:在I2C总线工作过程中,用镊子把SCL和SDA两个信号短路一下,很容易进入BUSY死锁状态。长时间短路也可能产生超时。HAL_I2C_Init()、HAL_I2C_Master_Transmit()、HAL_I2C_Master_Receive()等函数返回值分别为HAL_BUSY(0x02)、HAL_TIMEOUT(0x03)。
试着用MCU复位,是可以恢复的,说明硬件没死穴。又测试不用MCU复位,而是在程序中依次调用STM32Cube_FW_F4_V1.5.0固件库提供的如下两个初始化函数:HAL_I2C_DeInit(&hi2c1)、HAL_I2C_Init(&hi2c1),并不能保证一定恢复正常。
BUSY死锁时,用万用表测试I2C信号电压,SCL、SDA均为低电平。如果调用函数:HAL_I2C_DeInit(&hi2c1),会函数释放IO口回到GPIO的默认状态(Input),此时再测SCL、SDA电压,均为高电平。这说明总线是被MCU这边的Master拉低的,而不是被Slave拉低的。当然也存在Slave刚好输出低电平拉低SDA的可能。
二.出错代码位置跟踪
单步运行,可以看到进入stm32f4xx_hal_i2c.c程序中I2C读写函数不远处(如图阴影所在行),读BUSY位,总会得到SET的结果,无法继续执行后续程序而返回。
三.参考文献
读了网上很多解决方案,其中比较有启发意义的有这几篇:
1. 百度文库,这个好像是ST官方客服提供的,关于死锁的可能机理和解决方案做了说明:
http://wenku.baidu.com/link?url=KB9p-TYrQcmVu1azHG66BXAcG6Pe6Bm2kWF_9ERSU35EOA8obiTVTDrZ6fZ3IOjfVAb71RCvJIiAODo4p4Sr0fUPDy0kQyyqWWJgxjfYHzO
2. STM社区,这个提到了初始化I2C引脚前应该先置为OUT及高电平。这在上电初始化时无虞,因为MCU复位后IO口为输入,并由外部上拉电阻拉为高电平。但在做故障恢复时很重要,因为此时IO口可能正被Master或Slave拉成低电平。http://www.stmcu.org/module/forum/thread-518463-1-1.html
3. 这个解决方案和上面思想两个相仿,但是写了太多代码,又有放置位置的要求,看起来头大。仅作参考:http://bbs.ednchina.com/BLOG_ARTICLE_2154168.HTM
4. 最重要的说明,在ST官方提供的STM32F4xx用户指南:RM0090 Reference manualRev9,第845页,关于I2C_CR1,SWRST位的Note,提到解决BUSY死锁问题:
意思是说SWRST位可以在出错或死锁时,用于复位I2C控制器,例如众所周知的BUSY位问题。我没有看其它老STM型号的手册,至少STM32F4xx有SWRST位,STM32L0xx用户指南提到可以用PE位复位。
四.问题的解决方案
按照ST手册的提示,经过各种尝试,本着尽量少改动代码、尽量不改动固件库里只读文件的原则,我的解决方案如下所述。假设主程序里有如下的代码,返回值ret不等于0表示出错,按stm32f4xx_hal_def.h头文件中的错误代码定义,返回值为0x02是HAL_BUSY,0x03是HAL_TIMEOUT,这两个返回值都可能得到。下面程序里红色的两行是错误处理必须的:
4.1 主程序改动,加错误处理代码2行:
unsigned char ret = Sensor_ReadData(uint8* buf); // I2C读写函数
if (ret != 0) { //I2C故障处理
HAL_I2C_DeInit(&hi2c1); //释放IO口为GPIO,复位句柄状态标志
HAL_I2C_Init(&hi2c1); //这句重新初始化I2C控制器
}
else {
// 。。。。I2C无错误时的正常程序
}
4.2 子程序的改动,加7行代码:
上面HAL_I2C_Init(&hi2c1)函数会调用HAL_I2C_MspInit(hi2c)函数,这个函数在stm32f4xx_hal_msp.c文件中实现,主要是初始化IO口以及外设,由STM32CubeMX工具生成或用户自行编写,非只读文件。以下节选该函数第一段,其中I2C端口用哪个pin,是由用户自己设定的,我这里用的PB6、PB7。红、绿底色的几行是为了处理BUSY死锁问题专门插入的。
void HAL_I2C_MspInit(I2C_HandleTypeDef *hi2c)
{
GPIO_InitTypeDef GPIO_InitStruct;
if(hi2c->Instance==I2C1)
{
__I2C1_CLK_ENABLE();
// PB6 ----> I2C1_SCL
// PB7 ----> I2C1_SDA
// strong pull-uphigh to recover from locking in BUSY state
GPIO_InitStruct.Pin = GPIO_PIN_6|GPIO_PIN_7; //此行原有
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; //GPIO配置为输出
GPIO_InitStruct.Speed = GPIO_SPEED_HIGH; //强上拉
HAL_GPIO_Init(GPIOB,&GPIO_InitStruct);
HAL_GPIO_WritePin(GPIOB, 6, GPIO_PIN_SET); //拉高SCL
HAL_GPIO_WritePin(GPIOB, 7, GPIO_PIN_SET); //拉高SDA
hi2c->Instance->CR1= I2C_CR1_SWRST; //复位I2C控制器
hi2c->Instance->CR1= 0; //解除复位(不会自动清除)
// 以下是原有代码
GPIO_InitStruct.Pin = GPIO_PIN_6|GPIO_PIN_7;
GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FAST;
GPIO_InitStruct.Alternate = GPIO_AF4_I2C1;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}
//。。。
}
上面程序中,把I2C端口配置成GPIO-OUTPUT,并强制拉高,是必需的。注意到手册里关于SWRST位说明的第一句:“When set, the I2C isunder reset state. Before resetting this bit,make sure the I2C lines are released and the bus is free.” 意思就是置位SWRST,会使I2C控制器保持在复位状态。解除复位前,确保I2C总线已经释放到空闲状态,即SCL、SDA均为高电平,再恢复I2C控制器。所以解除复位是用户来做的,硬件不会自动清除该位。
五.结论
我用这款STM32F439IGT单片机,I2C部分没有出现断电才能解除BUSY死锁的严重问题,看来STM已经意识到这个硬BUG,并在后期产品里逐步进行了改进。
在没有硬件死穴的情况下,我这里仅增加10行程序,就可以用软件恢复故障。多次尝试,触发I2C故障时,一次就可以恢复,无需加延时等语句,也未改动现有固件库代码。
Circuitlife
2015年6月3日
解决STM32 I2C接口死锁在BUSY状态的方法讨论的更多相关文章
- STM32应用实例十:简析STM32 I2C通讯死锁问题
I2C接口是一种使用非常普遍的MCU与外部设备的接口方式,在STM32中也集成了I2C接口,我们也常常使用它来与外围的传感器等设备通讯. 最近在我们使用STM32F1VET6读取压力和温湿度传感器数据 ...
- STM32 I2C 难点---这个不错,留着慢慢研究
来自:http://bbs.ednchina.com/BLOG_ARTICLE_2154168.HTM I2C 总线在所有嵌入式系统中用得极广, 是一个工业级别的总线, 但由于STM32 是一个32位 ...
- 为 MaixPy 加入软 I2C 接口(移植 MicroPython 的 I2C)
起因 本文的重心为讲解如何为一款芯片移植和实现 micropython 的通用组件,但会顺带解释不同芯片的工作方式和特性. 国际惯例,先有起因,再谈问题的解决,所以记得上次总结的 关于 K210 Ma ...
- i2c接口笔记
一. i2c基础知识 1. NACK信号:当在第9个时钟脉冲的时候SDA线保持高电平,就被定义为NACK信号.Master要么产生STOP条件来放弃这次传输,或者重复START条件来发起一个新的开始. ...
- STM32 i2c通讯失败复位方法
最近在调研STM32 F10X,准备把公司AVR的MCU项目迁移到STM32上.在调研STM32 i2c这一部分时,在与i2c slave硬件连接断开后,这时再去读/写 i2c slave需要STM3 ...
- (6)s3c2440用I2C接口访问EEPROM
在前面阅读理解了I2C的官方协议文档后,就拿s3c2440和EEPROM来验证一下. 本来是想用s3c2440的SDA和SCL管脚复用为GPIO来模拟的,但在没有示波器的情况下搞了一周,怎么都出不来, ...
- JZ2440 裸机驱动 第12章 I2C接口
本章目标: 了解I2C总线协议: 掌握S3C2410/S3C2440中I2C接口的使用方法: 12.1 I2C总线协议及硬件介绍 12.1.1 I2C总线协议 1 I2C总线的概念 2 I2C总线的信 ...
- Java程序中解决数据库超时与死锁
Java程序中解决数据库超时与死锁 2011-06-07 11:09 佚名 帮考网 字号:T | T Java程序中解决数据库超时与死锁,每个使用关系型数据库的程序都可能遇到数据死锁或不可用的情况 ...
- 树莓派配置RTC时钟(DS3231,I2C接口)
1.购买基于DS3231的RTC时钟模块,并且支持3.3V的那种 2.配置树莓派 a.打开树莓派的i2c接口 sudo raspi-config -->Interfacing Options - ...
随机推荐
- .NetCore多文件上传进度的示例
主要讲的内容有: 1-----form方式上传一组图片 2-----ajax上传一组图片 3-----ajax提交+上传进度+一组图片上传 4-----Task并行处理+ajax提交+上传进度+一组图 ...
- windows上传文件到linux云服务器上
安装putty,将pscp.exe移到 C:\Windows\System32 目录下. 在cmd 中执行,pscp -l rot -pw [password] -ls [ip]:/opt 查看目录 ...
- 2018-2019-2 《网络对抗技术》Exp5 MSF基础应用 20165326
Exp5 MSF基础应用 实践内容 主动攻击实践 ms17_010_enternalblue 靶机:win7 x64成功 针对浏览器的攻击 ms14_064_ole_code_execution 靶机 ...
- Delphi7第三方控件
控件安装(安装时建议先关闭Delphi) 1.只有一个DCU文件的组件. DCU文件是编译好的单元文件,这样的组件是作者不想把源码公布.一般来说,作者必须说明此组件适合Delphi的哪种版本,如果版本 ...
- java web从入门到精通
1.Springboot配置 1.1mybatis mapper.xml所在的目录必须为resource的资源文件夹,如果xml文件在java的package里面,需要修改文件夹类型 idea修改方式 ...
- exp迁移测试库10.2.0.5
目的: 将一套10.2.0.5的UP-UNIX系统的数据,迁移到一台Windows环境下. 迁移方案:由于不同的操作系统,为了方便迁移,只是测试,使用EXP/IMP方式. 迁移流程: 一.源端导出 1 ...
- vue打包上传oss
今天把vue打包之后上传到oss,遇到了一点问题,现在解决了总结一下心得: OSS (Object Storage Service)名为对象存储,配合cdn使用达到静态文件托管加速,提升网站文件访问速 ...
- 把一个给定的值存储到一个整数中指定的几个位《C与指针5.8.5》
编写一个函数,把一个给定的值存储到一个整数中指定的几个位.它的原型如下: int store_bit_field(int original_value, int value_to_store, uns ...
- USB接口禁用与启用
前几天闺蜜淘了一台小本,但是发现计算机USB接口是禁用的,有点头疼,所以问了万能的度娘,找到了n种办法.不过这一种是适用于我的情况,简单记录一下. 解决方法:(主要就是修改注册表) 1.打开注册表编辑 ...
- 转发: 探秘Java中的String、StringBuilder以及StringBuffer
原文地址 探秘Java中String.StringBuilder以及StringBuffer 相信String这个类是Java中使用得最频繁的类之一,并且又是各大公司面试喜欢问到的地方,今天就来和大家 ...