(一)裸机工程创建' y4 O- u* N1 {# Y
使用STM32CubeMX创建裸机工程:
( D6 M3 Y; I. T( B) P. J& c! M" @. \- e
这里我是用的芯片是STM32F103RBT6,时钟主频为72MHz,配置时钟树:
% R/ K, `2 U2 ?! H8 g% g: {# Y. E9 E h$ M
3 ^# d4 h! f, K9 `0 z
; L, S! m E: y- x配置调试串口1:
7 R" j; P, z8 L) G8 f3 K' V9 T/ E
6 m4 q0 M9 i3 \$ ]6 g* @) [3 ?2 t
配置两个LED灯:& f9 i2 L G" V% k* L% s( U; M
) D* q5 Q% |3 W( z7 K
3 j9 i7 u# }) c3 G: \: h
% E1 n4 c8 U+ a! j" W4 A" H& s8 { H由于FreeRTOS使用系统嘀嗒定时器当作心跳,HAL库也使用了这个定时器当作超时计数用,FreeRTOS和HAL库不能同时使用SysTick定时器,所在在这里将HAL库的定时器改为其他的定时器,我改成了定时器4:/ O- l, P/ Z, ?1 \) ~
5 R. ?6 X$ s: n7 Y$ {
; b0 h) f+ T& i* V; F2 |" A8 E; u
3 o7 ?; Q; F) Q1 ]/ e* r配置中断,注意这里一定要选择组4,也就是全部为抢占优先级:
; D# m6 s1 ^: B; N& G
0 [3 J+ j, L- `, m8 M
6 g$ [ u) f4 w
$ t8 N! Y: ]/ N+ z" r- {
输出工程:% k' D- t. B" c* |
" W+ o' V( s& ]+ H# D) I! [
" }) N. ~, p5 K/ u" }+ u$ a, w6 f
' ^' ] w4 q G0 z( B5 [. M4 M
: K/ f5 _% Z- h* W. F' V) _& `2 x8 W3 u& ^# f. I8 K
(二)移植FreeRTOS
3 w f4 H6 K. ^7 d* c& m6 p9 Z' t/ L& e0 [
解压FreeRTOS源码包有如下文件结构:: G+ I8 ~ ?5 f. u2 w% J
/ `/ T3 @; S" O- v
) F4 ]/ T% Y' V' V# G/ q S' r
/ Z9 s: \- N2 z8 \: U/ r
在Keil工程目录下新建FreeRTOS文件夹用于存放freertos的源码。( i9 w8 D+ w& L; S/ g7 y
- j) l( _! `7 g" g
拷贝FreeRTOS/Source/文件夹内的以下文件,放入keil工程目录FreeRTOS文件夹内,效果如下图:; K' t! v3 r( C4 _2 ~1 z1 e6 {
" ~/ u" e' O3 n: _6 V5 c
o% U& C) W8 e# u( V" o
# v, a, [; n. [/ t/ d% ?4 }, n在Keil中将FreeRTOS源码加入到工程内:- j3 R1 d C M3 \3 \; D
. W C" A* h, X; V8 n
) L r3 N8 i8 g |1 u N
' H( o! }7 r J* |# |
打开stm32f1xx_it.c文件,在头部添加FreeRTOS的三个函数导入:- extern void xPortPendSVHandler(void);6 W# y: F4 q7 y- ~
- extern void xPortSysTickHandler(void);
# n' O6 ^) \' O' b( v: z - extern void vPortSVCHandler(void);
复制代码 ) I5 S. A/ o5 a2 s
4 u2 G. V$ @8 X/ w; x
3 F1 Z4 a: `+ S5 p5 V! ~5 L4 k+ U在函数SVC_Handler中添加函数调用:
! F3 B4 P) l7 P4 ]+ r
4 U k- o1 r8 `* o
. x! {2 q% t. E/ e* c
5 ` ~+ J3 ~, G4 Q' Q; L在函数PendSV_Handler中添加函数调用:
4 E1 h( g5 k( s7 S6 ~0 I% \. S& s1 m% I* W0 Q/ F' o) v
# P' }2 J4 I; K/ C$ R, _. b/ ^$ r" J: f, K! n: ]
在函数SysTick_Handler中添加函数调用:
3 N9 f- P+ x! G3 ]0 C' b3 U4 }' W% Y
; e1 B/ r& W- r
: \/ `# }8 a& a- B2 _' \6 L2 P* m# n8 y- \, B
新建一个名为FreeRTOSConfig.h的文件,这是FreeRTOS的配置文件,这里我贴一个我配置的:5 f* b" O+ \( r0 o, T+ N" ]
' V T% p% O; C% {* ~
- #ifndef FREERTOS_CONFIG_H__
! X* I* }/ y Z0 F" ~ - #define FREERTOS_CONFIG_H__3 ?$ J. y- R y/ A4 A+ @
- 0 g6 {7 r! p( R* d3 C" S& V
- /*
Q+ i3 H$ Q6 ~7 ~
/ J! L3 |& j ]- */
, K1 Y7 l/ t2 \( `
" ]! f" d6 R' N& `- // 设置为1使用抢占式,为0使用时间片轮转调度。
, B, K( t' I$ D& d( U& g - #define configUSE_PREEMPTION 1
: B: H( ]& K, k8 }
8 r4 [0 W+ d' k9 V: @$ u" T- // 设置为1使能低功耗tickless模式,为0保持系统节拍(tick)中断一直运行。* d& o( @% E+ [6 J4 { C$ n
- #define configUSE_TICKLESS_IDLE 09 Q1 m* _% ^' O" G. n" M. ]
( k' X; H+ s/ z' E* x6 n; v- // 系统时钟主频
: e8 i* p, `% H7 q. {- e4 i7 C8 M - #define configCPU_CLOCK_HZ 72000000; ]9 c3 n( {% z5 D: v3 _* c
F4 d+ f9 _6 o5 x1 U. C- // 系统节拍中断的频率,即1s进中断的次数,配置为1000就是一秒进1000次中断,系统节拍就是1s。
: ?7 i- ~ _* w( M5 h - #define configTICK_RATE_HZ 1000
; @8 j$ l' T7 L! e, ]
( @3 P8 [% D0 h7 h5 U6 Q. W- // 任务最大优先级,对于STM32来说最大不要超过32
% O# _4 d7 d6 j w; ~5 q7 S - #define configMAX_PRIORITIES 32
! g, f6 |3 E" E! u% r3 M y3 a5 ~; v0 R
* Y- Y- T' P- g- // 任务最小栈大小
3 m, U$ d7 o1 I9 L! u" P! } - #define configMINIMAL_STACK_SIZE 645 @: {0 ?6 |/ L6 {% ~8 I; ~
- 7 S$ y" C7 l8 I" a: ]; Q
- // FreeRTOR堆空间大小
k6 ^; ^. R9 _4 z: m - #define configTOTAL_HEAP_SIZE 8192
: S4 j% i( |# {" A
% B# j7 Z1 \$ M, Z" J( R2 e- // 任务名称最大长度
8 _: l( a3 x1 x# N& s6 X - #define configMAX_TASK_NAME_LEN 16
! i9 v7 j* h; i: x$ a ?! |- o4 ~ - 3 F. f8 e/ I; G; x
- // 系统节拍计数器的变量类型,即定义portTickType是表示16位变量还是32位变量。
; l" Q3 h3 H! u3 X" ` - #define configUSE_16_BIT_TICKS 0% p, [- l+ Z' E
8 b& u$ W: R% \- // 设置为1允许任务调度,为0不允许(时间片耗尽才让出CPU使用权),该参数抢占式方式下才生效9 V: V8 |* q* i
- #define configIDLE_SHOULD_YIELD 1/ b8 |5 u# Q2 z
- ! q7 l1 o, ]" [6 l `
- // 设置是否使用互斥量, g, r) e) c! o$ ]: Y, ]' B
- #define configUSE_MUTEXES 1
- `4 y- _6 W4 D, z8 U" s, I
4 D W9 m2 A7 \, C- k$ f& M) j- // 设置是否使用递归互斥量$ n" `, v# r; ]% v
- #define configUSE_RECURSIVE_MUTEXES 0
1 V$ ]: i) d n X - ' T b6 z0 q/ S: _& o
- // 设置是否使用计数信号量
* O, J4 H9 V2 y6 d6 q+ M5 N4 t - #define configUSE_COUNTING_SEMAPHORES 00 T4 H) k5 O6 E$ W& [
- 5 F3 w* f% E. u6 A+ ~$ D6 c- x" g
- // 设置可以记录的队列和信号量的最大数目
1 y; J; V6 D, K, }: K, X. i - #define configQUEUE_REGISTRY_SIZE 103 y, y6 j) O. ]% ^+ S! Z& V' o
- , m( d% T3 ?2 ]. S, Q* z+ Y2 K! v; e" ]
- // 是否使用空闲钩子函数( T) G5 W$ I9 }* M
- #define configUSE_IDLE_HOOK 0
( |7 j+ R) s# c3 E3 R - . r! D& r# `4 t5 j- k
- // 是否使用TICK嘀嗒钩子函数
# L% U. Z- c+ m9 Q9 c+ Z - #define configUSE_TICK_HOOK 0
- q0 w( y0 [: F+ A2 O- C5 D - - a0 E# Q6 g( \8 \! @; W, Z
- // 是否使用栈溢出检查& j7 N! T, w; R: B4 A
- #define configCHECK_FOR_STACK_OVERFLOW 0
! L8 N/ V0 |* |$ K/ X* Q! x - 1 ]; o/ o3 a2 ]. `0 v+ O1 a: R
- // 是否使用内存申请失败钩子函数 i. U3 Y. l9 g; F- u, p% ~
- #define configUSE_MALLOC_FAILED_HOOK 06 g( u% P2 h2 W( a! ~3 e, _
- + X& X- j9 q) j- ~: c6 E
- // 是否使用软件定时器
; c$ `% S% a1 h ~ - #define configUSE_TIMERS 1
0 s3 I$ q# Q( R3 \. V
6 Y% E; ]& ~) [6 m/ U* C# K- // 设置软件定时器服务/守护进程的优先级5 y0 n1 k4 v( P1 J
- #define configTIMER_TASK_PRIORITY 3
7 f. B4 Q0 u* V4 A - l2 y0 L c$ p) P
- // 设置软件定时器命令队列的长度
! e" B( v& r$ B$ z- t, n - #define configTIMER_QUEUE_LENGTH 10
) ^- T2 p+ s- ~) w: ` - 8 m7 b4 X# J! Q
- // 设置软件定时器服务/守护进程任务的堆栈深度2 Y) {) |5 ^0 b m/ T
- #define configTIMER_TASK_STACK_DEPTH configMINIMAL_STACK_SIZE3 t5 c& I6 q6 f3 n+ c3 w
4 F9 w9 T. Z' o6 E2 k- // STM32的最低优先级/ ^0 u6 m$ k: i/ o. ?* d* F7 l6 a( B/ [7 H4 L
- #define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 15
, Y( C9 ]3 e, B4 x; M
' L7 l9 ]+ x! S/ F: J- // 能够在中断服务函数中安全调用FreeRTOS API的中断最低优先级
! D+ ]) M+ U. C" E1 [% X T% G - #define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 1! `" g9 P. c& _; u" j% k0 ~ B
" {$ e% f, I& F& [6 v. J- #define configKERNEL_INTERRUPT_PRIORITY ( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << 4 )
) F% L1 l8 F, Q7 F6 t. }) d - #define configMAX_SYSCALL_INTERRUPT_PRIORITY ( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << 4 )) l3 M# A" Y# T C1 N+ [
5 U) j- A1 J" L' O- 6 I$ {# k1 R5 P
- // 将以下定义设置为1以包含API函数,或设置为0排除API函数
d5 s5 @+ }/ U8 } - #define INCLUDE_vTaskPrioritySet 1
- h6 S% Q/ @7 X5 Y. |* ^, a - #define INCLUDE_uxTaskPriorityGet 1! {" i) X& t) |8 f7 ^9 L H' x* T
- #define INCLUDE_vTaskDelete 1
0 E& Y- s+ P. c( f+ r" R - #define INCLUDE_vTaskSuspend 15 t) G4 g1 J& p; d7 m
- #define INCLUDE_xResumeFromISR 1
' l9 z; \' @5 P" _9 G# w - #define INCLUDE_vTaskDelayUntil 1
5 u8 S0 L! d- ]# h& w8 z' T - #define INCLUDE_vTaskDelay 1
8 ^ j" W6 V5 p4 `, O - #define INCLUDE_xTaskGetSchedulerState 1$ `7 U J- |3 N5 R$ x
- #define INCLUDE_xTaskGetCurrentTaskHandle 13 S/ J3 R. u* h3 Q; C3 [
- #define INCLUDE_uxTaskGetStackHighWaterMark 0( t4 E1 Z- Q2 z0 V! G6 ?
- #define INCLUDE_xTaskGetIdleTaskHandle 0
/ [% |8 I& v' D. C - #define INCLUDE_xTimerGetTimerDaemonTaskHandle 0) ]1 b' ~$ |2 H- s$ f0 V
- #define INCLUDE_pcTaskGetTaskName 0
- `* I1 ^; d" X) Q) j& z - #define INCLUDE_eTaskGetState 0# I$ L9 o- V) O5 ]" c
- #define INCLUDE_xEventGroupSetBitFromISR 11 p3 c! p5 j; N. ]0 E
- #define INCLUDE_xTimerPendFunctionCall 0
& g4 ~( f1 A8 O
) {9 ^! ^: G, g+ n7 |8 d0 R- #endif /* FREERTOS_CONFIG_H__ */
x( T3 X S8 f+ r
复制代码 4 N( D2 e: T1 {/ H. J7 P
修改main.c文件,添加头文件:
) a3 n G! i, \- M- L/ A9 l( E+ t
) Z. N! x z" C w! U- #include "FreeRTOSConfig.h". E5 ~4 w5 ]: h6 }; n+ V
- #include "FreeRTOS.h"8 ?; T* ~; P2 b% b
- #include "task.h"
复制代码
4 y, [! y. G# ~) L8 j这里我创建了三个任务,任务一为led1灯每间隔500ms闪烁一次,任务二led2每间隔300ms闪烁一次,任务三为串口每隔1s打印一次helloworld。
1 q( G$ b9 N/ O. N1 X; L; I/ b* `' n) t( c4 S$ y+ J7 Y9 d
7 d4 U$ Z: g- {( B- void led1_task(void* arg)7 q- S, `/ g% P- M9 ]
- {( B4 k+ Z5 L1 A( |* e$ Y. n! |* I
- while(1)
- ^! m* D. g/ P; b# @/ L - {8 ^ X3 _; A3 e2 l& x- h+ P
- HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_8);5 R/ C( M- N+ P0 L
- vTaskDelay(500);
( Y. T4 f; l, V! l& `, f - }* M$ h+ W4 _' e7 J+ F9 P1 E
- }
$ E( i" w" r! X+ L& } - : z3 }( P" |$ v" k, j. r
- void led2_task(void* arg)3 C$ ^* _+ s0 l1 H- O
- {
t. \5 l6 q) H: P - while(1)7 Q/ S, P8 {+ d
- {
Z( ~" `" z* |$ h - HAL_GPIO_TogglePin(GPIOD, GPIO_PIN_2);3 S! K" C* ?0 e, q V
- vTaskDelay(300);- V2 x1 }6 O3 C3 c" v5 _
- }# B: d' _7 J6 O, E6 _
- }
: ?! b7 N1 B1 B! M
0 Q( r( R4 M: y8 M/ \- void print_task(void* arg)7 j/ M8 _- {7 j
- {
! Y! X+ W; A; m q/ z - while(1). B4 A' n' W+ e# P" c5 p* ^+ H3 V9 Z& z
- {
2 E6 X/ R* V3 ]0 | - HAL_UART_Transmit(&huart1, (uint8_t *)"HelloWorld\r\n", 12, 100);
/ a- B2 e1 L& G& `" Y - vTaskDelay(1000);
0 W% i8 f |- w3 `( ?; g - }
6 X* i! S3 R' o - }$ z; N |3 _: Y# x
' C" ~9 N* _3 a- int main(void); {+ g: d7 U- z
- {
, L7 O8 R( R9 Z+ c - HAL_Init();' C6 t7 c# `- A* ~9 {
- SystemClock_Config();4 e# E7 S" N) x! d
- MX_GPIO_Init();/ O- l+ P- e1 o% A
- MX_USART1_UART_Init();, t5 j8 f8 T+ u* o" W! O! `
- ! g$ q) O% F1 x3 n
- // xTaskCreate函数参数说明:! w1 G' i7 {3 g: B! g5 f% n
- // 1、任务入口函数
$ ?9 k& p3 [; \* ~1 z - // 2、任务名称
# C7 P( S4 q% C; i4 j; W3 q - // 3、堆栈大小,单位word. T1 b' J" h+ s9 T
- // 4、传递给任务的参数7 K' L8 |# I. `1 n- y
- // 5、任务优先级
1 o X) c; W% V6 g% P4 O - // 6、任务句柄 T( W/ l1 e. m3 Y; W6 r
- $ x1 Q+ Q, r, q9 O
- // 创建任务led1灯任务
. \( T J3 r! e: O - xTaskCreate(led1_task, "led1_task", 64, NULL, 3, NULL);
; H/ I- D" _( p. P& i - 0 e- i; R' w: Y
- // 创建任务led2灯任务
5 w0 `- C1 o7 s6 s - xTaskCreate(led2_task, "led2_task", 64, NULL, 4, NULL);* _' Z8 W) p0 [5 C6 S0 @* @
-
8 e7 g- C, V( Q8 r( N8 { n - // 创建串口打印任务7 L0 O3 D0 ]: Y* p. R# I$ v; s7 t
- xTaskCreate(print_task, "print_task", 128, NULL, 5, NULL);" \3 \# [# r' U. z! {
) q% m! V5 `" Y# o0 C/ ~- // 启动任务调度
0 P, f7 q1 R4 q ~7 W - vTaskStartScheduler();
- G' i$ R+ L) P
6 U6 t# X1 V+ R6 s2 @- while (1)
0 p) p$ O0 E8 H; x5 R4 N - {. k& ^$ b4 l q# g# S* V8 F
- }0 D: h4 K$ g" [
- }
复制代码 1 i6 V, D" j2 a" i; n! U
到此,移植完毕!
- z K( K- X3 l$ u; w: T+ b" q+ m( d% J v$ m
总结:移植FreeRTOS还是相对简单的,主要是配置文件FreeRTOSConfig.h的配置,每个宏定义所代表的意思需要好好查阅!0 y' p; B) g1 p/ e6 p- E
|4 Y3 P, T: S
; ]2 k. \" P1 E9 C- f+ X1 O0 O. E' ?8 j. Z5 s8 o
|