发新帖本帖赏金 4.00元(功能说明)我要提问
12下一页
返回列表
打印
[信息]

【实战经验】跳不出的 while 循环

[复制链接]
4448|27
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
本帖最后由 香水城 于 2017-8-16 15:12 编辑

跳不出的 while 循环

问题:
该问题由某客户提出,发生在 STM32F103VBT6 器件上。据其工程师讲述:在为 STM32 调试软件过程中,遇到了一个怪现象:有如表(一)所示的一段程序中,KeyIn 是一个全局变量。当有按键按下时,键盘的中断服务程序会将对应的键值放入其中,当按键释放后,键盘中断服务程序则把 RESET 值放入该变量。行(1)的条件语句在主程序中,检测有无按键按下,并处理。行(2)则是等待按键释放。调试时,在行(1)和行(2)处各设置一个断点,然后全速运行程序。当程序停在行(1)时按下按键,继续全速运行程序。当程序停在行(2)时,放开按键,继续全速运行程序。这时,发现程序没有向下执行,而是依然回到并停留在行(2)处。打开观察窗口,观察变量KeyIn 的值,确认其值为 RESET。再次起动全速运行,程序依然回到行(2),而不是向下执行。

调研:
检查其软件工程,确认其所使用的工具链为 IAR Embedded Workbench IDE 5.3,工程设置中的优化等级为 High+Balanced。重复测试,确认现象如其所述。使用反汇编窗口检查编译器生成的指令,如图(一)所示:

从图(一)中得知,由语句 while(KeyIn != RESET) 生成的指令位于地址区间0x800032C-0x8000330,共有 3 条。其中,指令 LDR R1,[R0]负责将变量 KeyIn 的值装载到寄存器 R1 中,指令 CMP R1,#0 对该值进行检测,而跳转指令BNE.N ??main_1 则是实现循环。进一步观察,发现 3 条指令中,只有后两条参与了循环,而指令 LDR 只执行一次,不参与循环。于是可以得出这样的结论:如果在执行该循环语句的第一次循环时变量 KeyIn 的值非 0,那么装载到寄存器 R1 中的值就非 0,于是比较结果为“不相等”,从而进入下一次循环。由于从第二次开始,执行该循环时不更新寄存器 R1 的值,所以无论变量 KeyIn 的取值如何,比较的结果都是“不相等”,都会再次进入下一次循环。于是,一个无休止的循环诞生了。
将工程的优化选项更改为 High+Size,重新编译并观察反汇编结果,如图(二)所示:


这次由语句 while(KeyIn != RESET) 生成的指令变为 4 条,而且在每次循环中都重新向寄存器中装载变量 KeyIn 的值。从实际执行的效果上看,符合源代码所描述的逻辑。
将工程的优化选项改回到 High+Balanced 模式,并修改源程序,将变量 KeyIn 定义成volatile 型变量,如表(二)所示:


重新编译并观察反汇编的结果,如图(三)所示:

这种情况下,语句 while(KeyIn != RESET) 生成的指令为 3 条,但每次循环都重新装载变量 KeyIn 的值到寄存器。测试其执行效果,符合源代码所描述的逻辑。

结论:
通过以上的测试可以得知,该问题是由于编译器对 C 语言代码进行高度的优化时,对某
些语句解析错误造成的。

处理:
将变量 KeyIn 定义成 volatile 型变量,如表(二)所示。

建议:
既然这是一种由编译器的编译错误造成的问题,可否期待新版本的编译器为我们加以改正?非常不幸的是编译器真的很难做到这一点!更不幸的是几乎所有的编译器都有类似的问题!因为,在引用多个变量做循环处理时,如果对每个变量在每次循环中都重新装载,势必大大的降低代码的效率,根本谈不上优化。而编译器又无从分辨出哪些变量有可能发生改变,于是,只好认为只要在本循环中没有对其更改的变量就不会发生变化。所以,不能寄期望于编译器为我们解决这类问题。但这一问题还是要解决的,特别是基于多任务操作系的软件,其中有大量的变量由多个任务共享,如果发生此类问题将导致严重的错误。如何解决?有一个关键的切入点,就是:虽然编译器分辨不出哪些变量有可有发生改变,但是软件的设计者却是知道的。这样,完全可以通过某种方式告诉编译器,哪些变量有可以发生改变,或者通过一些手段迫使编译器对某个变量重新装载。将变量定义成 volatile 型变量,是告知编译器该变量有可以发生改变的一种方法。通常,外设的寄存器都定义成 volatile 型变量,因为这些变量的值有可能被硬件改变。定义成 volatile 型的变量在使用时往往不如普通的变量方便,因为要考虑对其读写的次序问题。比如表(三)所示的程序,编译器在对其编译的时候,会毫不客气的给出的警告:

