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

STM32芯片如何同时运行多个任务

[复制链接]
攻城狮Melo 发布时间:2023-6-28 13:47
在嵌入式系统中,需要同时处理多个任务的需求非常普遍。本文将介绍如何在STM32芯片上实现多任务处理,通过合理的任务调度和管理,充分发挥芯片的性能,提高系统的灵活性和效率。下面介绍两种多任务处理的实现方法
1. 时间片轮转调度机制
时间片轮转调度机制是利用定时器中断来实现的。设置一个定时器,当定时器中断发生时,切换到下一个任务的执行。下面是一个简单的时间片轮转调度机制的示例代码 定义不同的任务:定义任务的优先级、堆栈大小、维护一个任务列表,通过编写调度器代码,在合适的时机选择下一个任务来执行。
4 _* ^' `( k6 G' Z7 E
  1. #include "stm32fxxx.h"
    4 j, W* t0 M( k( G; e5 U- o$ ^
  2. // 定义任务的优先级
    , o7 R0 ~3 ^1 x* ]: |5 Y& c
  3. #define TASK1_PRIORITY 1
    1 E9 X) W  y, F6 ?* s( b' M7 J
  4. #define TASK2_PRIORITY 2
    # z9 m' K- I2 M9 ?
  5. // 定义任务的堆栈大小3 f+ e8 U8 M; x
  6. #define TASK_STACK_SIZE 128
    . c  W# E) u+ _6 g1 F6 {: z$ Q6 v
  7. // 定义任务堆栈空间
    ( Z( p5 P& e$ @. q
  8. uint32_t task1_stack[TASK_STACK_SIZE];3 h- C6 y! M8 X
  9. uint32_t task2_stack[TASK_STACK_SIZE];" i, n' D2 L% F2 L" Y* P9 j5 f/ q
  10. // 定义任务函数/ V& Y$ p2 f3 h( e
  11. void task1(void);
    5 m0 z. v0 |/ D6 W$ J3 E
  12. void task2(void);
    1 ]% [! z, M/ ^/ F+ B5 `" y# Q
  13. // 定义任务控制块结构
    2 d7 l9 Z" d2 N5 I
  14. typedef struct {, h' l) B2 ]) E. E/ m: Z( ~
  15.   uint32_t* stack_ptr;3 g( J& n# [0 k6 A% `# Q# P  m6 [
  16. } TaskControlBlock;# E6 ?) e* t0 f) U+ }! x! e
  17. // 定义任务控制块实例
    2 `, M& |* Y0 b, C5 Q# e! E/ r
  18. TaskControlBlock tcb1;+ @0 ?2 a; p& p# N0 |- ], v
  19. TaskControlBlock tcb2;3 V4 J% H0 I! s! g7 U
  20. // 定义当前任务指针- C6 ~  x2 y" g$ L$ t
  21. TaskControlBlock* current_task;
    7 Q2 k/ S- ]! J( ?" `0 n" O0 N
  22. // 任务1的函数
    1 k8 e$ J2 {, a% t' i7 ?. Z' `! L
  23. void task1(void) {
    & A* B# ^* ~9 t) y9 K: F
  24.   while (1) {+ m, i3 ^$ \' m0 n( k' ]- L* t
  25.     // 任务1的处理逻辑
    6 G  Q1 \; Y7 E- T& U, q8 h
  26.     // 切换任务
    . T0 _$ l5 K6 M3 `, J% {
  27.     __asm volatile("yield");; B) }: R( ~: P- w& Y7 h
  28.   }; x* V$ k+ k! M) a( L  G
  29. }
    % h. l2 ~& w) C2 z5 l
  30. // 任务2的函数, X4 M. \8 j+ Q. i+ L
  31. void task2(void) {
    / `& j4 q0 \$ ^3 O& X1 Q3 z
  32.   while (1) {% x" }  E& _/ Z/ C9 j$ U8 k
  33.     // 任务2的处理逻辑
    4 v0 ~1 t: ?6 m/ x
  34. % I' ^# f( N+ G6 Q9 s: F
  35.     // 切换任务
    * F1 Z. [, c% i- A$ R3 _4 I
  36.     __asm volatile("yield");
    + p( X4 X4 h% I' [7 \4 {* b+ I
  37.   }
    6 B( e/ I- Z, T; M+ q
  38. }
复制代码
1 f* x% }3 S( M# Z! p' [4 y

+ A8 [3 m2 O& O% B/ U) T
定时器中断:在中断处理函数中切换任务,并保存当前任务的上下文(包括寄存器、堆栈等),然后加载下一个任务的上下文,使其开始执行。
  1. // 定义定时器中断处理函数
    ; A  r' E+ v7 J& ]1 I
  2. void TIM_IRQHandler(void) {
    ! c( {; I( O, ~
  3.   // 切换到下一个任务- M# }# Z  T* B5 C$ U
  4.   if (current_task == &tcb1) {+ F' m& J5 {; |) a
  5.     current_task = &tcb2;9 O* ~  F  C+ E! D! H' M# h1 L
  6.   } else {+ Y  ]; c2 K; m4 G  t
  7.     current_task = &tcb1;
    ( q* i4 X4 x2 U  e% W' G
  8.   }! H) K3 Y# F( |, w' ?9 r* q  G
  9.   // 加载下一个任务的堆栈指针3 C2 W% [' Y2 @! h5 a# U
  10.   __asm volatile("mov sp, %0" ::"r"(current_task->stack_ptr));
    ! D; F' d. s" Z! m5 H  r
  11. }
复制代码

- c$ w( Y/ I( ^& h+ `
- B' Q" p9 x. c/ g3 \- W& }
多个任务之间可能需要进行通信和共享资源。可以使用全局变量或其他同步机制来实现任务间的数据传递和资源共享。
4 g& Y8 J2 _/ A9 R
  1. int main() {0 `  }. z" o& t0 p7 o6 y
  2.   // 初始化任务控制块
    8 f* e3 @) _' g+ K
  3.   tcb1.stack_ptr = task1_stack + TASK_STACK_SIZE - 1;
    8 W! R) v" U4 Z/ E3 T; K
  4.   tcb2.stack_ptr = task2_stack + TASK_STACK_SIZE - 1;5 n8 Z9 ]  u6 L0 B* b5 e% m

  5. ; q8 ]' V. g! P0 `4 a+ o8 L. S5 }
  6.   // 初始化定时器,设置定时器中断
    ' |1 I2 p" u! Y( b) f+ b1 `
  7.   // 这里使用TIM3作为定时器,具体配置请根据实际情况进行修改; \1 p( S9 |5 Q) ^; k
  8.   RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
    ! m+ l7 ?4 o3 X% I# O8 l* L
  9.   TIM_TimeBaseInitTypeDef TIM_InitStruct;
    $ o8 g; M. P! W' Z, ^
  10.   TIM_InitStruct.TIM_Prescaler = 1000;
    . M8 r  q8 c' s7 V3 m
  11.   TIM_InitStruct.TIM_Period = 1000;8 S& G; _8 \* K
  12.   TIM_InitStruct.TIM_CounterMode = TIM_CounterMode_Up;' _3 \) E( C2 H0 X( W' V4 Y+ p
  13.   TIM_InitStruct.TIM_ClockDivision = TIM_CKD_DIV1;- _# p% U: e/ N& e0 j1 c) P1 @9 m. |
  14.   TIM_TimeBaseInit(TIM3, &TIM_InitStruct);3 M7 T* R# n! ^$ b$ U: G
  15.   TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE);
    " `. N# v2 ]+ K" X) d1 Y) @) ^
  16.   NVIC_EnableIRQ(TIM3_IRQn);) R/ n+ I! i0 V" a- \3 ?
  17.   TIM_Cmd(TIM3, ENABLE);* h& u6 ]- j  q" P

  18. 5 O7 t1 m4 G& ~) o) x$ B
  19.   // 初始化当前任务指针
    2 l7 a- E. e. ?" C( K
  20.   current_task = &tcb1;
    , e/ {* @% f; b# f7 @$ \
  21. ) t* _1 N3 E7 P& Y& I+ [% o) u
  22.   // 启动任务1
    + d/ I9 R  r) l; t) ~& e* L, z
  23.   task1();
    3 w3 I; n, M4 s; {

  24. 0 J" Q( v; |% W; E3 h3 N" X( H
  25.   while (1) {
    # ?7 A( _: P: a+ }8 k5 w3 M
  26.     // 主循环,任务在定时器中断中切换+ b% s- M( B' o/ L: v) f* C, H
  27.   }
    ; q. m+ f( j3 ]. {
  28. }
复制代码
; ~% G. _+ ^  U- g. P. K# U+ K8 S

& w) [7 i) n& V5 V7 x, j
这种简单的多任务处理方式适用于较简单的应用场景,但对于复杂的多任务应用,建议使用RTOS来提供更好的任务管理和调度机制。
7 X) y3 {, b6 }+ k* h! v; q" q
2. 使用RTOS(实时操作系统)
RTOS是一种常用的多任务处理解决方案,它提供了任务调度和管理机制,简化了多任务应用的开发。对于STM32芯片,常见的RTOS有FreeRTOS、uC/OS等。以下是实现多任务处理的基本步骤:
创建任务:使用RTOS的API,在应用程序中创建多个任务。每个任务都有自己的代码和优先级
  1. void Task1(void* pvParameters)% u" I& \+ y: c% J  @2 M
  2. {( O& L& d% h( n# k8 v
  3.   while (1)3 K& [' P3 {5 R+ t- V$ m' R3 w
  4.   {
    , k5 d; p) z# ]# b
  5.     // Task1处理代码9 O2 H( \7 C! i  i
  6.   }  Y5 R, P3 J+ y9 {  m8 z* y
  7. }5 j5 d% e1 {8 L$ l! D1 D) T! \
  8. % X1 ^! h% X/ p5 k2 f% h
  9. void Task2(void* pvParameters)) c% g% O  E8 |- W0 j. i
  10. {+ ~% P* ~4 D( {! ^
  11.   while (1)' U% }! _" C, K
  12.   {) K2 f) N: j0 k$ T
  13.     // Task2处理代码2 Y: |  W7 N0 _
  14.   }& \% p3 X7 I( Z" W8 M! X) D' ?
  15. }
    ) V7 `2 l* s6 z; `

  16. ' y9 O( e, F$ ]
  17. int main()
    3 x) P, N1 m' @3 R) z
  18. {, \& u; D; c3 l
  19.   // 硬件初始化和其他配置6 X# A0 t- |% v3 D
  20.   // 创建任务" v/ V8 Z2 n! p5 r$ r. o
  21.   xTaskCreate(Task1, "Task1", configMINIMAL_STACK_SIZE, NULL, 1, NULL);1 {" p9 p7 A% J
  22.   xTaskCreate(Task2, "Task2", configMINIMAL_STACK_SIZE, NULL, 2, NULL);
    ! q% [4 Y# A/ x6 y* G

  23. ; r5 K7 [4 x9 w. w! K6 P
  24.   // 启动调度器# W  h0 H& Z* P4 U5 u2 k
  25.   vTaskStartScheduler();$ B0 S3 t3 t0 w' j: i% m6 z

  26. 0 a* z/ E% d+ Z: H. I: a
  27.   // 代码永远不会执行到这里
    0 s. V" J4 `* t* V
  28.   while (1)
    & o6 [) N; ~2 ~6 y" v3 s
  29.   {
    $ j4 A% p, q) p, L  b- @1 Q
  30.   }6 o$ q% h8 s& d% D7 @  x
  31. }
复制代码

6 D2 s  Q3 N, W+ v5 Q
内核参数:配置RTOS内核的一些参数,例如时钟节拍和优先级。
  1. int main()
    1 ^) Z+ Q; P9 [) e8 o
  2. {
    $ t( A. C) I$ r: j+ @
  3.   // 硬件初始化和其他配置: j* e1 p* d: a

  4. " H  H2 c! q' q2 t
  5.   // 配置FreeRTOS内核
    2 u; v4 [3 A& e: J, o+ b
  6.   // 设置时钟节拍7 C: M; C. u- u0 T/ B2 z2 {
  7.   TickType_t tickRate = 1000 / configTICK_RATE_HZ;! w! K8 A3 h2 b  t( u( J
  8.   TickTypeSet(tickRate);5 L$ t/ T! H2 r- ^: Z4 Q# i
  9. 8 m0 h* B) n+ V5 a2 j& g+ K
  10.   // 配置优先级分组0 R* F" d5 Q  x) D: |4 u$ W
  11.   NVIC_SetPriorityGrouping(0);
    $ I+ V4 r) s* I  b7 g

  12. 1 q" V( p# \) O5 l. n$ b
  13.   // 创建任务和启动调度器# ]3 D4 F  Q# [( s5 N; @, B
  14.   // ...
    ) [! M/ D, o  ~4 E3 Z

  15. 4 g: V) R. p, t8 ^
  16.   // 代码永远不会执行到这里4 R) x" G; F7 d, [5 V' b# x
  17.   while (1)( z% X1 L" Y5 v# f9 ~. a
  18.   {' {1 F5 j: G7 j: J
  19.   }
    # @5 k. W, F4 o/ |5 O
  20. }
复制代码
& s/ g, |5 E, ?5 Q6 J' ~
任务处理代码:在任务的处理函数中,编写任务的实际处理代码。由于FreeRTOS采用抢占式调度,每个任务的处理函数应该是一个无限循环,确保任务不会结束。
  1. void Task1(void* pvParameters)7 J9 l. ^0 c5 f- \9 o+ K/ ?
  2. {
    $ q0 @: ^; l; I* \/ h$ G" Z( X
  3.   while (1)7 v9 O% E$ ?: ?& f, C& ~' S) f
  4.   {7 C+ {7 d) @' @: N; `1 I
  5.     // Task1处理代码0 C) [. Z$ ~1 m% f! Q
  6. 2 |: [/ v6 L" [( `, q' z( ], V
  7.     // 任务挂起一段时间,以便给其他任务执行机会/ c- J' ?: x' H( \: T7 F0 z* A
  8.     vTaskDelay(pdMS_TO_TICKS(100));
    , y. z+ R# j: i6 t; n
  9.   }
    ( f7 v) X) a* g' Q. W, w" q& K5 w/ k
  10. }
    2 U( I) m' r& ?6 b5 _; U0 o& M/ ?7 D; T

  11. . s( b  u# E, }8 ^+ ~
  12. void Task2(void* pvParameters)1 z" Y: Z' j$ `2 t! r& G
  13. {; }4 t) |3 B/ {% f
  14.   while (1)* H0 w6 O' T! q& i1 c
  15.   {
    2 D/ K4 O* c9 h% V: F
  16.     // Task2处理代码" i, r' M5 j& S* N3 c

  17. 8 G+ w. o6 v: P
  18.     // 任务挂起一段时间,以便给其他任务执行机会: E0 b4 \1 u% M- h9 S. U3 F
  19.     vTaskDelay(pdMS_TO_TICKS(50));% V/ o1 }- o' F1 a0 Z( G
  20.   }# K$ ~4 f) {+ r$ g
  21. }
复制代码

) f/ f' N  H/ |- [( R- w/ ]
这是一个简单的示例代码,实现了两个任务(Task1和Task2),每个任务都在一个无限循环中执行自己的处理代码,并使用vTaskDelay()函数挂起一段时间,以便给其他任务执行机会。使用RTOS可以提供较高的可靠性和灵活性,适用于复杂的多任务应用场景。
& i+ i, t0 C' n$ p
4 ]3 v1 x+ ?6 i2 l, F5 a% z4 E6 u+ U/ j9 |. h
$ U2 T5 A$ {# t
转载自:安迪西
* y9 y! H2 x9 R6 ]4 }: d如有侵权请联系删除9 [8 @7 }" y* j
0 E+ A- \: G3 g2 j+ k
赞 收藏 评论0 发布时间:2023-6-28 13:47

举报

0个回答

所属标签

相似技术帖

官网相关资源

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