1、源码准备1 `# w* o3 N) y4 H% Q1 D
首先准备好我们下载好的FreeRTOS源码以及STM32F407的工程模板(这里以原子的F4跑马灯工程为例,可去原子的开源论坛自行下载),源码下载及参考请参考我的上一篇博客。
; |9 @6 H1 g- F' |6 l) v( M! L6 l* F0 x: ?2 j
2、创建FreeRTOS文件夹( t' i/ K/ T2 |' Q+ Z) d0 f
在工程中创建FreeRTOS文件夹,将FreeRTOS源码全部复制到该文件夹下: D/ x6 y6 Q) k0 v3 w# a! O
+ F" k7 ~7 L3 ]% R7 }
; t# n- T! B% v' S9 h/ U+ g& ]5 N& ~( M
( e5 A2 n5 _5 a) q! i打开portable文件夹,删除没用的文件,留下如下文件即可& d+ ? V) o7 D$ I% K% x7 I- M
( U }/ d0 o- q( s, x' L
$ W }. w3 C5 @1 S0 M6 |0 v6 T
, G8 `+ N' {1 j n Z6 y; f
3、在工程中添加FreeRTOS源码
4 [4 n# g6 K: a$ K打开工程,在工程中新建FreeRTOS_CORE和FreeRTOS_PORTABLE,然后向着两个组中添加对应的文件,如下图所示:
0 G" t- A" m7 C; W- O8 P. A
0 |8 \4 O9 Q" k/ t2 J: g
, V7 A! e+ i0 p2 I/ a. n5 x4 b
* ^ T7 u5 Q! k& E& t8 L, \( a6 W$ S FreeRTOS_CORE的文件打开FreeRTOS就可看到,port.c是RVDS文件夹下的ARM_CM4F中的,因为STM32F407是Cortex-M4内核并且带FPU。Heap_4.c是MemMang文件夹中,这里有5个文件,是5个不同的内存管理方法,为什么选择第4个,因为FreeRTOS内存管理所决定。
) i5 r q! I0 |: ^3 C
4 y& Q) M5 J* }) I% ^2 {4、添加对应的头文件路径
$ w5 A, p2 r4 I! p: H/ M$ l* t2 H' b& ]
/ ?( ~" q0 y8 y. |. a* y5 H; V
5 ]5 P! q" r' `" o' q
5、编译及错误解决
* x4 i% d3 m/ P3 C5.1 找不到FreeRTOSConfig.h文件4 o( C& H, C& ~1 K, ]. `
完成后编译一下,会出现如下错误& P& q, Y" L' K; i: a8 ?3 k' S0 r& _
9 o9 o, T6 Y3 Z2 Q1 t3 X/ y' R
E9 S7 B: R, T2 [; x/ h5 k7 R& ]3 f% j
很明显是没有FreeRTOSConfig.h这个文件,那我们就把它添加进去,具体位置在FreeRTOS的DEMO中找到CORTEX_M4F_STM32F407ZG-SK文件,如下图所示& U- E; m5 s# H7 n7 f
# T, I! W2 ~: i( I
1 m. k1 {% b; G. ^
# ]/ e6 e2 D9 m, I8 a 至于放到哪个位置自己随意,一般是放在FreeRTOS的include里面,而这个文件是FreeRTOS的配置文件,一般操作系统都有裁剪、配置功能,而这些都是通过一个文件内的宏定义来完成。
+ V- l( R- D8 ]& e6 p* s o; ^* z/ Z: \7 m1 F; C* Z$ b
5.2 SystemCoreClock未定义
I- d, w5 b2 }* \接着上面的步骤再编译一次,还会出现以下错误,意思是SystemCoreClock未定义
$ D0 U% G, D- n3 K$ v
! ]! s+ `1 O* M0 }( A$ H8 _% H1 v/ r$ S" W: Y5 k5 Q/ I
解决办法:修改条件编译' ]3 ^! J+ a2 ?+ z
b& p2 H" c7 E8 n
+ {( B) q1 i& [, h6 Y
q$ D/ h+ P" m& b5.3 重复定义
. j9 W$ Q, }- I( L- e( h+ w& Y1 I3 ?& Z接着再编译一下,发现还有错误,这次的错误是重复定义& U6 G9 K% V/ |' e& S
0 W W2 v! ~; J4 a
. A( ~( }$ c( N( \) x
4 v4 t* c& L+ n, c" V% ^& D! P解决办法:
$ L# h5 e# b8 Z! ^3 h) C* W$ T* ~1 P/ X. x, V1 U, n6 @
屏蔽掉stm32f4xx_it.c中的PendSV_Handler(),SVC_Handler(),SysTick_Handler()这三个函数,
/ \" M$ k' O! B' L& J! q) ?
$ ~. |" X/ p, T1 l- B
: ] g7 @, M' j5 H* _
' [4 @( C1 t+ A
5.4 钩子函数未定义3 `) c' w+ a5 U$ K, q% ^( z
继续编译一次,还是会有错,这次的错是函数未定义,他们都是Hook结尾的,称为钩子函数。
& Z+ o# r* A' X+ J$ y+ L3 z
6 }" Y7 f' f! Y3 M# i( q
& H! k# I6 Z9 S5 r0 U
$ |8 K# U8 y1 e2 p解决办法:
$ B; } ?7 _* x9 D5 g: G: [) k- O7 V* {, m
去FreeRTOSConfig.h中关闭这些钩子函数,他们都是宏定义决定,这里将configUSE_IDLE_HOOK、configUSE_TICK_HOOK、configUSE_MALLOC_FAILED_HOOK和configUSE_FOR_STACK_OVERFLOW定义为0.
" c' ^0 T" ], P! L( E; [7 M; c% L) h( J: q, Q! T* L+ Z. G E
$ i% s. g% D1 u: N
" w+ k f( F9 _! F8 p再编译一下应该就没错了。
2 X. S1 W3 ?; I& V6 @5 X7 Z7 @
6 J% ~, Q& \! O( ]* n* D$ L, u6、修改SYSTEM文件
3 P1 t" Z2 ]2 _因为原子的SYSTEM文件夹是针对UCOS编写的,所以要进行对应的修改
/ o2 u* S/ m; U, b Q& ]* a# E1 A+ O' h2 D( @
6.1 修改sys.h文件
2 |" G; p; Q1 Y8 A+ r把宏定义改为1即可,要支持OS,UCOS也一样的) ]. K% e S0 T: P$ O" E
n/ y7 L1 J' r! G3 B2 t
& `' j) O/ x' T
/ c" V9 [0 }" }: a; V/ d6.2 修改usart.c文件2 n( L* z+ I$ \& G
修改头文件为1 \' Y0 t/ k' z7 ~* B
3 T. |$ k! X/ g; m0 _
1 E* ~ h( V3 H+ ]3 |' j8 J4 b8 O& ~ H8 m* x3 p6 n4 e. z5 O
修改串口中断服务函数为:
! ^/ f& s& `2 _( z7 h8 ~$ D3 c: D' b. a% l; h2 u* M
) V% n. C4 s$ U0 V p! ^
4 o4 }0 C+ y8 {. U8 K6.3 修改delay.c文件
) ~+ P2 t. K6 d! U' C1 {同样先修改宏定义的头文件9 q; z% q2 R2 @- d
G+ G( M- h4 N- N
# `/ S+ B5 d2 J$ F' ^* m7 h, [4 q+ A/ \& \
接着修改systick中断服务函数为, r# f4 L. i2 A- A5 U. R
7 k! x8 r) r$ E# d! W
5 ?, c8 r4 p) X. }. Y p9 e( s! [: h6 P) L* Q+ [; J5 D2 o
在滴答定时器中断服务函数中调用FreeRTOS的API函数xPortSysTickHandler();
$ h) k, a) m n. r6 s# s
( i6 c3 A5 A6 N1 B# F在修改delay_init()函数,如下:
# t) [( ?2 Z2 _& b( D$ l
' a* T: v/ @: L8 w$ l$ d8 k
7 z) a& r' h; x0 J0 _/ F& i% v. G
T8 D# l6 [$ b3 v/ v k
接下来就是延时函数的修改: Q3 g9 e0 a1 \/ W
' v4 c9 \9 g: T8 q
# ]* c+ S. B; a' {6 u+ ]
3 \8 e6 V% J f$ ^( l修改完后再编译一下,会出现重复定义的错误,如下图:- F& M( T; f" j4 q6 }: `
2 B% X9 K8 Q7 U- S4 N0 D
+ G/ s1 ]& n# q, n
$ f7 T* @* W3 Y/ i% j* e# H解决办法:屏蔽FreeRTOSConfig.h掉底部的#define xPortSysTickHandler SysTick_Handler
9 A+ U3 V, @: q$ A- r( `9 y1 H" c
2 @; x6 `6 p2 c) G4 l
0 v' H n$ A. z% G
: q& Y3 |% |4 ?7、修改main.c进行功能验证) ?. t1 G0 ~. ?6 }! u
主函数主要是实现实时系统多任务的创建,具体如下
3 |8 Q/ i3 O2 f3 n4 ~ }2 @. X5 m4 d% m
- #include "sys.h"- d- t; {8 g& l/ v
- #include "delay.h"
7 N# t8 _" Q! i9 L+ ? p, B; C1 { - #include "usart.h"
# [1 ~" }" r4 r* [3 N" G& u; [# E* J - #include "led.h"
# d3 B8 N5 x, W6 Y# U0 |6 n/ D m - #include "FreeRTOS.h"# O! A5 u' A; C3 G) x" \( r9 C; n5 {8 M
- #include "task.h"
' o/ Z7 |- F; C/ ^' `! D' f4 a& U - / {0 A3 A! ~+ t; H- Q* ^
- #define START_TASK_PRIO 18 p- _/ Y$ j2 G' e# Z& }3 A3 Q$ _ U8 j1 F& ~
- #define START_STK_SIZE 120" G) o/ v' I: h8 A" R1 }
- void start_task(void * pvParameters); //任务函数 p+ o; u8 b% E {
- TaskHandle_t StartTask_Handler; //任务句柄 ( `2 O9 b9 J- I7 W- {
- ) [. r; p4 X! o* f# P$ q' C& X
- : f4 b4 p2 ^7 c3 N4 Z1 d
- #define TASK1_TASK_PRIO 2" e; v5 O2 n9 W0 ~2 M( u# `
- #define TASK1_STK_SIZE 120! O, J# W6 u/ _' W
- void task1_task(void * pvParameters);* ?7 n: E1 ~/ r- |
- TaskHandle_t Task1Task_Handler; //任务句柄 ! F. @5 G( Z2 I; _ t6 U
- 2 h/ S' V, e8 I: |* d( M+ k
- #define TASK2_TASK_PRIO 36 J, b3 ?0 }9 |, N' ~8 H7 ^
- #define TASK2_STK_SIZE 120 # I) b- B" o* E2 n# u
- void task2_task(void * pvParameters);
- @" z% a4 x% ^+ C - TaskHandle_t Task2Task_Handler; //任务句柄 & n/ ]7 ?) G4 h L, C! P5 Q
- , P. q* A) Y$ H) q( ]. Z1 w
- int main(void)
2 z5 Q% K3 f- j! D" i+ z# e7 G, |$ X - {% r! d9 D$ [, C7 l# j" x% Z; x% |9 c
- NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
, f m- x: B* _; S3 m - delay_init(168); //初始化延时函数
/ W: r" J6 Q% C) {) e4 [0 P: ]$ Z - LED_Init(); //初始化LED % I& C, z# p! W' Z b7 ]: @
- uart_init(115200); //初始化串口8 U6 W9 A4 T; T$ A1 v+ _
- 4 U# j8 T1 c* r l) g0 w9 E8 ]/ T
- xTaskCreate((TaskFunction_t ) start_task,
, _, p6 m9 O, f8 b# B - (char* ) "start_task",
. t# H( c* d0 Z: Z - (uint16_t ) START_STK_SIZE,# h+ _ j; H& a) p* A, Z3 i/ H
- (void * ) NULL,1 J, t5 K H6 r3 X# ^! U6 j4 d% y8 Z
- (UBaseType_t ) START_TASK_PRIO,0 l- p% j" l; p0 m# z
- (TaskHandle_t* ) &StartTask_Handler);
- V6 T( T; z; r0 E - vTaskStartScheduler(); //开启任务调度" _( i: L/ y& X0 O. O. f
- }
9 y7 P0 k; {8 [) q% E - 6 e* e9 p; A: e1 a* w; a- q, ]% O* {
- void start_task(void * pvParameters)
8 Z$ C, `( @& H( S K: A' \# g# L' b - {2 O- l; [$ h; `! h0 _* j, h
- //创建Task1* W3 v: W, M( x4 P, D1 C
- xTaskCreate((TaskFunction_t ) task1_task,
5 g) v8 F" y: L2 H - (char* ) "task1_task",% ?8 h& H6 H6 `; l
- (uint16_t ) TASK1_STK_SIZE,
8 b" M2 j3 p) v3 f; C - (void * ) NULL, j& E3 W! Q* o' K, {! E
- (UBaseType_t ) TASK1_TASK_PRIO,
l8 l1 z7 b. }. v - (TaskHandle_t* ) &Task1Task_Handler);! {& y' s$ P' g! Y. ]& L( L
- : l3 g1 M6 u; Z7 T! O4 M6 h6 J
- //创建Task2
0 C. u; Y+ E4 {: Y' Z1 |3 J$ a - xTaskCreate((TaskFunction_t ) task2_task,
2 ^& ?2 X" M+ f* ]& F! r: Z - (char* ) "task2_task",
' S* |- s& U4 `6 g - (uint16_t ) TASK1_STK_SIZE,
+ V2 P# p( h3 v1 h7 A1 D5 ] - (void * ) NULL,1 W% t0 j1 l7 E- _8 |0 Q P
- (UBaseType_t ) TASK2_TASK_PRIO,. a0 U" @4 _" H4 W4 s; }
- (TaskHandle_t* ) &Task2Task_Handler);, U* ?! q! ?- p( L" n! W+ H
- vTaskDelete(StartTask_Handler); //NULL
: i3 Q( K8 Q4 v8 P2 B. f - }
) O9 g/ _# b8 W8 ?+ ?. h5 h - V ]% q; U: U7 A0 ~- v6 T
- void task1_task(void * pvParameters)
7 p/ S/ O0 U% n u8 j. C# |, j - {
9 E& O" X- }. |& i - char task1_num=0;
6 B9 e3 W8 `; M5 F9 X - 1 ?3 l: \6 i% X4 j( ]" X
- while(1)7 }* p4 G) O: m' _+ j, L
- {0 \4 L! F N$ Z( Q( m) s4 e) W
- task1_num++;
( G) h+ \! u" F' F! h5 y& y1 } - + p6 A- v( V2 N B: V1 s( A/ ?
- LED0 = ~LED0;
& ^' a) E6 \, } - printf("Task1 Runing %d !\r\n",task1_num);
" t; S- d- t) r - / V& h: J7 s- h
- vTaskDelay(1000);
, g+ I" x% v3 o% n- Z1 F. }+ ^ - }' ~4 _8 s, n. H- _2 i
- }
9 E; ^; r u7 p9 O2 b - Q2 y6 g+ y: W+ t
- void task2_task(void * pvParameters)
! {. b! E' i. z( o$ Y3 C - {1 M; G+ Z; A7 p
- char task2_num=0;3 C7 |* B) w% M
- while(1)3 Z; [3 \) F; Y
- {
' W$ J$ k9 f: B5 C5 ` - task2_num++;: q+ ?2 n3 b( n' e6 M) w7 T- \
- - @" w( l; h+ o% u7 t' N' D! A
- LED1 = ~LED1;
: ~1 W% M% G0 _: u2 F - printf("Task2 Runing %d!\r\n",task2_num);
" Q H$ E$ r7 ]) S) Y) [' n3 Z - vTaskDelay(500);1 K' [+ O. v! {# p# W# K: X
- }
H, q1 J: c F* d - }
复制代码 ( W6 J' K; `) ?7 [& W
实验现象;
5 f4 e; `" t) z/ _2 _* P0 J, I1 x
2 u9 h* r2 B& y* `2 {' B 开发板上LED0和LED1进行不同状态的闪烁,串口也会打印任务执行次数,很明显任务二的速度是任务一的两倍。/ q9 R) v6 J. E: f- ^5 I& I3 v
8 i7 ?( a {, K0 q" Q1 ~0 p! n1 b! f
6 T! ^9 x O! W9 A
0 k4 t3 ^+ D$ x6 k
* j. {4 G8 `/ D7 n5 ^8 x
2 X7 o5 x/ a! i8 x( g) J
我这里使用的是STM32F407VET6,原理都差不多,用那块板子都一样。至此,FreeRTOS移植和任务创建成功。$ r3 a" W- i: a
7 A/ I0 |1 D+ g$ K/ b. n: ]
& Y' W/ [4 u0 m$ Z4 I
5 L0 o. ^* V2 ?4 G/ H
|