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

关于SYSTICK延时函数的两个小疑问

[复制链接]
STMCU小助手 发布时间:2022-9-16 15:55
我们知道,STM32库函数里通常使用来自内核的系统定时器SYSTICK作为时基,实现计数延时。一般来讲,ST公司提供的库函数里将SYSTICK定时器配置为1ms的定时器中断,每产生1ms中断则相关中断事件计数变量加一。具体应用中我们经常会调用那个Delay()函数以实现计数定时,做延时或超时管理。
3 @3 l# a: o6 D4 U! w( C% @8 O
微信图片_20220916155527.png

1 J. |% b) V) l1 y) _8 r9 c
微信图片_20220916155521.png

/ P1 F; i) l/ s' U
有人在阅读ST提供的LL库里的这个延时函数时,发现代码里对延时参数总是做了个加1操作,代码如下:

' _: u& `" {3 L: R& s9 S
微信图片_20220916155517.png
% O+ k% g+ m2 E0 j4 h# N; D
上图中红色代码,程序进来后就对给定的延时参数做了个加1操作,这不将1ms延时变成2ms了吗?
其实,这个地方已经有做了注释,就是为了保证有1个最小的延时等待,函数参数给定1ms的延时,经过这样加1操作后就能保证至少1ms的实际延时,极限情况的确可能达到2ms,但不会超过2ms。但如果这里不做加1操作,函数参数给定1ms的延时,实际延时最短的情况的极限是0,最长的极限是1ms.
我们不妨借助下面图形一起看看,可能更直观点。

1 k" O0 d( H% T! W; K
微信图片_20220916155513.png
1 i5 B5 @0 n+ A5 R5 e8 ]! L7 z
假设调用Delay函数时给定延时参数为3,首先读到的滴答中断计数值为5,显然在上图的绿色区除了两个边界点外的任一时刻读到的数据都是5。如果先不对延时参数加1,那么实际延时就落在(2ms,3ms)之间;如果对延时参数先加1,那么实际延时就落在(3ms,4ms)之间。换言之,拟定延时n个ms,若做加一操作,实际延时落在(n,n+1)ms开区间;若不做加1操作,实际延时落在(n-1,n)ms开区间。
那这个的加1操作是必须的吗?我不这么认为,加不加1没有原则性问题。当我们把该定时器的溢出中断时间定好后,精度就定了,以1ms中断为例,我们就得接受1ms内的误差。这个地方知道怎么回事就好,毕竟中断代码的编写、延时参数的拟定都是我们自己定的。
上面代码是ST提供的LL库里关于那个延时函数的写法,看看HAL库的写法是不是一样的,不妨顺便看看。【下面截图便是Hal_Delay()函数代码】
/ h, Q4 M* M  F3 M' w% t
微信图片_20220916155510.png
) x. ?# P0 V2 [- g
显然,在HAL库里的Delay函数里进来后也首先做了加1操作,跟LL库的做法一样。
上面HAL库延时函数代码里的Tickstart是调用Delay()函数时首次读到的滴答中断计数变量的值,wait是延时值,Hal_GetTick()函数动态读取到的滴答中断计数变量的值【uwTick】。对于上面while语句里面的uwTick与Tickstart的比较的写法,有人可能会产生疑问。当Hal_GetTick()函数读取到的滴答中断累加值uwTick小于或等于Tickstart时还能得出正确的比较结果吗?
比方像下面这种情况,某无符号整型变量循环累加计数,现在需求得Val2与Val1的差值以测试信号宽度。

) g, ?) T5 b  b+ ?
微信图片_20220916155506.png

' e% i( R8 B# U% B7 q' Z4 U, {
假定上面数据的计数周期为T,且Val2与Val1的间隔不超过1T。此时二者的间隔用数学表达式就是:Val2+T-Val1。这里的代码似乎没有考虑这个溢出周期的问题?可是那个uwTick完全可能出现小于tickstart的情况啊。

) F1 W+ ~/ s& b: H
微信图片_20220916155503.png
% y! ?2 s& Q; K4 E
这是怎么回事呢?我也一度很怀疑这个写法,当时也做了些简单测试。对于测试结果,现在回想起来当时也没做冷静的分析而做出了错误的判断。
回过头来想,按理说这个代码不该有问题。毕竟是老代码了,何况之前也没有人反映这个地方有问题。难道哪里误会了?
因为当前代码用到的几个变量都是32位无符号数,刚开始测试时也是基于32位数做的,结果动不动就很大,不容易判断正误。我干脆将相关数据全部改为8位无符号数,这样测试起来就方便些。
我先准备了下面的0~255的循环计数表格。显然计数溢出周期为256。

