STM32’s I2C 硬件BUG引发的血案(qzm)

2010-4-8 1: 45 下面的函数中有一个BUG, 也就是SR2不能用WHILE来轮询,而应直接读出.如下面代码段, 因此,在这里说的这是STM32的BUG其实是我的代码的错误:
    I2C2->DR = inerAddress[1];
    while( (I2C2->SR1&Q_I2C_SR1_BIT_BTF)==0 );
    I2C2->SR2;   // 正解

2010-4-1 1:44,一直都不相信STM32 的I2C接口会存在问题,因为工作经验无数次告诉我,嵌入式系统设计中的99.999%的问题不会是由于MCU本身的设计问题所引起,绝大部分都是硬件工程师或软件工程师的某个设计缺陷所造成的. 这次的设计经历也不例外.

由于终于可以抽多点时间用于设计STM32的I2C的软件接口, 因此, 决定重构之前用于主从STM32通讯所用的I2C模块(基于I2C中断+状态机收发)。I2C的中断发送模块很快就重构完毕并且强化了错误处理和I2C总线hang的自恢复功能代码模块(I2C BUS Hang:也就是SDA 和SCL都被某Slave DEVICE拉低,大部分发生在主机接收从机传过来的数据包的最后一个字节的数据时,没有发送P(停止位)所造成的,这时Slave device (EEPROM)因为接收不到P,从而会DEAD LOOP地发送数据给主机,在主机方向看,I2C总线就相当于一直处于BUSY的状态,也就是网上问得最多的关于I2C的问题-I2C总线TMD的怎么会突然间死掉了、BUSY了、HANG的原因。)。

本以为I2C中断读模块也会很顺利地完成,但是却被卡住了一整天,只要加一个断点,在大部份代码处加无所谓,读FUN每次都正确执行,但只要全速运行,就只能运行一次,然后就过不去了,而且过不去的地方并不固定。由于DUBEG得有点集中不了精力,于是就做好问题的Brainstrom的笔记,放了下,晚上出差到深圳,到客户处做系统需求的讨论会,主要是上位机部分的。然后第二天晚上又赶回来,在车上的半梦半觉中让潜意思去思考。回到家后,选择在我的生物钟的最佳的时侯,晚上10点。开始重新在问题的Brainstorm处接着DUBGE。WORK PLAN如下:
(1)把程序恢复到测试I2C写的测试用例状态,PASS。

(2)一小段代码就DEBUG一次的步步为营方法,按之前的思路把代码加入到读的模块(忽略防守代码,只加入必需的功能代码)。都OK后,全速运行,XXX,又卡住了。细过了一片读模块。PASS。于是进入计划中的(3)

(3)全速运行,然后在可能卡住的地方加上断点,而不是之前的先加断点,然后运行的方式(这种方式,已确认PASS):
加了几处后,发现程序是在下面代码处卡住的:
image

但是奇怪了,每次我只要在箭头指向的语句后加上断点,每次都能PASS。于是知道要到 I2C_CheckEvent 函数中就能找到问题的原因了,于是做下面的几步,目的是把包含I2C_CheckEvent Fun的链接库中的stm32f10x_i2c.o排除掉,而把stm32f10x_i2c.c加入到项目中,使得DEBUG时能进入到 I2C_CheckEvent Fun 中去.

image

image

于是全速运行,然后在 stm32f10x_i2c.c中加上断点,终于捕捉到问题点了,分析如下图所示:

image

/* Read the I2Cx status register */
//  flag1 = I2Cx->SR1;             // 原代码
//  flag2 = I2Cx->SR2;            //原代码
//  flag2 = flag2 << 16;           //原代码

  /* Get the last event value from I2C status register */
  //lastevent = (flag1 | flag2) & FLAG_Mask; //原代码
  lastevent = (vu32)( (vu32)(I2Cx->SR1) | ((vu32)(I2Cx->SR2) << (vu32)16) ); //qzm为了确认而加入的,实际效果和用库的是一样的结果。
  lastevent &= (vu32)FLAG_Mask; //qzm为了确认而加入的,实际效果和用库的是一样的结果。

为了确认,我也把 Fun中的所有变量改为以v开头的,以确保不被编译器所优化掉,编译代码也不作任何的优化。但是全速时I2C获得的事件会多出个BTF位,而在一开始时如果先进入DEBUG,加上断点,然后运行,lastevent == 0x30001(之也说明了库代码是不存在BUG的),如下图所示:

image

这应该是STM32 I2C硬件接口的BUG,解决方法如下:


image

把库中的I2C中断事件判定结合SR1和SR2的思路相反,我把读SR1和SR2明确地分了出现,并进行确认, PASS, 给模块加上防守代码,做好文档, 至此,模块的生命周期进入到白盒测试和黑盒测试阶段.

  • 9
    点赞
  • 62
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
如果你想在 JavaScript 中将 HTML 元素添加到地图中,你可以使用第三方库,如 Leaflet 或 Google Maps JavaScript API。 下面是一个使用 Leaflet 的示例代码: ```html <!-- 在 HTML 中添加一个地图容器 --> <div id="mapid" style="height: 400px;"></div> <!-- 引入 Leaflet 库 --> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.7.1/leaflet.min.css" integrity="sha512-qZm8vLkC8hdnQVqjv3M9dZwB3i6J5jE/6wE0C6tye2TtXfGKgYc2g6j6N2WzR2Wd1z+yzwtu+6jN2lRyJ8wNlQ==" crossorigin="anonymous" /> <script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.7.1/leaflet.min.js" integrity="sha512-BnV5CJfX1eU7P3kELTcDmJ5J5Hh3dK6fFV4NQqktdq6u4fOqS4t2yK4RyqLhLsJ0T9vQWY7fR4lrlOye9Wt4LQ==" crossorigin="anonymous"></script> <!-- 在 JavaScript 中创建一个 Leaflet 地图对象 --> <script> var map = L.map('mapid').setView([51.505, -0.09], 13); // 添加一个 OpenStreetMap 图层 L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { attribution: 'Map data © <a href="https://www.openstreetmap.org/">OpenStreetMap</a> contributors, ' + '<a href="https://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>, ' + 'Imagery © <a href="https://www.mapbox.com/">Mapbox</a>', maxZoom: 18, tileSize: 512, zoomOffset: -1 }).addTo(map); // 创建一个 HTML 元素 var marker = L.marker([51.5, -0.09]).addTo(map); marker.bindPopup("<b>Hello world!</b><br>I am a popup.").openPopup(); </script> ``` 在上面的示例代码中,我们首先在 HTML 中添加一个 `div` 元素作为地图容器,然后在 JavaScript 中使用 Leaflet 库创建一个地图对象,并将其与该容器关联起来。然后,我们添加了一个 OpenStreetMap 图层,并在地图上添加了一个标记(marker),并将其与一个弹出框(popup)绑定在一起。 如果你想使用 Google Maps JavaScript API,可以参考 Google Maps JavaScript API 的官方文档。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值