(一)裸机工程创建. a& p! d' ?0 E) R6 v8 A& r- I
使用STM32CubeMX创建裸机工程:
6 P/ b' F. D$ E2 T4 ]; Z4 T) z$ h
这里我是用的芯片是STM32F103RBT6,时钟主频为72MHz,配置时钟树:
+ n! {* X$ q9 |& l6 V6 a8 L/ `; s( Z5 A- u% x* P+ T% H
8 T* f6 P S8 i: @- ^. n" B5 ~8 ?
5 h# y4 Z; I5 [2 W* h7 e0 s( K
配置调试串口1:, n6 @! @3 G6 g
1 ?- h, `; F$ x+ w) p" ]8 n: w
& n3 w* o* M# f" y8 N! M6 `7 q( b% I3 [1 K
配置两个LED灯:
' Q* i- X1 ~7 P" a* F1 h5 J9 J- ^
& K: R5 Z1 G" m W2 X
8 ]3 H! {4 ]/ ]" v# ~+ V
由于FreeRTOS使用系统嘀嗒定时器当作心跳,HAL库也使用了这个定时器当作超时计数用,FreeRTOS和HAL库不能同时使用SysTick定时器,所在在这里将HAL库的定时器改为其他的定时器,我改成了定时器4:; p) S! v6 D2 l( a
- d8 j( Y C; G
3 M- b6 ]/ ]. D! q" c; Y
4 B1 x# _; l u
配置中断,注意这里一定要选择组4,也就是全部为抢占优先级:; F; d* n& n. _$ v& Q8 G% t
& O# d4 G; ^7 ~, f: l
( T) q" ^! F+ Y, q: t8 a
4 ]2 |1 w1 T$ O; u2 {输出工程:7 g+ s t; x; X# d
3 w H2 {$ p T5 p
+ e% D( I4 d% q# Z% ~0 V% A% F
- Q- @1 w* a2 j# o% `' @, I* Z3 N2 R, Z( O) C( v% k8 h3 d+ Y
, z ]7 U8 K7 x+ }9 m& q(二)移植FreeRTOS, ~: ^1 _+ X8 u3 t: u9 b
5 O( _5 r* ^- k( w1 t4 i
解压FreeRTOS源码包有如下文件结构:0 O p1 B- X, A; e$ ?8 E- E- o2 Z) }( t
" |( K/ N: |" t2 X0 }
# V' I! _" H/ J1 w! Y
+ j& M( \. m' {( C. w9 g5 ?
在Keil工程目录下新建FreeRTOS文件夹用于存放freertos的源码。
* m/ g7 @+ R3 B; r6 U: Y$ h0 x- u* L# c4 A0 |7 }2 I
拷贝FreeRTOS/Source/文件夹内的以下文件,放入keil工程目录FreeRTOS文件夹内,效果如下图:/ N* b9 h# Z! r
5 z) I' G9 U& g
/ q. E4 Q i* l! V4 `
3 |2 y+ F, g; V% D& d, t; I在Keil中将FreeRTOS源码加入到工程内:# S+ d1 M% a9 ?: }
3 W: Z0 e- X2 B4 Z% P
7 z% g4 m: d5 M+ F4 s2 g$ n* i' l- ^3 M5 `. w) K; o5 ~6 G
打开stm32f1xx_it.c文件,在头部添加FreeRTOS的三个函数导入:- extern void xPortPendSVHandler(void);
9 \7 j" _$ Q" N8 c - extern void xPortSysTickHandler(void);6 w. c5 r Q' n3 i* X6 ?
- extern void vPortSVCHandler(void);
复制代码
9 h! k. t( Q' k6 M$ L
* Y- F9 A6 U. \4 p, j1 V9 k" I
. H+ }% f* j( p9 p7 E* D在函数SVC_Handler中添加函数调用:) s& R. ~& t0 R1 e, ^0 Z
! N# A# P# q& e) R5 P& J( p8 c
5 S& T. W7 @$ d8 q! I
! F( q* s& Y f/ r+ o2 v+ ~" f/ v
在函数PendSV_Handler中添加函数调用:
3 m5 f8 Q1 [3 ^; C$ D, e5 }( L2 x. `8 B6 V
{2 X2 F6 T# P5 x# l& M3 t# M8 @1 M5 G
在函数SysTick_Handler中添加函数调用:
: g) U5 }) \) o% B; m- k4 X
/ s" L* ]8 D* V
) z. _/ n+ {0 p! ^/ I3 X( V5 X0 T! M% H7 |! x% [, c& N p
新建一个名为FreeRTOSConfig.h的文件,这是FreeRTOS的配置文件,这里我贴一个我配置的:
* q1 s$ Q* ]" A& w! v$ l# u! X
+ B1 J$ y* j6 G; J( V) _- #ifndef FREERTOS_CONFIG_H__
) m: o7 J" l& F - #define FREERTOS_CONFIG_H__, \6 w; A- D! p! O
% [# G% m* x" K5 O- /*/ {- H6 N* Z3 T/ O* j
- & f7 `6 ]; t0 Q
- */
m" L* o! @+ H - ( F1 T) u3 y( J) o. R$ A; |
- // 设置为1使用抢占式,为0使用时间片轮转调度。) X& ] P1 q! ]- R
- #define configUSE_PREEMPTION 1" l7 Y2 ~1 f8 D/ `
- / y. m- V8 l$ A: j1 [
- // 设置为1使能低功耗tickless模式,为0保持系统节拍(tick)中断一直运行。
3 l* \' E+ ~/ z$ c7 q, ` - #define configUSE_TICKLESS_IDLE 07 u9 H& I9 B* J/ r- y5 q) o
- ! I& ?2 \3 D/ _& S6 \
- // 系统时钟主频7 i; x% s) k, f0 _8 U/ F: f
- #define configCPU_CLOCK_HZ 72000000
: A0 R. _6 G# r% L - + |9 ~1 r, S5 R# P7 J! X3 Q
- // 系统节拍中断的频率,即1s进中断的次数,配置为1000就是一秒进1000次中断,系统节拍就是1s。8 I; V k. z, ]5 K7 ]3 V# Q
- #define configTICK_RATE_HZ 1000% n$ O+ R( E, B& n, \, G
- & m2 t s# J( X. \8 R/ y
- // 任务最大优先级,对于STM32来说最大不要超过32
3 `" I, J9 D2 B: c; {* R - #define configMAX_PRIORITIES 32" S0 t$ N$ c' Q
- : @+ u0 C+ u3 Z8 H& u/ \
- // 任务最小栈大小6 r, _- r* q1 `% M" m% Q
- #define configMINIMAL_STACK_SIZE 64
9 d) R4 Y5 H5 q$ W' q$ Y6 \4 M
5 V, ~" s* i& q1 V- // FreeRTOR堆空间大小
2 S* D4 g2 z* i, R - #define configTOTAL_HEAP_SIZE 8192: [2 x3 A( B2 V" A0 v
* _; Q5 ?/ w w8 J Y" h; }& U- // 任务名称最大长度6 w+ K/ ]. I+ B1 H% u
- #define configMAX_TASK_NAME_LEN 164 |, O: h. ]; ]$ H5 O4 p
' F+ J. e. E6 f% F- // 系统节拍计数器的变量类型,即定义portTickType是表示16位变量还是32位变量。: A+ U) k) V( B$ \9 o2 s, g8 ^
- #define configUSE_16_BIT_TICKS 0
& \8 S7 d: e0 p) U7 `5 E - 3 ], M+ Y0 Z7 {
- // 设置为1允许任务调度,为0不允许(时间片耗尽才让出CPU使用权),该参数抢占式方式下才生效
* ^7 h2 R6 {; L! }) [ - #define configIDLE_SHOULD_YIELD 12 ]: ]+ u2 F5 v. h3 G5 R
- 0 D, \* Q8 A. Z! Y9 s4 b
- // 设置是否使用互斥量
Y: U" Y& U4 b% ? - #define configUSE_MUTEXES 1
; A( r! N# k: f$ Q0 i( @/ V - ) b0 u; z* H# O
- // 设置是否使用递归互斥量
) n i2 A3 A/ y- \0 Z# G - #define configUSE_RECURSIVE_MUTEXES 0+ z, e: p. F5 O6 s3 B c& c* {
- % X( O) F1 @. y5 j s% h
- // 设置是否使用计数信号量
* { P: \! Y+ q" K4 S - #define configUSE_COUNTING_SEMAPHORES 06 l+ Y5 }% m0 @* x- j
- 9 {7 x2 ?3 M: c
- // 设置可以记录的队列和信号量的最大数目
1 Q( w/ v- r: z( T- ]' F9 s - #define configQUEUE_REGISTRY_SIZE 10
* z( A5 h8 `1 k# e* F9 x+ E8 J4 E
: F7 A- H' d7 Q) ^' f( y* K( s/ I- // 是否使用空闲钩子函数
9 E5 N, L4 f6 @, K - #define configUSE_IDLE_HOOK 0! x0 h' w( N! B7 F6 X
# Q9 ]! g# m6 M. F: g4 P- // 是否使用TICK嘀嗒钩子函数! ?) D4 j" n- O9 G
- #define configUSE_TICK_HOOK 0
" ?0 a3 I o" K9 V
6 t2 u0 i& j+ W* N" r/ J5 n- // 是否使用栈溢出检查
) a5 q$ U* C- C: V. M9 ]/ I5 v - #define configCHECK_FOR_STACK_OVERFLOW 0
$ k" v" x# \8 M - " w9 p" u/ I' O' M2 X1 M4 P6 W
- // 是否使用内存申请失败钩子函数# j& b6 H; P7 M
- #define configUSE_MALLOC_FAILED_HOOK 00 Q( P. w3 W: {. s4 w, d
- x/ a6 \! k2 X: j
- // 是否使用软件定时器# T6 U3 O& r* d& l. W" ^" h9 t1 n
- #define configUSE_TIMERS 1
5 k! R$ W# o0 ~$ U. e - . |2 d9 m6 t( b4 `
- // 设置软件定时器服务/守护进程的优先级3 M3 V4 l$ m' f3 V; M4 i
- #define configTIMER_TASK_PRIORITY 3
5 d) L9 U" U6 U. n# W - & I) k9 J+ @: T: m
- // 设置软件定时器命令队列的长度
8 J* s$ i% _7 ^, d" W - #define configTIMER_QUEUE_LENGTH 105 y6 i5 {/ Q" k( L- s" f0 U" L
r8 ]0 n/ ~) d/ V- // 设置软件定时器服务/守护进程任务的堆栈深度9 q. q" x! R" m6 V
- #define configTIMER_TASK_STACK_DEPTH configMINIMAL_STACK_SIZE5 z" N9 `3 A3 x$ M6 K/ c+ M/ `& e
- + m$ v6 W! Q0 h# P5 _) v4 J
- // STM32的最低优先级
7 F6 u: n( g- b( y - #define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 15) v; G( H9 f% j2 L
- 6 V% ?; I) Z2 ], Q4 T
- // 能够在中断服务函数中安全调用FreeRTOS API的中断最低优先级
& o ?4 p' z+ D2 E$ P: g - #define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 17 H7 U# N5 ~4 R; @) N# ~
9 o/ B9 @( b" m+ _3 u9 O- #define configKERNEL_INTERRUPT_PRIORITY ( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << 4 )% O$ A* l; r& ?% @% Y0 D
- #define configMAX_SYSCALL_INTERRUPT_PRIORITY ( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << 4 )
# d$ A& ?- E# P8 d1 @1 U - 0 w( M' x: |" ^, H: E; e* `
- # _; c* S9 H5 n6 F
- // 将以下定义设置为1以包含API函数,或设置为0排除API函数
8 l6 d4 ^/ d! f v( P4 F$ ] - #define INCLUDE_vTaskPrioritySet 1
: @; r8 M! I# r# n1 W1 H! e# ]) N - #define INCLUDE_uxTaskPriorityGet 1
3 ^% h0 w1 N) I4 r - #define INCLUDE_vTaskDelete 1, D, {0 z5 p" g7 l( c
- #define INCLUDE_vTaskSuspend 1
F4 Q/ X& v9 O3 R# Q- p7 h9 G; \ - #define INCLUDE_xResumeFromISR 13 g" B: A- Z8 e( e0 Z- z
- #define INCLUDE_vTaskDelayUntil 1
2 b( {5 ~' k& Q6 @ - #define INCLUDE_vTaskDelay 1
$ Z) M0 N* S! ~1 O3 W6 b8 Z - #define INCLUDE_xTaskGetSchedulerState 19 H: P; Z) O- `6 Y1 B
- #define INCLUDE_xTaskGetCurrentTaskHandle 10 c, C: B9 q: r8 @6 |5 G/ \' s" P
- #define INCLUDE_uxTaskGetStackHighWaterMark 0
7 c4 d, m1 o b# G/ j0 b; {4 D - #define INCLUDE_xTaskGetIdleTaskHandle 09 r p; {! M; E% ]: X: @8 E& L$ V
- #define INCLUDE_xTimerGetTimerDaemonTaskHandle 0
; b& Y0 I7 Q% K' P - #define INCLUDE_pcTaskGetTaskName 07 W6 o" q+ c. G3 d3 V7 q5 a% }: L* W$ j
- #define INCLUDE_eTaskGetState 0
9 N* A* ?' I$ L. P8 h' l% J9 [ - #define INCLUDE_xEventGroupSetBitFromISR 11 T: R/ f9 V$ h: N3 [
- #define INCLUDE_xTimerPendFunctionCall 0
3 i/ k* D! V0 X1 l% |# x: z3 p
# s ?8 m0 Z. v* n- #endif /* FREERTOS_CONFIG_H__ */
& r/ f8 i7 w0 X3 I& d2 D! q9 z
复制代码 & z3 {$ P* f7 U) c# p
修改main.c文件,添加头文件:; {8 E" K& q+ h
7 P6 w/ G7 f, h. v/ s3 ?9 i* f- #include "FreeRTOSConfig.h"
" v$ ^! v9 G# D a/ }- U - #include "FreeRTOS.h"3 w1 ~( p8 L0 J; ]% I
- #include "task.h"
复制代码
4 L9 ?) O# Q1 c这里我创建了三个任务,任务一为led1灯每间隔500ms闪烁一次,任务二led2每间隔300ms闪烁一次,任务三为串口每隔1s打印一次helloworld。
1 b4 F' t4 L& o3 t5 A. m* @% h6 Y _1 o" v |% q
. E4 k2 r" l& G+ l. x# {- void led1_task(void* arg)3 h% W- T2 B. k4 F
- {
9 [5 y1 q2 w x+ M) k - while(1)
/ ^' h2 O4 N9 Z6 E7 p - {
0 R- z: ~& x8 l4 F - HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_8);+ r9 B H/ ]) X0 l
- vTaskDelay(500);
9 S8 l& e5 Y( @) L2 T; b - }5 D Z- M! K) ^
- }
. ^" V" Y) U4 w5 o, f - / }) [3 Q+ i, g! m5 |; R( c
- void led2_task(void* arg)+ w0 t2 R7 @, {# i
- {
# J1 ~3 V% x7 m" N* X - while(1)7 J) q5 P. S/ L7 O
- {7 R4 h- V& h" L3 ^
- HAL_GPIO_TogglePin(GPIOD, GPIO_PIN_2);
! {/ _$ \% O! \2 T - vTaskDelay(300);$ f' z! E6 Z, x9 V7 H
- }% P# s0 M. @0 k
- }; G: j( K+ U+ O E- ]) C
- 3 f2 H' S8 ~9 ]) k
- void print_task(void* arg)
- @2 L' k! \0 X6 G7 |. F - {: g- f; V( C) G" ]
- while(1)" k, {& u' j; G/ g7 j$ A
- {) {2 J! u7 j" R. I- ~" ?/ r
- HAL_UART_Transmit(&huart1, (uint8_t *)"HelloWorld\r\n", 12, 100);# S* n, d& `* H
- vTaskDelay(1000);
' @. v' V6 B2 p- q" a& _ - }; s: _2 f; }4 a) J
- }
) X+ z$ ]* c0 C, n2 a8 A* c - + {9 A7 G% U" r) n# w) r# X1 `
- int main(void)
" N2 ]4 E F: z$ L7 Z) W0 o8 H( Y - {5 t$ o6 l. [/ @4 w4 n' q* y0 @
- HAL_Init();4 A2 _! X, z% @) `2 Y7 A( A- _/ q
- SystemClock_Config();3 a8 V, }* @/ B$ W! I5 _7 H
- MX_GPIO_Init();
P" H" I; t: f - MX_USART1_UART_Init();
) X& d1 `& L3 ?6 E+ i - / u7 l y3 P3 J. q. J) b/ Z
- // xTaskCreate函数参数说明:
- C9 w2 @; L8 Y% r1 U" H - // 1、任务入口函数) H2 K9 B F9 J- W4 n
- // 2、任务名称2 B& v4 h7 q8 b
- // 3、堆栈大小,单位word
3 x, i* h) {4 }& O; L - // 4、传递给任务的参数
4 \% v5 j/ P2 `, y - // 5、任务优先级' e! F1 p) M* o; M5 c5 {
- // 6、任务句柄( f6 I! S* w$ U( X" p
-
8 a. y; k, [2 m6 Q6 X U M! q Q - // 创建任务led1灯任务
% E4 {) ~; s9 t4 i - xTaskCreate(led1_task, "led1_task", 64, NULL, 3, NULL);
! e4 _# w: Z/ o - ' [( b y- o$ a5 i6 s y/ c
- // 创建任务led2灯任务% G9 }3 N. W$ |( N
- xTaskCreate(led2_task, "led2_task", 64, NULL, 4, NULL);
T0 P* C1 m' j - f9 d% e' ~- ?' i
- // 创建串口打印任务
# T9 ]2 E' D) i' B - xTaskCreate(print_task, "print_task", 128, NULL, 5, NULL);4 Q+ m5 V, l, V+ V* Y7 c3 P3 w
- 2 c" D/ ^8 k0 v: d# i+ R' T
- // 启动任务调度4 u0 `2 A0 V# c# R3 _
- vTaskStartScheduler();( f; r& c1 z: r5 `: d4 o9 T" l2 Z
- * N' n8 l* y6 `* N2 {0 Y
- while (1)
8 l% v0 a& Z9 P8 E0 F8 z/ y - {2 U y& Q) i9 P
- }; |; D% s- ?& K4 T( T. Z
- }
复制代码 3 P$ E" G" T- K* [
到此,移植完毕!/ c$ s+ _9 I# I
& ?$ [9 [6 K0 b3 @9 m) Q总结:移植FreeRTOS还是相对简单的,主要是配置文件FreeRTOSConfig.h的配置,每个宏定义所代表的意思需要好好查阅!' s2 K I% \" R3 @' D4 z+ R
/ ?# Y- l+ O. a8 a/ {, i3 b7 P# D) Y1 O! w/ m
& P$ C. I/ x' N- h2 S
$ f- P: T' {. @" W. D, ~ |