2 t$ C- z5 |( b+ t& ?/ h
微信图片_20220916155449.png

2 V3 q! f' z* ?* Y7 c
现在要在任意一个不大于1个计数周期内求取任意2个数据之间隔,即求这两个无符号数之差。假设计算上图中第1行数据5与第2行数据3的间隔值。这里用StartValue表示5,NowValue表示3,Result表示结果,都定义为8位无符号类型。
参照库代码写法,代码该这样写:Result1= NowValue – StartValue;
按照我的想法,代码该这样写: Result2= NowValue + 256 – StartValue;
经过测试,结果是一样的,也不难验证是正确的。
" K8 S) P0 @  H; I  B; f  y
微信图片_20220916155445.png

2 R+ H+ [6 k! w# X  u
对于这个结果,意外,但也意料之中。因为我在添加那个256周期值时心里就在犯嘀咕,有所警觉了。我现在是两个8位数的加减,加个256按理是不会影响结果的,这样推理下来,两行代码的结果就本该一样。
我们再回头看看上面HAL库函数中延时函数:
3 q% h3 K" B1 f+ l1 z3 O: b! m$ V
微信图片_20220916155443.png
, Z( o, H5 Y4 H
这里tickstart和wait以及函数HAL_GetTick()读到的变量是uwTick值都是32位无符号变量,显然,uwTick与tickstart的延时间隔也不会超过0xfffffff。
如果uwTick小于或等于tickstart,通常我们需要加上溢出周期,这个周期值等于32位数据模值,用16进制表示就是1后面跟8个0。经过测试发现下面代码的两种写法在微处理器的计算结果也是一样的。【uwTick对应下面截图代码中的NowValue, uwTick对应StartValue】
% a9 c9 b) I( g' Q6 u
微信图片_20220916155440.png

+ \+ j3 K7 E9 o; w: F6 L7 ]. b. @
因为都定义成32位数据宽度了,结果很大,但是正确的。很明显上图中A行代码更简洁。看来,库代码这里这样写是没问题的。
上面两次测试除了位宽差别外,其它都一样。怎么感觉求算不超过1个计量周期宽度内任意两个数据的间隔值不用考虑那个溢出周期呢?
其实,只要是任意两个相同无符号数据类型数据做减法,且数据所能计量的最大值为其满量程值【比如8位对应255,16位对应65535】,求算任意一个周期内两个数据的间隔值时,使用上面A行代码写法是没有问题的,但这并不等于说不用考虑溢出周期的问题。
以现在测试为例。A行程序代码在微处理里完成的就是数学表达式NowValue 加上 溢出周期值 减去 StartValue的结果,我们不妨将A行代码的计算过程做个翻译解读就看得出来。只是B行代码里再多加1个溢出周期值也不影响计算结果。【注:前面两个测试的溢出周期值刚好等于各自所用数据宽度的模值。】
但实际应用中,数据变量的计数周期往往并不等于所用数据宽度的模值。此时前面A行代码的写法就行不通了,而要用B行代码写法。即A行代码写法只能用在特定场合,B行代码写法具有普适性。
我们不妨看看下面表格。这个表格跟前面0~255的表格类似,只是换成由一系列0~99的数据周期性排列而成。

/ c' _1 R4 D4 r1 K1 k' k) |7 A* q
微信图片_20220916155436.png
. l9 t7 h& M; M6 d: X
同样,我们求第一行的数据5与第2行的数据3之间的间隔数,这里的数据都定义成uint8类型。显然这里计数周期为100,而非256,此时的代码就不能采用前面A行代码写法。
我们可以看看验证结果:【显然第一种写法的结果是错的,不难判断正确结果是98】

- F+ x  v$ M- Y' Z2 P
微信图片_20220916155433.png
/ d& _& e" u, B
我们继续看一个实战性的例子,看一个有关使用TIMER测量脉冲宽度的例子。
下图斜线表示计数器计数方向,现在利用TIMER的捕获功能测量下面一段脉冲的高电平宽度。
" ?7 D" w! _3 \$ M1 v% f
微信图片_20220916155429.png
% X6 j! k2 P5 B5 y; h5 _1 E$ @
假设TIMER为16位,计数器向上单向计数。于脉冲的上沿和下沿事件分别捕捉到两个计数值Val1和Val2,二者都定义成16位无符号整型变量。
结合图形可以看出,脉冲虽然跨越了溢出点,但脉冲宽度没有超过1个计数周期,Val2的值小于Val1。此时我们求算t1的宽度,正常都会这样写:
t1 = Val2+ ARR+1 - Val1;
当然,如果说你把ARR刚好设置为0xfffff, 此时程序代码就可以简化成 t1 = Val2 - Val1。
OK,关于STM32片内Systick定时器延时中断应用的两个小疑问就聊到这里。问题虽小,但可以牵扯出不少东西供探究。。

. M, Z/ T4 e  Y
转载自:Miler
1 `" L  {1 m2 o" w- I
赞 收藏 评论0 发布时间:2022-9-16 15:55

举报

0个回答

所属标签

相似技术帖

官网相关资源

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