1mx00wcihxi64011392357.png
; W. D3 o3 d7 m& A" x- Y' g1 Q' C" _1 M8 X
问题场景在FreeRTOS中创建了线程A、线程B,其中线程A优先级大于线程B。线程A、B任务代码如下:
6 p# m0 v1 f- @' ovoid A(void *argument)% S3 T* W9 j; o2 N: _+ z
{ R- a i+ I- o/ { ]" Y
while (1)
/ f: A& O( u2 S H1 \4 }( ~! } d+ [ {
1 r$ w; l1 f/ ?! D" L printf("A\r
5 x; n' R+ x# Z6 L( I");- e+ s b6 ], g
HAL_Delay(1000);4 K2 n, o4 X0 l& ~% g4 A0 B- ~2 a
}' d$ Z I$ V! A, \
}
8 ^( U& f% V2 E, M' I6 W* y6 hvoid B(void *argument)
9 l$ r5 I, s2 K1 }, v* P{
: D( h5 A5 p, j; v7 W. C4 [ i while (1)4 V/ Q8 ]) P+ ?( i. K
{
2 h, Y; \" ^4 W printf("B\r- d5 H! z. `8 r0 v8 N
");
2 O! g" M& Y, E( p& ~8 m5 S HAL_Delay(1000);2 N" {* R' V) a4 L0 X
}/ ~. T9 H+ i, j+ e+ v
}1 m s2 O# T8 Z% `3 m9 b
烧录程序后查看串口数据发现只打印了A而不打印B,说明只执行了A线程没有执行B线程。) v( j. C3 R# h0 k2 I; Z9 w
) ~" H) c3 L- ~问题原因
- j* A+ y4 e8 |, iHAL_Delay是由ST提供的STM32 Cube HAL库中的一个函数,通常用于在STM32微控制器上实现简单的延时。HAL_Delay函数使用系统时钟来进行延时,并且在延时期间会阻塞整个处理器,也就是说,它会使处理器暂时停止执行其他任务和代码。. }# l! V; _5 u% N9 N" \
在开始运行线程之前,线程A、B处于就绪态,由于线程A优先级比线程B高,FreeRTOS任务控制器优先选择线程A运行,此时线程A进入运行态。随后线程A打印A,然后被HAL_Delay函数"阻塞",注意此时的"阻塞"并不意味着程序进入了阻塞态,由于HAL_Delay阻塞的是整个处理器,因此FreeRTOS无法进行其他线程的调度,也就是说,HAL_Delay同时阻塞了线程B。当HAL_Delay函数运行结束后,线程A重回就绪态,由于线程A优先级比线程B高,FreeRTOS任务控制器优先选择线程A运行,循环往复,线程B不被执行。 l, S8 ~: i! a$ I6 O
解决办法osDelay是FreeRTOS(Real-Time Operating System)中的一个函数,用于实现任务的延时。FreeRTOS是一个开源的实时操作系统,专门用于嵌入式系统。osDelay函数允许任务挂起一段时间,然后由操作系统调度器在指定的时间后重新运行该任务。在等待期间,任务会被放入挂起状态,让其他任务有机会运行。2 E S2 T0 c* V; }
也就是说,当调用osDelay时,线程A进入阻塞态,此时任务控制器选择进入就绪态的线程B执行,循环往复,线程A、B同时被执行。我们可以将任务A和B进行如下改动,即可看到既打印A又打印B。
- i8 G! Q) {3 ~! z6 B1 q7 Y6 g
3 L- b# c' J0 J% q7 R3 avoid A(void *argument)- _1 [8 [3 a: D* F9 y( ?4 m
{1 ?- r2 Z- N1 R1 [
while (1)5 V* C) t. N9 J
{1 E4 E4 b; E! T K
printf("A\r
9 r4 M# _! ^2 h) {+ J& o4 B");4 o6 F4 l( Y, Z
osDelay(1000);* b5 a* K0 m& _
}; T# {2 z& U# G0 M* x4 e
}
, l' Y- y' N6 v7 ]# D8 svoid B(void *argument)# y: n7 d/ ?0 e2 n" f- D. E
{# O6 Y3 y! J" z) e [
while (1)% V" w5 K9 z' Q+ L
{" M7 O d; J- u* H+ p' W4 j
printf("B\r; w5 u5 p! L6 I- M& k6 ]! p. c. T8 v+ y
");
# H8 R" T' `2 A( n osDelay(1000);& m( j1 Z% _; a
}& O. c5 m- j0 M- ^4 b& l
}& f2 `$ `) }- u9 l% _* R8 o
使用osDelay可能带来的问题观察一下HAL_Delay和osDelay的函数原型:
5 ^- Y, T6 A$ y- A8 w' \
1 N0 A" \* p' k k: ^9 z9 ]4 {/**
" c8 J0 a$ r' F6 N- \ ^ * @brief This function provides minimum delay (in milliseconds) based
/ ]/ m, z# T4 b1 g# z/ K+ i */
" N z$ K. ^ y6 X( e* @& ~__weak void HAL_Delay(uint32_t Delay);
. l5 a: q- G( X: R) ]1 V/*, ? ]! m5 G4 N5 `; ~" M
Wait for Timeout (Time Delay).- s. x6 o, M, e z7 ]6 K
*/
9 H& U) \$ [! Z- gosStatus_t osDelay (uint32_t ticks);
" c4 x* ?* u4 k/ ]8 x8 U D可以看到HAL_Delay函数的目的是提供毫秒级别的延时,意味着当你输入HAL_Delay(500),硬件会尽量延时精确到500ms的时间。
, z! P* P# }( s& _6 X! B3 d与之不同的是,osDelay函数的输入是ticks。ticks是一个计时单位,表示任务将被挂起的时间长度。每个tick的时间取决于FreeRTOS配置的时钟节拍(tick)周期。例如,如果tick周期为1毫秒,那么传递参数ticks为10就会使任务挂起10毫秒。由此可见,osDelay函数延时的时间和一个ticks记时时间长度有很大关系。
8 ]# c* c0 o7 O那么如何确定ticks具体代表多长时间呢?首先我们应该找到用于配置的头文件,通常这个头文件名字叫做FreeRTOSConfig.h。其中,configTICK_RATE_HZ配置选项的值表示每秒钟系统时钟节拍(tick)的数量。configTICK_RATE_HZ的值一般默认被设置为1000,表示系统时钟每秒产生1000个tick,即每个tick的时间间隔为1毫秒,此时osDelay对单个任务延时的时间长度和HAL_Delay近似。
' ~3 L* C5 |3 {4 V- n# d Q/ v==========
|" ]5 m( l: Z往期回顾:STM32中断,看着一篇就够了) i1 D, J0 F s6 U* v
STM32CubeMX介绍
& Y/ h* S6 V$ ~【蓝桥杯单片机】第二章 软件安装/ M u7 l$ M! L. U" \" }8 A8 s* I
【蓝桥杯单片机】第一章 大赛概述9 g' n' |% S6 Q' g0 t
【蓝桥杯嵌入式】第九章 PWM8 u1 j/ ]2 M/ E6 @3 n6 T E7 G
==========, q. b( ~; U, G& w
rbwfewkdtzp64011392457.png
: J6 @% c# V N8 J' ~ y! _
4 t- Y* w+ I4 P6 D# ~& z
/ a8 b2 @ A4 ?9 P
nkpk5tj5drc64011392557.png
|