(一)裸机工程创建 @' e; O5 z( W& j2 z6 F$ E, K& o) f/ p
使用STM32CubeMX创建裸机工程:
: \5 J. m% U' c% u R- t( O% s' r4 R/ G
这里我是用的芯片是STM32F103RBT6,时钟主频为72MHz,配置时钟树:% m% T! J! h* \. i1 X
( e. R; x9 U) v! S
. z9 X0 S, ~# p
- y& U# \9 M% q0 g: h配置调试串口1:" P% d, r6 X, l3 X" i
( A* j8 G+ T: P" g+ v
- F. F7 e1 r/ ?9 x! C
$ L; T- Y) s) w6 K6 Q! g配置两个LED灯:, Y l. X6 x0 Q/ Y- H
3 \/ i% h' k: E2 t, N$ J% H
X4 F9 g8 @ u- t: i0 ]* z# J" x6 B7 ?) _" q+ E! P
由于FreeRTOS使用系统嘀嗒定时器当作心跳,HAL库也使用了这个定时器当作超时计数用,FreeRTOS和HAL库不能同时使用SysTick定时器,所在在这里将HAL库的定时器改为其他的定时器,我改成了定时器4:( y+ w9 d) F1 o! L6 Y
) Z0 H5 l( w" w* E' s
5 b" I, [+ S8 l: \/ ^1 _5 G2 k2 }
$ t$ `! Z! y/ B配置中断,注意这里一定要选择组4,也就是全部为抢占优先级:
8 H) c1 s! Z' ]% d# K0 ? m8 x
2 g" V# V2 D n2 j( u& V4 Q
+ ^( J; b' z! U7 o1 x
% Z( o, R6 W% i0 W9 L/ y
输出工程:
! o+ Y: a8 m( y ^* I' ^. p) K. {* D- b% Y
/ @6 e( l" J% y
0 _; A/ Q1 v0 V+ [! {& p5 E6 f1 q0 Z; K. O7 V* w/ z
$ T$ Y' b+ V+ u" ^" n: d(二)移植FreeRTOS6 c$ Z8 C, Y$ ^# ?! f2 h
! `: X' Y* x3 V% X
解压FreeRTOS源码包有如下文件结构:) a1 M7 u# i4 r/ T. O
0 u2 W' z1 U7 T) y% i3 z) l/ Y+ X
, h6 \9 y) r* ]; \8 {
8 }, o r. Z8 x0 c) x+ k. ?- h在Keil工程目录下新建FreeRTOS文件夹用于存放freertos的源码。/ J7 [1 I7 X7 Z" m( Z$ x
4 J$ p6 D" W2 }. C6 }
拷贝FreeRTOS/Source/文件夹内的以下文件,放入keil工程目录FreeRTOS文件夹内,效果如下图:
8 x' G7 L8 k: T# W$ L: H* t1 @# R2 |- I. e! @4 Y% H3 Z- H
6 e% R5 g6 e/ R! ?. ]/ h& j, n
0 D, s$ X# A" X在Keil中将FreeRTOS源码加入到工程内:
0 b u8 j6 T; ] q& I
6 L7 T( i) p' q/ e" U! h
; J @8 V5 Y# U9 ~! y8 c
; k* O+ h1 u+ y6 V: M
打开stm32f1xx_it.c文件,在头部添加FreeRTOS的三个函数导入:- extern void xPortPendSVHandler(void);
; W( J/ a A' q$ s. o* p6 } - extern void xPortSysTickHandler(void);+ y) T9 i' X. T' E( B: `
- extern void vPortSVCHandler(void);
复制代码
$ {: D3 v8 F, q$ l- G0 ?* r
4 T/ i, J# c2 e2 o2 t5 E6 ?
1 N& k, B- v( [& E: o. @; L; }) d在函数SVC_Handler中添加函数调用:
0 T Y, e# }( y' F
) O2 J( Y; y1 H; O/ z5 Z
1 p! ?5 i% f$ b& q) L+ B
# p j& } w! f' m- c
在函数PendSV_Handler中添加函数调用:
$ U* v2 \) T& [' H- |- N+ _8 f
# U4 u7 Y; y! ^# D+ y
( _# Z" o& ?: u
, j) t# R6 ]9 v+ W/ x
在函数SysTick_Handler中添加函数调用:
4 ~4 D( m, _: R( n7 T# Y
: C: f" l% J x6 V, h( [
* Y" G$ [# S/ s2 @& T% m+ ]
9 y1 m4 \ \0 r/ V3 D7 I/ V* p R
新建一个名为FreeRTOSConfig.h的文件,这是FreeRTOS的配置文件,这里我贴一个我配置的:
% k' X) p' ]$ A9 a- P$ u) f, w: l2 x& ? D% F, V
- #ifndef FREERTOS_CONFIG_H__
7 _( B$ v- ]/ M( L+ x - #define FREERTOS_CONFIG_H__3 `1 \6 H; Q" ?. W# s- n8 w
- # f2 W" V6 V- [, k
- /*
) E" X* q, H8 _* k
9 o' a; D3 j, n8 J1 Y' T: q- */1 d% m- D% w/ Q# l5 O
# u4 r# ^" @3 j# s- // 设置为1使用抢占式,为0使用时间片轮转调度。
7 R( D) Q3 W' _# d9 M - #define configUSE_PREEMPTION 1
8 V) T: ]5 P9 Y) P! I9 T
Q: O" W; U# j) V' P- L- // 设置为1使能低功耗tickless模式,为0保持系统节拍(tick)中断一直运行。; t, y) z" B1 K! L
- #define configUSE_TICKLESS_IDLE 0 ^* c1 D+ U& b6 v8 ^/ F) C
0 b) D* C) }/ A% T5 u) P- // 系统时钟主频
% q$ @. d1 g# z$ b% M9 H - #define configCPU_CLOCK_HZ 72000000* H* B: t m& _
- # V) f L7 B; x
- // 系统节拍中断的频率,即1s进中断的次数,配置为1000就是一秒进1000次中断,系统节拍就是1s。
1 K, H* ^% B2 S4 i- h% e' ~ - #define configTICK_RATE_HZ 1000
! J! o% @2 p% f2 W" d$ T - r, z. r' }8 e* u
- // 任务最大优先级,对于STM32来说最大不要超过32
5 J# u& M; {. u4 e1 V$ Y - #define configMAX_PRIORITIES 32
+ e% t/ |+ p. u2 W& Q+ N% O3 a - - R5 t7 m# R/ `: [/ r# [
- // 任务最小栈大小( h& c$ c r+ t# v
- #define configMINIMAL_STACK_SIZE 64/ N# V3 \; i) g
0 _! E% C: `! X! }4 V P! e- // FreeRTOR堆空间大小: i8 P: V/ j- |7 J/ Q9 d! G n5 X
- #define configTOTAL_HEAP_SIZE 8192: g* Z4 X( }0 x. h) J
- 1 h: y9 M; f" z- M7 @
- // 任务名称最大长度8 i2 o( L% I( \# Z' @: a/ h% J% M
- #define configMAX_TASK_NAME_LEN 16( G: o" i# _" x1 A, E" u; K: z$ J
- . k+ E7 a y. x
- // 系统节拍计数器的变量类型,即定义portTickType是表示16位变量还是32位变量。
+ l; |8 W2 A; v* Y# t) E - #define configUSE_16_BIT_TICKS 0
; j0 }+ w& `$ ~6 e$ u, ]% u3 Y8 Q - + F; U0 j1 d) Q# k% y% R& V9 Q
- // 设置为1允许任务调度,为0不允许(时间片耗尽才让出CPU使用权),该参数抢占式方式下才生效
/ N0 S7 H+ S+ y8 H# q) o - #define configIDLE_SHOULD_YIELD 14 c5 _! ?1 K& [3 z
3 [5 G- Z6 o- @6 I' h- // 设置是否使用互斥量2 ^! K. a, J" x( c. d6 g, Q9 l" v
- #define configUSE_MUTEXES 1
* Z( t8 E7 H3 ~. J1 ]$ O0 e. H
, k$ q. b5 S5 G/ E- J- // 设置是否使用递归互斥量$ K! Y) h* X" r
- #define configUSE_RECURSIVE_MUTEXES 0' V3 I* h; m, T: m6 W! c; p
- + y9 X& K( L) E- _, v
- // 设置是否使用计数信号量
6 }" t* n# W8 i5 _, j: G - #define configUSE_COUNTING_SEMAPHORES 0
2 F& @+ t* C1 {5 y: W+ s - * P! [8 c0 A1 v9 P9 z" N* G4 T
- // 设置可以记录的队列和信号量的最大数目
8 C4 M3 o5 u& b" M - #define configQUEUE_REGISTRY_SIZE 10
/ C/ s2 E) {/ |, w t# v
: k- Y0 ^ O# w- y9 L2 |! a- e- // 是否使用空闲钩子函数
: }" u/ W7 E: L3 }" y4 o - #define configUSE_IDLE_HOOK 0
1 A1 e% ]) e9 c6 o1 T
3 l+ W: u, }/ [/ P- // 是否使用TICK嘀嗒钩子函数 \8 {7 m8 _* H2 v
- #define configUSE_TICK_HOOK 0& {% Q6 o0 ^( w: S
8 X0 y4 d0 }2 q! H- H2 W" I9 j2 r- // 是否使用栈溢出检查 U' w; i; M+ U$ w
- #define configCHECK_FOR_STACK_OVERFLOW 0
% M5 b7 `8 d* d+ ?" ]/ s$ _; n V
* N, t& T9 [$ v' O- // 是否使用内存申请失败钩子函数
i7 `5 S' j' m8 O! M% R - #define configUSE_MALLOC_FAILED_HOOK 0
( z! a( G o3 X- [5 g3 k- @- y& d - Q* |; I5 S. V" N) {
- // 是否使用软件定时器
- s3 D, r! C2 r# ^: p5 u e - #define configUSE_TIMERS 1
0 p6 k! D+ _" P* d# z; n! v - 4 [. U" g! \4 h
- // 设置软件定时器服务/守护进程的优先级4 F) p& X' _9 {" \6 q
- #define configTIMER_TASK_PRIORITY 3
& p6 o [6 ]+ p( {/ {- y - 8 Q! X; _# `, B& q7 V; X
- // 设置软件定时器命令队列的长度
/ g% Q- G$ N$ k9 {' r# S0 e - #define configTIMER_QUEUE_LENGTH 10
' s. N% l/ V9 e. H. s- L
0 D o1 Y8 d- w+ H+ f3 [, z: ^4 r- // 设置软件定时器服务/守护进程任务的堆栈深度
- r" ^! p4 L. u( v( s x - #define configTIMER_TASK_STACK_DEPTH configMINIMAL_STACK_SIZE4 y. T% s2 G# V1 n) e
9 @' V1 G# H8 \0 C: B# @- // STM32的最低优先级/ i z3 j: q+ j6 y( Y/ ?; m! p. D
- #define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 15
- w9 {- k# U. r( [; u$ L- ~6 I8 P
7 ]. n: S9 S8 K! V* d" _$ h- // 能够在中断服务函数中安全调用FreeRTOS API的中断最低优先级, } \* Z8 _! k% ~ @$ ^$ n* W0 P9 J
- #define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 1& G+ i' ]. ~( K6 v% N# b
2 k9 d9 Q& N: E$ S6 p- #define configKERNEL_INTERRUPT_PRIORITY ( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << 4 )
- U% R. F- y8 y4 p# j& E9 Z+ P - #define configMAX_SYSCALL_INTERRUPT_PRIORITY ( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << 4 ) L" G6 T+ N% n) A" S0 R
- + z8 d8 f6 r$ B1 c& L
7 E) ^6 [5 X& P2 D' j: y- j5 s- // 将以下定义设置为1以包含API函数,或设置为0排除API函数
8 R* X' B+ D5 C ^) C M; x: Y - #define INCLUDE_vTaskPrioritySet 1" ^+ b) \1 Z+ Y% R# N) P4 D
- #define INCLUDE_uxTaskPriorityGet 1* [6 v0 l) M6 K% E7 E) P) f9 X d$ `
- #define INCLUDE_vTaskDelete 1
4 G0 t/ S- `, b - #define INCLUDE_vTaskSuspend 1
# u: F2 E5 S2 b3 i9 K - #define INCLUDE_xResumeFromISR 1
! V/ Y9 _4 \( Q7 p3 _ - #define INCLUDE_vTaskDelayUntil 1
1 c3 o$ I, p9 m( m. R - #define INCLUDE_vTaskDelay 1
" b7 g) T% O5 d5 g, _+ }: v - #define INCLUDE_xTaskGetSchedulerState 1# z- r0 l0 L4 h
- #define INCLUDE_xTaskGetCurrentTaskHandle 1) c H: M8 B/ B9 t4 S6 d) W
- #define INCLUDE_uxTaskGetStackHighWaterMark 0
6 [0 f: W5 r0 G- d+ Y! b5 D - #define INCLUDE_xTaskGetIdleTaskHandle 0( ]* @/ C; r+ H/ d! [, l: ^0 G8 J
- #define INCLUDE_xTimerGetTimerDaemonTaskHandle 0/ U2 x) l: r: ~/ D1 x
- #define INCLUDE_pcTaskGetTaskName 0. V) Z2 u- C b* I
- #define INCLUDE_eTaskGetState 0/ a9 Q3 \2 R+ D
- #define INCLUDE_xEventGroupSetBitFromISR 1
2 I" J$ v Z' [+ s, Q - #define INCLUDE_xTimerPendFunctionCall 0# N! A( ^% q' u& t2 K4 Z0 C1 a
- - w1 s" I, u' b1 b% S
- #endif /* FREERTOS_CONFIG_H__ */" r" t5 ^1 o* x" K1 G6 I: X
复制代码
, A! R; n7 l9 i' }, z7 ~3 w修改main.c文件,添加头文件:
: `7 u* F3 y- |! a0 m& Z9 O, }& p0 @4 I
- #include "FreeRTOSConfig.h"7 F3 w" U* \6 H3 r# G& F+ q2 F
- #include "FreeRTOS.h"& G: V2 q' Z5 N5 C
- #include "task.h"
复制代码
0 e* g% V, ^8 } Y+ S这里我创建了三个任务,任务一为led1灯每间隔500ms闪烁一次,任务二led2每间隔300ms闪烁一次,任务三为串口每隔1s打印一次helloworld。
- [5 S5 E. a0 u. U6 x; L d4 O5 P: w
2 f* k/ c3 F- p
" r9 x$ l j- x3 v3 p/ e7 C6 u- void led1_task(void* arg)
* T4 s8 D6 i8 e" f - {! s# g0 L( z9 T+ K: Q8 g3 a
- while(1)
x! g2 R9 h( ]" T0 t0 `; l$ n R5 u5 N$ j - {- A; T) [+ h+ C& O$ u, s2 T
- HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_8);
* G9 n# J& ^$ B2 L: p9 U }, t - vTaskDelay(500);$ Q; {1 L, Z8 W v
- }
; R' Q3 V4 C# ?, S - }, N7 J9 I& `# n f2 h1 A
- 8 U5 @6 o5 i7 Q
- void led2_task(void* arg)4 D6 X2 X7 v8 u
- {4 H) {, f/ x8 h7 c
- while(1)8 t K, ?& z, o$ s4 B1 k0 w5 C' }
- {
$ z& a6 o( ^& A: @ - HAL_GPIO_TogglePin(GPIOD, GPIO_PIN_2);6 g. g4 c5 W9 _/ u/ e: i: b
- vTaskDelay(300);+ Y& l; ]$ r7 c" J) K7 a- p
- }
# L' O( I3 y& W& I" x; i& X - }" c$ H" @1 n; d6 ?8 s
- Y [8 q& T1 z1 Y$ }0 |1 O# |2 M6 ~- void print_task(void* arg)' H6 N1 X' |0 _8 `4 Q+ S" v& \5 A
- {
+ y1 a7 }" V4 w- x5 ] - while(1)
. q g) @# y& c - {
& I* E* F$ h7 a, J t* l# _: g2 z - HAL_UART_Transmit(&huart1, (uint8_t *)"HelloWorld\r\n", 12, 100); @3 X$ p2 ~* E1 X9 q: p& _
- vTaskDelay(1000);
# d. N7 E" S/ g/ G _9 v - }4 \" O0 H* Z+ X$ C- X6 {& Q7 `
- }
& g- w8 ~6 K6 L( v1 @& e
" s) `9 K c3 Z [- int main(void)* M8 @/ C; O5 E
- {
, |' w& i' h z. Z - HAL_Init();
( p1 p7 r; W1 D( a3 ^% x - SystemClock_Config();5 X3 D( }0 W% H( F
- MX_GPIO_Init();
) Z! l6 f: `. G( E1 u& m' P: C$ j - MX_USART1_UART_Init();4 i; {( }) I- z$ w
- * r' _9 v7 O8 c( [7 \9 E) F& G
- // xTaskCreate函数参数说明:
& @4 ]6 F9 P9 i9 U* r# z; P - // 1、任务入口函数! M4 Y" j7 W: V! |
- // 2、任务名称# M' Z/ {' {7 E" ~: \0 E2 {! y
- // 3、堆栈大小,单位word, ^: E) }- M& p
- // 4、传递给任务的参数/ I1 {/ ?6 c/ [" `- `3 b6 o
- // 5、任务优先级; w7 m. z9 N8 f; u. _3 P5 z* Z
- // 6、任务句柄
9 Z# j, Y9 T7 [ D* { -
5 h! `5 d( X( G; |7 T/ B - // 创建任务led1灯任务- h% w( u- `& m' @" P
- xTaskCreate(led1_task, "led1_task", 64, NULL, 3, NULL);
2 x( ]1 \1 n8 w1 Q - ( k9 f) i/ r8 e+ R
- // 创建任务led2灯任务
" c# u+ K; d! s - xTaskCreate(led2_task, "led2_task", 64, NULL, 4, NULL);
( Y2 Z; V, V) m/ m& C' f4 k - 6 J( y2 m _! }
- // 创建串口打印任务( @$ B" Y; \! {5 w$ q1 z5 C
- xTaskCreate(print_task, "print_task", 128, NULL, 5, NULL);' c3 i4 b2 A( `+ p; d0 I# K
( e$ U/ T0 K' @4 w1 P- // 启动任务调度
& p' Y+ _/ _. v* }! O* Q1 o - vTaskStartScheduler();
7 x6 `7 T, b+ [! D0 K - ; z" b% b, v4 l* n3 m% f7 ?
- while (1)
9 g, P! I4 V( v - {/ [ x0 M T. f+ b
- }9 s& ]- ~3 m: e/ \* V. S
- }
复制代码 % o- H+ R5 d5 H5 H9 ~% A6 c
到此,移植完毕!
Y6 v5 ^: e! u9 g% V+ u- E! @) E E* u9 n7 |; k' b8 J8 L7 i
总结:移植FreeRTOS还是相对简单的,主要是配置文件FreeRTOSConfig.h的配置,每个宏定义所代表的意思需要好好查阅!
$ q- Q7 H3 {8 h/ P9 @% b
7 T' B+ m% `" f" h8 ]9 m& X$ `# T$ G1 p, d; E
1 k- v* L; [) c |