空循环与 Volatile

今天下午啊,在帮老陈同学调试它的小屏幕程序代码。有段程序他在调试时可以运行,但直接编译下载到板子上就不能运行。通过控制变量法,我们可以知道应该是调试程序和编译程序存在某些不同。再看问题代码,是一段等待硬件响应的空循环。看到这里,我忍不住地向空循环中加了句 delay 延时。果然,加了后就能跑了。这时不禁联想到之前微机课设时看到的编译器用户手册中说道,对于一些奇怪的代码,编译器是会进行优化的。于是,把编译器的优化选项关掉,就可以成功运行了。查看配置后也发现,调试时是会阻止一些会影响调试的编译优化。

但是问题没这么简单,编译器怎会连这些基本的东西都没考虑到呢?出于好奇,把优化后的汇编程序弄出来看了看,其循环部分的大意如下所示:

WHILE_LOOP:
CMP R3, #1
BNE WHILE_LOOP

好家伙它根本没重新读取值就比较,那不就死循环了吗!这时结合对优化的记忆再查阅 ARMCC 手册,可以发现它在讲 volatile 时提到了类似的例子。再看 while 循环里的条件判断,是一个判断外设变量的值,再进去看,果然这个库里的外设变量没有用 volatile 修饰。加上 volatile,开启编译器优化,上传程序,能跑。问题解决啦。到头来,还是代码不规范的问题呀,编译器是有考虑到这种情况的。

后话

代码规范真的很重要,按规范写代码可以省心不少。之前还看到一个新闻,一个人因为代码不规范被同事枪杀了,所以为了自己与他人的生命安全,同志们在写代码时一定要按规范行事。多看看编译器等的官方文档、User Guide 还是有帮助的。

顺便在这里摘录一些从 ARMCC User Guide 上看到的编程原则吧:

循环编写

写循环时尽可能做到:

  • 使用简单的终止条件
  • 编写倒数到 0 的循环 (count-down-to-zero loops)
  • 使用 unsigned int 类型的计数器
  • 测试与 0 相等 (test for equality against zero)

使用 volatile

以下情况需要用 volatile

  • 访问内存映射的外设 (Accessing memory-mapped peripherals)
  • 多线程共享全局变量
  • 在中断或信号处理程序中访问全局变量

空循环与 Volatile
https://worranhin.github.io/2023/03/13/debug/
作者
Hin
发布于
2023年3月14日
许可协议