|
在嵌入式系统中,需要同时处理多个任务的需求非常普遍。本文将介绍如何在STM32芯片上实现多任务处理,通过合理的任务调度和管理,充分发挥芯片的性能,提高系统的灵活性和效率。下面介绍两种多任务处理的实现方法 1. 时间片轮转调度机制 时间片轮转调度机制是利用定时器中断来实现的。设置一个定时器,当定时器中断发生时,切换到下一个任务的执行。下面是一个简单的时间片轮转调度机制的示例代码 定义不同的任务:定义任务的优先级、堆栈大小、维护一个任务列表,通过编写调度器代码,在合适的时机选择下一个任务来执行。
4 _* ^' `( k6 G' Z7 E- #include "stm32fxxx.h"
4 j, W* t0 M( k( G; e5 U- o$ ^ - // 定义任务的优先级
, o7 R0 ~3 ^1 x* ]: |5 Y& c - #define TASK1_PRIORITY 1
1 E9 X) W y, F6 ?* s( b' M7 J - #define TASK2_PRIORITY 2
# z9 m' K- I2 M9 ? - // 定义任务的堆栈大小3 f+ e8 U8 M; x
- #define TASK_STACK_SIZE 128
. c W# E) u+ _6 g1 F6 {: z$ Q6 v - // 定义任务堆栈空间
( Z( p5 P& e$ @. q - uint32_t task1_stack[TASK_STACK_SIZE];3 h- C6 y! M8 X
- uint32_t task2_stack[TASK_STACK_SIZE];" i, n' D2 L% F2 L" Y* P9 j5 f/ q
- // 定义任务函数/ V& Y$ p2 f3 h( e
- void task1(void);
5 m0 z. v0 |/ D6 W$ J3 E - void task2(void);
1 ]% [! z, M/ ^/ F+ B5 `" y# Q - // 定义任务控制块结构
2 d7 l9 Z" d2 N5 I - typedef struct {, h' l) B2 ]) E. E/ m: Z( ~
- uint32_t* stack_ptr;3 g( J& n# [0 k6 A% `# Q# P m6 [
- } TaskControlBlock;# E6 ?) e* t0 f) U+ }! x! e
- // 定义任务控制块实例
2 `, M& |* Y0 b, C5 Q# e! E/ r - TaskControlBlock tcb1;+ @0 ?2 a; p& p# N0 |- ], v
- TaskControlBlock tcb2;3 V4 J% H0 I! s! g7 U
- // 定义当前任务指针- C6 ~ x2 y" g$ L$ t
- TaskControlBlock* current_task;
7 Q2 k/ S- ]! J( ?" `0 n" O0 N - // 任务1的函数
1 k8 e$ J2 {, a% t' i7 ?. Z' `! L - void task1(void) {
& A* B# ^* ~9 t) y9 K: F - while (1) {+ m, i3 ^$ \' m0 n( k' ]- L* t
- // 任务1的处理逻辑
6 G Q1 \; Y7 E- T& U, q8 h - // 切换任务
. T0 _$ l5 K6 M3 `, J% { - __asm volatile("yield");; B) }: R( ~: P- w& Y7 h
- }; x* V$ k+ k! M) a( L G
- }
% h. l2 ~& w) C2 z5 l - // 任务2的函数, X4 M. \8 j+ Q. i+ L
- void task2(void) {
/ `& j4 q0 \$ ^3 O& X1 Q3 z - while (1) {% x" } E& _/ Z/ C9 j$ U8 k
- // 任务2的处理逻辑
4 v0 ~1 t: ?6 m/ x - % I' ^# f( N+ G6 Q9 s: F
- // 切换任务
* F1 Z. [, c% i- A$ R3 _4 I - __asm volatile("yield");
+ p( X4 X4 h% I' [7 \4 {* b+ I - }
6 B( e/ I- Z, T; M+ q - }
复制代码 1 f* x% }3 S( M# Z! p' [4 y
+ A8 [3 m2 O& O% B/ U) T 定时器中断:在中断处理函数中切换任务,并保存当前任务的上下文(包括寄存器、堆栈等),然后加载下一个任务的上下文,使其开始执行。 - // 定义定时器中断处理函数
; A r' E+ v7 J& ]1 I - void TIM_IRQHandler(void) {
! c( {; I( O, ~ - // 切换到下一个任务- M# }# Z T* B5 C$ U
- if (current_task == &tcb1) {+ F' m& J5 {; |) a
- current_task = &tcb2;9 O* ~ F C+ E! D! H' M# h1 L
- } else {+ Y ]; c2 K; m4 G t
- current_task = &tcb1;
( q* i4 X4 x2 U e% W' G - }! H) K3 Y# F( |, w' ?9 r* q G
- // 加载下一个任务的堆栈指针3 C2 W% [' Y2 @! h5 a# U
- __asm volatile("mov sp, %0" ::"r"(current_task->stack_ptr));
! D; F' d. s" Z! m5 H r - }
复制代码
- c$ w( Y/ I( ^& h+ `- B' Q" p9 x. c/ g3 \- W& }
多个任务之间可能需要进行通信和共享资源。可以使用全局变量或其他同步机制来实现任务间的数据传递和资源共享。4 g& Y8 J2 _/ A9 R
- int main() {0 ` }. z" o& t0 p7 o6 y
- // 初始化任务控制块
8 f* e3 @) _' g+ K - tcb1.stack_ptr = task1_stack + TASK_STACK_SIZE - 1;
8 W! R) v" U4 Z/ E3 T; K - tcb2.stack_ptr = task2_stack + TASK_STACK_SIZE - 1;5 n8 Z9 ] u6 L0 B* b5 e% m
; q8 ]' V. g! P0 `4 a+ o8 L. S5 }- // 初始化定时器,设置定时器中断
' |1 I2 p" u! Y( b) f+ b1 ` - // 这里使用TIM3作为定时器,具体配置请根据实际情况进行修改; \1 p( S9 |5 Q) ^; k
- RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
! m+ l7 ?4 o3 X% I# O8 l* L - TIM_TimeBaseInitTypeDef TIM_InitStruct;
$ o8 g; M. P! W' Z, ^ - TIM_InitStruct.TIM_Prescaler = 1000;
. M8 r q8 c' s7 V3 m - TIM_InitStruct.TIM_Period = 1000;8 S& G; _8 \* K
- TIM_InitStruct.TIM_CounterMode = TIM_CounterMode_Up;' _3 \) E( C2 H0 X( W' V4 Y+ p
- TIM_InitStruct.TIM_ClockDivision = TIM_CKD_DIV1;- _# p% U: e/ N& e0 j1 c) P1 @9 m. |
- TIM_TimeBaseInit(TIM3, &TIM_InitStruct);3 M7 T* R# n! ^$ b$ U: G
- TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE);
" `. N# v2 ]+ K" X) d1 Y) @) ^ - NVIC_EnableIRQ(TIM3_IRQn);) R/ n+ I! i0 V" a- \3 ?
- TIM_Cmd(TIM3, ENABLE);* h& u6 ]- j q" P
5 O7 t1 m4 G& ~) o) x$ B- // 初始化当前任务指针
2 l7 a- E. e. ?" C( K - current_task = &tcb1;
, e/ {* @% f; b# f7 @$ \ - ) t* _1 N3 E7 P& Y& I+ [% o) u
- // 启动任务1
+ d/ I9 R r) l; t) ~& e* L, z - task1();
3 w3 I; n, M4 s; {
0 J" Q( v; |% W; E3 h3 N" X( H- while (1) {
# ?7 A( _: P: a+ }8 k5 w3 M - // 主循环,任务在定时器中断中切换+ b% s- M( B' o/ L: v) f* C, H
- }
; q. m+ f( j3 ]. { - }
复制代码 ; ~% 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,在应用程序中创建多个任务。每个任务都有自己的代码和优先级 - void Task1(void* pvParameters)% u" I& \+ y: c% J @2 M
- {( O& L& d% h( n# k8 v
- while (1)3 K& [' P3 {5 R+ t- V$ m' R3 w
- {
, k5 d; p) z# ]# b - // Task1处理代码9 O2 H( \7 C! i i
- } Y5 R, P3 J+ y9 { m8 z* y
- }5 j5 d% e1 {8 L$ l! D1 D) T! \
- % X1 ^! h% X/ p5 k2 f% h
- void Task2(void* pvParameters)) c% g% O E8 |- W0 j. i
- {+ ~% P* ~4 D( {! ^
- while (1)' U% }! _" C, K
- {) K2 f) N: j0 k$ T
- // Task2处理代码2 Y: | W7 N0 _
- }& \% p3 X7 I( Z" W8 M! X) D' ?
- }
) V7 `2 l* s6 z; `
' y9 O( e, F$ ]- int main()
3 x) P, N1 m' @3 R) z - {, \& u; D; c3 l
- // 硬件初始化和其他配置6 X# A0 t- |% v3 D
- // 创建任务" v/ V8 Z2 n! p5 r$ r. o
- xTaskCreate(Task1, "Task1", configMINIMAL_STACK_SIZE, NULL, 1, NULL);1 {" p9 p7 A% J
- xTaskCreate(Task2, "Task2", configMINIMAL_STACK_SIZE, NULL, 2, NULL);
! q% [4 Y# A/ x6 y* G
; r5 K7 [4 x9 w. w! K6 P- // 启动调度器# W h0 H& Z* P4 U5 u2 k
- vTaskStartScheduler();$ B0 S3 t3 t0 w' j: i% m6 z
0 a* z/ E% d+ Z: H. I: a- // 代码永远不会执行到这里
0 s. V" J4 `* t* V - while (1)
& o6 [) N; ~2 ~6 y" v3 s - {
$ j4 A% p, q) p, L b- @1 Q - }6 o$ q% h8 s& d% D7 @ x
- }
复制代码
6 D2 s Q3 N, W+ v5 Q
内核参数:配置RTOS内核的一些参数,例如时钟节拍和优先级。 - int main()
1 ^) Z+ Q; P9 [) e8 o - {
$ t( A. C) I$ r: j+ @ - // 硬件初始化和其他配置: j* e1 p* d: a
" H H2 c! q' q2 t- // 配置FreeRTOS内核
2 u; v4 [3 A& e: J, o+ b - // 设置时钟节拍7 C: M; C. u- u0 T/ B2 z2 {
- TickType_t tickRate = 1000 / configTICK_RATE_HZ;! w! K8 A3 h2 b t( u( J
- TickTypeSet(tickRate);5 L$ t/ T! H2 r- ^: Z4 Q# i
- 8 m0 h* B) n+ V5 a2 j& g+ K
- // 配置优先级分组0 R* F" d5 Q x) D: |4 u$ W
- NVIC_SetPriorityGrouping(0);
$ I+ V4 r) s* I b7 g
1 q" V( p# \) O5 l. n$ b- // 创建任务和启动调度器# ]3 D4 F Q# [( s5 N; @, B
- // ...
) [! M/ D, o ~4 E3 Z
4 g: V) R. p, t8 ^- // 代码永远不会执行到这里4 R) x" G; F7 d, [5 V' b# x
- while (1)( z% X1 L" Y5 v# f9 ~. a
- {' {1 F5 j: G7 j: J
- }
# @5 k. W, F4 o/ |5 O - }
复制代码 & s/ g, |5 E, ?5 Q6 J' ~
任务处理代码:在任务的处理函数中,编写任务的实际处理代码。由于FreeRTOS采用抢占式调度,每个任务的处理函数应该是一个无限循环,确保任务不会结束。 - void Task1(void* pvParameters)7 J9 l. ^0 c5 f- \9 o+ K/ ?
- {
$ q0 @: ^; l; I* \/ h$ G" Z( X - while (1)7 v9 O% E$ ?: ?& f, C& ~' S) f
- {7 C+ {7 d) @' @: N; `1 I
- // Task1处理代码0 C) [. Z$ ~1 m% f! Q
- 2 |: [/ v6 L" [( `, q' z( ], V
- // 任务挂起一段时间,以便给其他任务执行机会/ c- J' ?: x' H( \: T7 F0 z* A
- vTaskDelay(pdMS_TO_TICKS(100));
, y. z+ R# j: i6 t; n - }
( f7 v) X) a* g' Q. W, w" q& K5 w/ k - }
2 U( I) m' r& ?6 b5 _; U0 o& M/ ?7 D; T
. s( b u# E, }8 ^+ ~- void Task2(void* pvParameters)1 z" Y: Z' j$ `2 t! r& G
- {; }4 t) |3 B/ {% f
- while (1)* H0 w6 O' T! q& i1 c
- {
2 D/ K4 O* c9 h% V: F - // Task2处理代码" i, r' M5 j& S* N3 c
8 G+ w. o6 v: P- // 任务挂起一段时间,以便给其他任务执行机会: E0 b4 \1 u% M- h9 S. U3 F
- vTaskDelay(pdMS_TO_TICKS(50));% V/ o1 }- o' F1 a0 Z( G
- }# K$ ~4 f) {+ r$ g
- }
复制代码
) 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
|