你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

基于STM32延时函数怎么起作用

[复制链接]
攻城狮Melo 发布时间:2023-3-17 16:28
有人利用STM32做开发,基于片内SYSTICK定时器做延时,即利用SYSTICK的周期性溢出中断来实现指定延时。可是他发现自己编写的延时函数似乎不起作用。代码也很简单,颇为奇怪。
( H3 [) U" t9 P' `  j
我这里基于他的功能模拟一段测试代码,如下截图所示:
" g5 }8 n& k% Z: O' V0 v
微信图片_20230317162707.png
4 f" z# n9 Z4 {& T1 Y

0 L' z$ W+ h) B: K1 p# }
上面代码在主循环里,每一轮循环就是先让LED闪烁5次,然后熄灭一会。就这样循环进行。其中,有个全局变量Delay,决定每次延时长短。

3 l' {1 G. _3 r. O  A
uint32_t  Delay;  //Define a globle variable
7 v# ]" M: h2 I1 N& c
关于SYSTICK的中断服务函数也很简单,像下面这样:
' H1 |" t1 S1 I+ l1 T+ J: j
+ I1 o4 l/ g8 N4 R; T, }( _# V; @
微信图片_20230317162702.png

! A2 Q+ N% s; y7 m1 ?  j% N4 ]& t; s2 ^  t' `6 j' z& u
所有测试代码就是上面两个截图所涉及的内容。
9 Y4 g8 R0 Y) l9 P- |/ V

4 t, u5 i+ {; W, G& o7 _  p. e
在测试过程中发现,当我们开启IDE的编译优化,尤其优化等级较高时,上面测试代码就没法按预期运行。最直接的体现就是看不到LED的周期性闪烁。
- v) A5 u7 n. e
不过,当我们不开启IDE的优化或设置较低优化等级时,上面的功能往往能够得以正确运行。看来,优化对我们的代码造成了影响。
, w6 R; |, n  M: E; i; |
这里代码非常简单,最可能受到优化影响的就是那个Delay全局变量,是个共享变量,主流程和中断程序都要访问它。一般来讲,像这种可以被多个地方访问的共享变量,原则上都要冠以volatile。不过,目前它的定义中并没有加上这个Volatile修饰词。
. \! P; P+ G: v" p8 e5 @7 c
我们不妨将Delay的定义加上volatile,然后把优化等级设置O3来验证下。【此时我使用的是ARM MDK IDE】

/ S1 r# g# R. o9 Z; Q4 ]7 m) s1 G8 C8 l2 J4 t+ k
微信图片_20230317162658.png
  m" J/ c0 ^* m0 ~( {. W: m1 L" M
基于上面的定义和优化选项,程序运行是正常的。看来,这里的关键问题是Delay变量没加volatile,开启优化后让真正的代码偏离了用户的预期。

' F/ e9 [+ f0 {' S  f& g! A
优化后的代码没法直接从用户源代码看出端倪来,我们往往可以借助汇编文件来看看编译后真实代码的样貌。我们不妨就看看那段主循环代码在开启优化但又不对Delay变量加volatile时编译后的汇编代码。9 ?: |9 ]' u8 w$ f* P- `
2 K" [6 [( V; ?5 U6 f' T2 S0 ~0 Y
微信图片_20230317162655.png

9 r* Q8 [3 F" S0 C' K1 E! u1 G
我们可以从上面汇编代码看到,优化后的主循环代码运行时最后变成在黄色语句那里原地踏步踏了。说实在的,这块被优化后的代码相比用户源代码已经面目全非了,难怪看不到LED的周期性闪烁了。当然,如果对Delay变量加上volatile并开启优化,此时的主循环汇编代码跟上面的贴图就相差甚远,这里就不贴出来了。
6 j3 d' B7 X/ T: i; S" q
其实,很多有过使用STM32库或基于CubeMx配置初始化文件的工程师可能知道,HAL库里就有现存的可以调用的延时函数HAL_Delay()。平常我们在调用这个函数时怎么没感觉有类似问题出现呢。比方将前面的主循环代码换成库函数的写法,并使用库函数HAL_Delay().: ]4 l7 _- _2 w& I9 b6 p
1 C8 M0 I; u' m+ E5 o- r0 s
微信图片_20230317162649.png

* v* ]; z% n6 t- e- E0 y; O. p% Y
% P6 [2 X$ ~% U$ s# q
如果这样写,即使开启O3优化等级依然没有问题。感觉上我们前面的写法跟库函数HAL_Delay()的也没什么差别,都是利用SYSTICK的周期性中断进行统计溢出次数而已。
& Z5 ~3 L) H' a+ ]. r8 _' j
HAL_Delay()用到一个统计溢出次数的全局变量uwTick,类似前面定义的Delay变量,它被SYSTICK中断服务程序动态修改,在HAL_Delay()里面经HAL_GetTick()实时读取,显然这个uwTick在定义时也应该加上volatile才合适。

6 X! @0 f- l/ h# m4 I6 O
我们不妨看看该变量的定义:
; X4 a) b' S/ b: L0 z& ^; x
微信图片_20230317162640.png
# p1 }0 s8 t' @6 L6 r& ]
2 _( p, S1 d# ~
看来,HAL库早就考虑到该变量的特性,在定义时已加上了volatile.

( A% @3 k  \1 u& E  k
稍微小结,本篇提出的问题跟代码优化有关,因为个别变量定义得不够规范,在开优化或优化等级较高时会发生状况。对于那些可能被多个线程访问的共享变量原则上都要加上volatile。具体到MCU应用,对于那些既会被主程序访问也会中断服务程序访问的全局变量,在使用时建议加上volatile,否则,不开优化或优化等级比较低时可能没任何问题,当开启较高优化等级时可能就问题来了,若代码庞大、共享变量又多时,查起错来可能就比较辛苦。对于编程人员来说,volatile不是什么新鲜玩意,但编程过程中有时可能忽略它,这里结合实际案例来了解、理解它可能更为具体些、深刻些。

% L) _- P2 y# h" A  |  i. X
转载自: Miler
如有侵权请联系删除
' ]4 [$ U- I4 P& ?' g+ P
5 R1 F; {7 M4 q5 D/ Q' U
赞 收藏 评论0 发布时间:2023-3-17 16:28

举报

0个回答

所属标签

相似技术帖

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版