1、源码准备. b' C0 f F' D! Q; @/ |; U m
首先准备好我们下载好的FreeRTOS源码以及STM32F407的工程模板(这里以原子的F4跑马灯工程为例,可去原子的开源论坛自行下载),源码下载及参考请参考我的上一篇博客。9 ^6 Q8 B- E l/ P# R' d) T$ p5 _
6 p9 f2 b0 M% @/ y3 Q2、创建FreeRTOS文件夹
( C) O" M% h0 w m8 h% l在工程中创建FreeRTOS文件夹,将FreeRTOS源码全部复制到该文件夹下
6 D1 T0 B$ S# M _' N+ X* |2 `. }
" U+ _3 Q5 W; Z
/ M* c% l$ Q; g: U6 Z. B& \
7 s4 e/ o6 }; B! p# g2 i2 j打开portable文件夹,删除没用的文件,留下如下文件即可, H+ u( W$ z2 q' f x" O
% N" ]' G7 L* m% Y7 ? C; I
: d. D9 t" @6 T7 l. n. c0 r
' O' V2 y l5 D6 S, M2 B# ` B3、在工程中添加FreeRTOS源码+ c n! Z. l( u! k, u1 p* B+ s5 D) u
打开工程,在工程中新建FreeRTOS_CORE和FreeRTOS_PORTABLE,然后向着两个组中添加对应的文件,如下图所示:3 k4 J+ @4 M! p; R5 F! F( W( X
, L3 s2 a' I( b/ x( |
* x5 K! S7 v9 W% j `1 \6 E; t% b d) n, u" i1 @
FreeRTOS_CORE的文件打开FreeRTOS就可看到,port.c是RVDS文件夹下的ARM_CM4F中的,因为STM32F407是Cortex-M4内核并且带FPU。Heap_4.c是MemMang文件夹中,这里有5个文件,是5个不同的内存管理方法,为什么选择第4个,因为FreeRTOS内存管理所决定。" G6 S$ u Q+ ^; V- U. J& q
2 I( ]% r8 w" u% y: P6 [! D
4、添加对应的头文件路径, e, @/ [. S' X, U" E
3 B" f. `7 ^1 Y6 m( [' w' x
4 M6 G4 r. q0 D
! F: e% n. E; x( O% \
5、编译及错误解决! [) m# m2 D+ L3 J. u! f1 n
5.1 找不到FreeRTOSConfig.h文件
( N1 h! |: \0 s1 M5 E8 ^+ `3 Q$ a完成后编译一下,会出现如下错误
8 P1 K f0 w1 n @# l9 T( X4 e( m+ Z% u9 h
! o* F: i: _& j6 z$ J: |: L1 E# N7 I* H. C( a9 c2 d; w$ S+ [
很明显是没有FreeRTOSConfig.h这个文件,那我们就把它添加进去,具体位置在FreeRTOS的DEMO中找到CORTEX_M4F_STM32F407ZG-SK文件,如下图所示
- |% M( S; J1 E7 a, n' L. H8 ?5 C) W- ^
: B k3 q0 X' I& C
! H# S$ V- {# i 至于放到哪个位置自己随意,一般是放在FreeRTOS的include里面,而这个文件是FreeRTOS的配置文件,一般操作系统都有裁剪、配置功能,而这些都是通过一个文件内的宏定义来完成。
7 r! t' k4 ~8 L, I+ n5 D) s8 O( b. Z2 B
5.2 SystemCoreClock未定义) x+ Z' _6 {- k
接着上面的步骤再编译一次,还会出现以下错误,意思是SystemCoreClock未定义
q/ p8 p3 M9 q
5 }, v7 v! ]3 h f% M3 m7 Y" z
# ~2 |! W' f! l1 `解决办法:修改条件编译; l1 B4 b$ k6 L( D1 {" D0 K
# W. f6 W, P Q( z: R: H
9 Q8 e( G5 F$ y |% A+ ^5 b, `8 ?- l
5 c! J! \# p `0 K. O5.3 重复定义( H2 X* l3 l( k: F6 B9 | B
接着再编译一下,发现还有错误,这次的错误是重复定义
; S6 e( h# e& D3 l4 r" U) `
- b2 c3 o& R! x" f& ~8 h
1 X% I; q, n) ^0 |) w
0 O+ F% k# {- U6 X. P# }解决办法:
; m$ X6 B1 j( F" r$ s m# K- v, K& w7 p7 N: s+ c1 a% k* R: |
屏蔽掉stm32f4xx_it.c中的PendSV_Handler(),SVC_Handler(),SysTick_Handler()这三个函数,# y, i5 p1 w; w' B/ z
+ u$ `$ z6 A8 x4 q1 i; s
; v! s6 f' Y; Y" ]( d" |0 z' M }6 h7 [( f
5.4 钩子函数未定义
. s) K: a" H' y 继续编译一次,还是会有错,这次的错是函数未定义,他们都是Hook结尾的,称为钩子函数。; B' p: T' Y' A% V' n9 r
8 U2 c3 g Q. f6 Q( [- ]9 L
+ X7 ~5 U6 C" Y; w8 x I
( y7 i3 m& V5 @: b3 A2 B解决办法:
- o% \. h: N3 [! o7 Y0 d* W7 h% ]/ D* l" \: k+ X' i
去FreeRTOSConfig.h中关闭这些钩子函数,他们都是宏定义决定,这里将configUSE_IDLE_HOOK、configUSE_TICK_HOOK、configUSE_MALLOC_FAILED_HOOK和configUSE_FOR_STACK_OVERFLOW定义为0.
. b5 b3 J. T2 d4 G l2 h! y7 m( N& T" E' x% h$ E3 w- ^/ o
* _# B B9 Q6 i1 U( s% [- x6 r$ X* f; j, K5 s( m/ I( i$ D
再编译一下应该就没错了。
; R/ [, z2 N9 r) f5 T% {3 R5 X- F8 ?- e0 p# E# R' U
6、修改SYSTEM文件0 j6 Q$ X% j- @ p& H: ?) M. o! f
因为原子的SYSTEM文件夹是针对UCOS编写的,所以要进行对应的修改5 a" J8 o4 }9 l5 r8 |$ l# m
3 O7 p3 t G, c H6.1 修改sys.h文件9 F _* n" ?/ B
把宏定义改为1即可,要支持OS,UCOS也一样的
~! }6 }! c. u# q1 z5 C
3 {+ F( Z+ o* o9 m3 Y1 ^, P
* F3 U# J1 J. I; u8 Y) D7 p( ?
& I6 c2 o7 X' b8 P- |- }, V6.2 修改usart.c文件
+ Z3 i+ j$ m r t, {$ Q修改头文件为- e! s" Q6 y O% I8 q
: c: G- ^& @* h. n0 y2 R+ t: F s
( c1 d7 n. m0 e2 d- E2 ^+ v# T
. |. o8 Z. ]4 f) |1 e# V修改串口中断服务函数为:
* R; Q d& U* K" y1 e; r& n6 B
+ M( r6 l, E2 A
; n* a L/ Q8 V) v6 w6.3 修改delay.c文件
9 I: k5 f& k4 u( \同样先修改宏定义的头文件0 A0 b# ]; p) c( j; P7 |" z. U
+ n2 n4 ] [, W P' j. B
3 o; G) a( D( V- R, r" j; {2 V
/ z _ E; g) R+ O' y) @接着修改systick中断服务函数为0 C# F- p) ~' X
( U1 X5 [' c/ Y+ o: J! G: B
2 {0 X5 T: o3 \9 `; j3 O- x0 U/ N
8 G- Q7 `% f, C: ^2 N; @5 ~在滴答定时器中断服务函数中调用FreeRTOS的API函数xPortSysTickHandler();/ T) F# e" q4 ]' J; t% @
5 u: ^ B8 g7 \/ N; o$ I在修改delay_init()函数,如下:
: J( ?2 }& ?/ P/ |" F" \+ e9 Q2 m8 y, I* |/ y
" t! {9 k! S. t1 {: U# T6 m0 x
) R8 X5 s z& d5 H5 V接下来就是延时函数的修改2 Z. ~2 ?- v% I3 M0 i
' p9 M5 E- ~' _' z$ x
# j7 C& L! |2 l* { n Q6 D
9 w3 Z7 ?% c3 b9 }* f5 _
修改完后再编译一下,会出现重复定义的错误,如下图:( o* V9 E! i; X4 L, J
5 V3 T! b6 M- B" u8 A
/ g2 |4 Z6 w* q4 I% m3 _5 U/ g; g, W6 [2 q* x! U" S( J! n$ l
解决办法:屏蔽FreeRTOSConfig.h掉底部的#define xPortSysTickHandler SysTick_Handler+ }1 Y. s6 ^+ w# S+ I e, j! E
4 Z ]6 `! P5 o1 q, O2 x) D8 a
! K$ F( U9 h" |
+ g( h6 n; M6 V/ o0 S6 @+ _. Q+ U7、修改main.c进行功能验证( ?5 P" @1 X7 l# J$ j+ n
主函数主要是实现实时系统多任务的创建,具体如下+ f4 A9 Q R: W' v/ |7 _
$ U0 D, c& b+ u. O" G. E) H- #include "sys.h"
& P. U1 E6 k' E) ?" K5 B) Y - #include "delay.h"$ ~9 H; e! K7 D6 r
- #include "usart.h", C+ J* E: Y8 g: D z/ d! ]
- #include "led.h"( y$ c) x8 E$ j" [7 h
- #include "FreeRTOS.h"
, N! n0 |" [0 \, _0 d! ` - #include "task.h"
1 r$ l! b; r9 P" \) Q
* f& G D* t4 M, H0 K" G" I0 Z6 s- #define START_TASK_PRIO 1$ x8 a$ [3 b* o3 p! ~0 }4 c+ s. `
- #define START_STK_SIZE 120
$ z) z/ p. c* O8 {/ ~ - void start_task(void * pvParameters); //任务函数. C4 J( ]0 z( Y
- TaskHandle_t StartTask_Handler; //任务句柄
; M$ @' h2 T& c - / C, ?" g& \0 F5 c, E" J
8 F4 {6 P; W ~/ ^- #define TASK1_TASK_PRIO 2
% {( j8 t0 u' s - #define TASK1_STK_SIZE 120# F& m9 |- \2 l3 C% Z
- void task1_task(void * pvParameters);- {( e7 R* g. o& d4 g3 w6 p
- TaskHandle_t Task1Task_Handler; //任务句柄
5 b5 K; y' e- C- N
1 H4 Y5 ]3 Q+ Q. D, h- #define TASK2_TASK_PRIO 3- v: u& l R( l! v
- #define TASK2_STK_SIZE 120 8 b+ A9 g" [* Z. U- u. \: T3 J) p0 }
- void task2_task(void * pvParameters);; h2 |4 N* _9 |0 ^. {' ?2 i- K6 N
- TaskHandle_t Task2Task_Handler; //任务句柄 - r$ {: [9 s+ m+ a7 X
- : F8 i: o* {/ V% g) }; Q; W
- int main(void)
- _+ `! w/ [. g - {) t0 d9 z, ?: x0 C7 R6 i ~
- NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
, i2 w$ J3 x' g& v' J0 H6 v - delay_init(168); //初始化延时函数 v* c5 a& u( s; R0 c0 R
- LED_Init(); //初始化LED
# p# e9 F5 m9 u) m6 J5 B9 D - uart_init(115200); //初始化串口) @0 Z+ [1 N, d% T% `
8 y8 R7 Q) e# l1 y. a- xTaskCreate((TaskFunction_t ) start_task,
- ?3 _2 A; p* u. \! B5 N7 V. g% T- I - (char* ) "start_task",
- [' X# L+ e! O. o9 M - (uint16_t ) START_STK_SIZE,
4 B! O: Q, G9 l# q9 V" U. [/ _ - (void * ) NULL,5 y& c6 Q* N! D& R, W0 P
- (UBaseType_t ) START_TASK_PRIO,
" W( Z$ ^. l& H* u' T - (TaskHandle_t* ) &StartTask_Handler);6 m: x" o" |9 p |
- vTaskStartScheduler(); //开启任务调度- m. w2 C" w# ~) w; V* i
- }
4 @ r8 j$ Q- J - . H9 [- p2 _) Y; S
- void start_task(void * pvParameters)
% D) J% U( p) M - {
4 m- R& y, K: K - //创建Task1( s2 H7 U: e* |6 Z5 n# [9 h
- xTaskCreate((TaskFunction_t ) task1_task,( b) [* ?$ v$ W" {* p" ?# b, e
- (char* ) "task1_task",
9 P. Z% h2 O1 T5 g. ^" H$ Z - (uint16_t ) TASK1_STK_SIZE,
8 H- r& g! h% } x# }0 M+ ? - (void * ) NULL,
2 ]$ y3 b, Q9 T - (UBaseType_t ) TASK1_TASK_PRIO,
; W% u: e$ x8 q2 a- z - (TaskHandle_t* ) &Task1Task_Handler);
8 o7 r a8 w& l+ K+ q8 }! t - ; n% q8 f" D3 \/ G9 |6 ? h8 m
- //创建Task2, o" L# ]" n# |0 _' R" j/ f
- xTaskCreate((TaskFunction_t ) task2_task,% N- A! e4 x1 [1 o# Y. ^
- (char* ) "task2_task",6 r# L, |1 g; D
- (uint16_t ) TASK1_STK_SIZE,/ \& `, X% u4 ~0 K
- (void * ) NULL,# ^ i _1 L1 t
- (UBaseType_t ) TASK2_TASK_PRIO,
' S; x' H3 a& a) F - (TaskHandle_t* ) &Task2Task_Handler);" Y2 \" L d/ X" _
- vTaskDelete(StartTask_Handler); //NULL
5 s. d, H$ a( g1 Q, H) v8 A$ ` - }$ G; t8 z; l# p- ^
- 3 z& p' e4 [" i
- void task1_task(void * pvParameters)
* G+ a7 q4 p" e ?2 u/ |! I& B! Y - {
6 X( I: i% Z. `, v - char task1_num=0;6 n" W. P2 D: ~8 o( p) P$ h( ?. ?" P. i
- & ]5 a( S% o+ E* U6 I
- while(1)) T% ~8 @1 V' q: }( B" `9 I$ }
- {" i1 H& _* E! Y- @/ D
- task1_num++;
2 x4 A2 l7 h5 I, g( q/ N+ v0 U - 4 a1 T# s o) m2 ~
- LED0 = ~LED0;7 D% w7 }5 ^# L3 Q1 W6 Z
- printf("Task1 Runing %d !\r\n",task1_num);
- w" ]( h& H* j8 I6 f# ^ - 4 E( J$ c: ^9 V% Z4 t$ x: y5 U
- vTaskDelay(1000);. n7 d5 h' T$ g
- }- U! G, K: c0 A2 F9 R" ]
- }4 K6 q* A! M1 E' ]/ f% v: X
' c$ V% e4 e- M# t9 m- void task2_task(void * pvParameters)) b. b+ d8 E3 C i9 O+ ~
- {
0 `9 p; p; M: }+ J' T+ i0 a z0 ~- } - char task2_num=0;
' ~. W d' k. N2 z0 N - while(1)
& Z, b- Z1 H z6 O - {
( K& ?1 ]3 ?! ^6 Z) d - task2_num++;3 L8 t3 }( p+ \5 M4 J4 y4 P
-
1 m$ K6 R: _) u1 Q - LED1 = ~LED1;
% X Q1 Z" }' Z$ k* Y/ P" Q - printf("Task2 Runing %d!\r\n",task2_num);% I% F* Z9 _7 l) S' q( }
- vTaskDelay(500);6 X4 v5 f# h! P7 t, G8 @
- }
1 P/ |+ V/ r+ L; I% L. _4 `0 r2 R \ - }
复制代码 g- o: D4 Q0 f8 o5 g, c3 z
实验现象;
- {, R6 h9 \2 V- c- q
! B9 S6 h0 j' v# c; y4 ^2 p 开发板上LED0和LED1进行不同状态的闪烁,串口也会打印任务执行次数,很明显任务二的速度是任务一的两倍。
2 m7 Y! w1 b; D4 G- ?; K( a
. a- ?) j8 C- W' m
9 c/ u) K# a1 a6 g3 s. n; D
4 C& ^; y5 z8 l, K
- g/ h$ ` w+ _& ?3 b& {
$ G& Q) @3 R2 t! p' D" O9 i4 o我这里使用的是STM32F407VET6,原理都差不多,用那块板子都一样。至此,FreeRTOS移植和任务创建成功。
( K! w7 T" h D- W9 b
( R7 l4 w+ Q- ?9 i% J& D: p! p5 ~! i
; b- a- g# `3 l! F* [' D! @! A, R1 j7 B* I! x$ g
|