可以人为的为这些变量设定读写次序,而避免产生相关的警告。如表(四)所示:

从表(四)中可以看出,定义这个次序不是一般的麻烦!那么,对于普通的变量,如何迫使编译器对其重新装载呢?我们可用函数来代替变量。比如,对表(一)所示的程序进行修改,得到表(五)所示的程序:

其中的 GetKeyIn()函数定义在另外一个 C 语言文件中。该函数只有一行语句,就是返回变量 KeyIn 的值。以 High+Balanced优化参数对表(五)的程序进行编译,并观察其反汇编代码,如图(四):

从图(四)中可以看到 while(GetKeyIn() != RESET) 语句被编译成了 3 条指令。其中,BL GetKeyIn 指令是用来调用函数 GetKeyIn()以取得变量 KeyIn 的值的,而且在每次循环中都对其调用,以重新装载变量 KeyIn 的值。在使用这种方法时,返回变量取值的函数一定要放在另外一个 C 语言文件中才有效。因为编译器进行编译的时候,是对工程中的 C 语言文件逐个进行的。由于被调用的函数不在当前的 C 语言文件中,所以编译器不知道该函数做了哪些事情,只好认为其返回值是有可能改变的,从而每次循环都对其调用,于是我们的目的就达到了。

对应的PDF:跳不出的while 循环
更多实战经验请看:【ST MCU实战经验汇总贴】

打赏榜单

21ic小喇叭 打赏了 4.00 元 2015-08-27

沙发
cornrn| | 2015-8-26 09:33 | 只看该作者
GOOD

使用特权

评论回复
板凳
fanxsd| | 2015-8-26 09:37 | 只看该作者
该外部引脚 上升沿和下降沿都产生中断?

使用特权

评论回复
地板
tian111111| | 2015-8-26 09:51 | 只看该作者
学习了

使用特权

评论回复
5
SLHSu37| | 2015-8-26 10:14 | 只看该作者
好棒的经验,学习学习

使用特权

评论回复
6
mark0668| | 2015-8-26 10:33 | 只看该作者
学习了.

使用特权

评论回复
7
cauhorse| | 2015-8-26 11:30 | 只看该作者
图一中,解析完,??main_1标号怎么跑到 LDR r0, [r0]后边去了;前面的if(KeyIn != RESET)对应语句,LDR r0, [r0]却处在标号之下;
对比两处语句似乎完全一样,那同样写成if(KeyIn != RESET),在High+Balance配置下编译,实际会不会是while判断条件的效果呢

使用特权

评论回复
8
sjw1716094642| | 2015-8-26 13:21 | 只看该作者
香水城的帖子一直都这么棒呢

使用特权

评论回复
9
mmuuss586| | 2015-8-26 15:01 | 只看该作者

学习了;

使用特权

评论回复
10
ayl439| | 2015-8-26 15:45 | 只看该作者
看一下

使用特权

评论回复
11
55854234| | 2015-8-26 15:58 | 只看该作者
不错 学习了

使用特权

评论回复
12
丁弋宇| | 2015-8-26 19:58 | 只看该作者
全局变量,建议全部加volatile关键字
尤其是中断里面会修改的,100%必须加

使用特权

评论回复
13
风清扬mxz| | 2015-8-26 22:06 | 只看该作者
哎哟,不错哟

使用特权

评论回复
14
w522930954| | 2015-8-27 09:22 | 只看该作者
这个问题以前就碰到过。。。后来才有了加volatile的习惯。。
习惯是碰壁之后自然而然就产生了。。。

使用特权

评论回复
15
w522930954| | 2015-8-27 09:22 | 只看该作者
丁弋宇 发表于 2015-8-26 19:58
全局变量,建议全部加volatile关键字
尤其是中断里面会修改的,100%必须加

全部加效率有点低啊。。。

使用特权

评论回复
16
han0097| | 2015-8-27 10:18 | 只看该作者
学习了!

使用特权

评论回复
17
丁弋宇| | 2015-8-27 11:19 | 只看该作者
w522930954 发表于 2015-8-27 09:22
全部加效率有点低啊。。。

谁的效率低:lol
你不加,效率就高了?

使用特权

评论回复
18
FireRiver9| | 2015-8-27 11:49 | 只看该作者
其实在编程时经常会遇见这样的问题,版主分析的很深刻

使用特权

评论回复
19
lgq1542380129| | 2015-8-27 18:38 | 只看该作者
香版主分析的面面俱到

使用特权

评论回复
20
734774645| | 2015-8-28 08:55 | 只看该作者
全局变量,建议全部加volatile关键字,否则容易被优化掉。

使用特权

评论回复
发新帖 本帖赏金 4.00元(功能说明)我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

认证:意法半导体(中国)投资有限公司
简介:STM32技术专家

596

主题

17108

帖子

289

粉丝