1. 简介
, e) }0 J; O8 K( ^- m, T1 F4 E 我们已经了解了FreeRTOS,接下来我们就把FreeRTOS移植到我们所使用的平台上,我们使用的板子是STM32F407。
- H, U% O0 H+ \' j0 f2. 预备工作0 ?2 ]0 \' g# `& ]3 W. s* ^' d
要移植FreeRTOS,肯定需要一个基础工程,基础工程越简单越好,我们就用之前的跑马灯实验作为基础工程。
' y9 j6 @0 s' N- h! d
6 W4 C5 @* Q! g1 p/ z3. FreeRTOS移植
; k8 b/ [4 c+ D- Q3.1 创建FreeRTOS文件夹1 }4 j# g, y( _; t4 S8 T- j
/ E5 c+ }9 d: } H" ~
9 a( O ~( P* I3 p. b
& ^; N- S& R& Y* u1 z3.2 将FreeRTOS的源码添加到FreeRTOS目录中# Z- V+ p9 I& L5 B6 c4 P0 L
, ~. i8 l3 V& O$ D" t! V9 B% e/ W
FreeRTOSv10.4.1\FreeRTOS\Source
' H7 D" e; z" O( t1 L$ ?; J2 J$ {8 `1 s$ q' [1 X% N A
. d2 `! c- T! z% M& m: t1 K7 G8 e( H; S$ d
3.3 删除portable文件夹中相关的文件和目录
( i" I+ ` B: N7 D1 d0 k! f2 u
5 ]* d% C# F9 H, J+ ?我们只需要留下keil、MemMang和RVDS这三个文件夹,其余的都可以删除的。
6 P2 v2 B' ?' N4 E$ c- b. Q$ ^: p! J, J2 M7 i# l
% T4 n* Y& t$ x- ~) `+ \4 ]$ V
: F( x: K2 ?! b9 X( M% ^" n6 d3.4 向工程分组中添加文件+ v- M% M5 B1 z, r6 G
$ ^; ~" ]+ [4 J, O7 F4 }2 M7 }
打开基础工程,新建分组FreeRTOS_CORE和FreeRTOS_PORTABLE,然后向这两个分组中添加文件。
2 m/ T/ t1 V2 {/ y3 E+ D7 _
4 `, t/ \" y1 |8 C
$ l1 a3 O; ]# ^! ]5 z. O; P, f u7 j
FreeRTOS_CORE中的文件就在FreeRTOS目录中。FreeRTOS_PORTABLE分组中的port.c和heap_4.c是怎么来的呢?port.c是RVDS文件夹下的ARM_CM4F中的文件,因为STM32F407是Cortex-M4内核并且带有FPU,因此要选择ARM_CM4F中的port.c文件。heap_4.c是MemMang文件夹中的,里面有5个c文件:heap_1.c,heap_2.c,heap_3.c,heap_4.c,heap_5.c。这5个c文件是五种不同的内存管理方法。6 h3 S8 l! H) B0 v
- `8 L H0 \$ W, a9 p/ v0 j0 G这5个文件都可以用来作为FreeRTOS的内存管理文件,只是它们的实现原理不同,各有利弊。后面我们再来讨论FreeRTOS的内存管理。
6 N6 U0 l! ^! s! ]. }' h- _0 E, g1 }2 Q s. M
3.5 添加响应的头文件路径
9 s/ Q0 V' p* S' r2 r0 M; g! ]
$ N) `( O! u( m0 v
I) q1 V4 m N1 j) ?) P; R$ Q' I8 s
# e6 P/ O% G& ?$ S3.6 找不到头文件的错误
$ Y5 H; i4 P7 P7 z8 t9 ~- ..\FreeRTOS\croutine.c(27): error: #5: cannot open source input file "FreeRTOS.h": No such file or directory
复制代码 1 w) V% G4 e7 l$ j' l
解决办法; b8 s7 w$ l; ?" E% o" _# u
9 J% H5 A* X% `FreeRTOS.h头文件在FreeRTOS\include目录中。! D/ s3 }$ j8 w4 [6 n
* q. F" ~7 k+ B6 \
6 e I; y' t8 q$ k% ^8 }. Q" M
; F5 A7 |) Z0 x2 n9 t- ?$ P
再次编译发现又有头文件找不到FreeRTOSConfig.h文件
# a/ K) n/ [- p5 Y8 w2 U7 a
0 C' g9 q) \+ i7 p5 h- ..\FreeRTOS\include\FreeRTOS.h(57): error: #5: cannot open source input file "FreeRTOSConfig.h": No such file or directory
复制代码 ) w5 m' S3 B, ]4 i7 f9 o
在项目文件中搜索该头文件发现找不到,这个时候我们可以找找FreeRTOS的官方移植工程汇总会不会有这个文件,打开FreeRTOS针对STM32F407的移植工程文件,文件夹是H:\02_stm32\其它资料\FreeRTOS\FreeRTOSv10.4.1\FreeRTOS\Demo\CORTEX_M4F_STM32F407ZG-SK。7 [5 ]% t& K2 k/ @% h( A
8 ? B& Q* D! b# d2 ~% V6 m
6 U9 S0 r X$ K6 `2 Y$ \- [+ O
% u' j5 O& W: r4 m2 o* G+ Y% L找到该文件之后,我们直接将它复制到FreeRTOS源码中的include文件夹下。FreeRTOSConfig.h文件是配置文件,一般的操作系统都有裁剪、配置功能,而这些裁剪及配置都是通过一个文件来完成的,基本都是通过宏定义来完成对系统的配置和裁剪的。
+ n+ ^; E/ A' ~' i: J' X7 ?( d0 r s0 \9 ~, n2 Z( N7 H
3.7 SystemCoreClock没有定义的错误
# T2 Z u+ ^' s; k9 B) [; k. m' J8 K: E* x
- ..\FreeRTOS\portable\RVDS\ARM_CM4F\port.c(732): error: #20: identifier "SystemCoreClock" is undefined
复制代码 SystemCoreClock未定义,因为在FreeRTOSConfig.h中使用了SystemCoreClock来标记MCU的频率。6 Y; u: b0 | k8 c/ e! M
+ ?! Z: b9 V1 D; w( L' C
2 r! f$ z: ?) p" D
k2 j, M; {# _6 d4 e这是一个条件编译,只有定义了__ICCARM__以后下边的代码才有效。% n2 v' m$ S- n
" F0 w# k1 y7 r- /* Ensure stdint is only used by the compiler, and not the assembler. */3 s$ Y& g0 n; J. O% V
- #ifdef __ICCARM__8 }* |& Q) \* p9 c9 m# V8 \
- #include <stdint.h>
p2 p0 {" Z$ ^ - extern uint32_t SystemCoreClock;' h2 B6 V0 {# l( w) |1 x% ~5 ~9 e
- #endif
复制代码 5 g# S0 J1 Q- [. ?/ l1 ^
修改这个条件编译,修改后的代码如下:
4 r5 `7 F& N& T9 T$ |- /* Ensure stdint is only used by the compiler, and not the assembler. */
9 p# H' I9 W3 E" L: u8 d8 z3 L( Z - #if defined(__ICCARM__) || defined(__CC_ARM) || defined(__GNUC__). n& k! @8 I$ D/ f1 ^
- #include <stdint.h>
# r5 l7 `; d2 J - extern uint32_t SystemCoreClock;: U1 k2 s$ f& p- G. j, Q
- #endif
复制代码 $ S; g/ g0 `- g' f E# M
3.8 重复定义的错误
) b" K! V; j. E, t1 x( p5 v: x/ K2 G: {
- ..\OBJ\01Test.axf: Error: L6200E: Symbol PendSV_Handler multiply defined (by port.o and stm32f4xx_it.o).6 m, z# {0 K" B3 M; }
- ..\OBJ\01Test.axf: Error: L6200E: Symbol SVC_Handler multiply defined (by port.o and stm32f4xx_it.o).
3 p0 V4 F0 n2 v/ u5 `8 a0 Q - ..\OBJ\01Test.axf: Error: L6200E: Symbol SysTick_Handler multiply defined (by port.o and stm32f4xx_it.o).
复制代码 7 W! l& t# M# c) }
port.c和stm32f4xx_it.c两个文件中有重复定义的函数:PendSV_Handler(), SVC_Handler(), SysTick_Handler()。屏蔽掉stm32f4xx_it.c中的PendSV_Handler(), SVC_Handler(), SysTick_Handler()三个函数。再次编译,发现如下未定义错误。' q! M2 k8 P" x0 U& D8 f
! i, O4 Y, `3 m! n7 i
3.9 未定义错误) t2 X+ V& y, z0 Y/ }- r! u
+ e( g/ U- E- c$ |
- Build started: Project: 01Test
. J/ n; E4 l# w% F5 |8 M$ p - *** Using Compiler 'V5.06 update 6 (build 750)', folder: 'D:\Keil_v5\ARM\ARMCC\Bin'
% u2 \2 K0 E% \6 k9 ]; X! v+ Y - Build target '01Test'6 W7 z% S$ f5 t
- compiling stm32f4xx_it.c...
1 @2 l5 @: O4 N$ f2 I' F5 l - linking...
9 N6 S6 j) L; J - ..\OBJ\01Test.axf: Error: L6218E: Undefined symbol vApplicationIdleHook (referred from tasks.o).
: ~4 p5 W* f8 M3 A: r& Q# Z - ..\OBJ\01Test.axf: Error: L6218E: Undefined symbol vApplicationStackOverflowHook (referred from tasks.o).0 K, t' |" V( O3 O) _+ {
- ..\OBJ\01Test.axf: Error: L6218E: Undefined symbol vApplicationTickHook (referred from tasks.o).
& \( r) t2 t b! \4 X+ P0 x - ..\OBJ\01Test.axf: Error: L6218E: Undefined symbol vApplicationMallocFailedHook (referred from heap_4.o).
+ j" W& ~3 A7 b3 H0 K! n9 _ - Not enough information to list image symbols.
7 P9 f, S2 X& N* C - Not enough information to list load addresses in the image map.
1 @# M+ ]/ x+ D$ a - Finished: 2 information, 0 warning and 4 error messages.
, K5 A7 i t4 L( ` [& A8 K+ ? - "..\OBJ\01Test.axf" - 4 Error(s), 0 Warning(s).
4 l0 b- Q2 E Z2 q - Target not created." w" V& _: O* Q1 I& X. }. F: c! Q% u
- Build Time Elapsed: 00:00:051 e1 a1 Z9 \* _; M7 N
复制代码 / j9 |9 Q' h0 v- M1 y6 U- C
发现这些未定义的函数,它们都是Hook结尾的,这些函数有个共同的名称:钩子函数,这是因为在FreeRTOSConfig.h中开启了这些钩子函数,但是没有定义这些钩子函数导致的。我们在FreeRTOSConfig.h中关闭这些钩子函数就行了,关闭的方法很简单,将相应的宏定义改为0即可。这里将宏configUSE_IDLE_HOOK、configUSE_TICK_HOOK、configUSE_MALLOC_FAILED_HOOK和configCHECK_FOR_STACK_OVERFLOW定义为0。
0 w6 j8 o* u* p2 t* q8 S, X* E3 [) [, z. s
最后编译一下,应该就没有错误了,如果还有错误的话大家自行根据错误类型查找和修改错误。% O; i& I, E1 J5 X4 f( d& K6 L
) M& ~1 I5 X% y9 H# F0 I: y" `4. SYSTEM文件修改; a- z2 @- p/ z0 M
4.1 修改sys.h文件
& f! F% w$ M/ q7 c: ]3 S, F使用FreeRTOS,我们应该将宏SYSTEM_SUPPORT_OS改为1。- //0,不支持ucos
, E1 o$ y5 m; V% ? w8 w( A! A$ Z' \ - //1,支持ucos! s8 [, O: x' M" A
- #define SYSTEM_SUPPORT_OS 1 //定义系统文件夹是否支持OS
复制代码 ' c' T) W% C$ [0 ]' J- _
4.2 修改usart.c文件
' [6 N! _% {0 v1 \ i' gusart.c文件修改也很简单,usart.c文件有两部分要修改,一个是添加FreeRTOS.h头文件,默认是添加的UCOS中的includes.h头文件。修改之后内容如下:
9 Y# X/ ~$ b" f2 g3 t- //如果使用ucos,则包括下面的头文件即可.! C" Q$ w9 Y; |
- #if SYSTEM_SUPPORT_OS4 v m4 X( b5 l; ?# j- H8 \
- #include "FreeRTOS.h" //os使用
4 }2 D p( I! [6 j! K4 d+ L& k - #endif8 V2 _& m' ^- I) S# E, a
复制代码
6 i4 a3 E) w* _4 m% }- I. f修改USART1中的中断服务函数,在使用UCOS的时候需要进出中断的时候添加OSIntEnter()和OSIntExit()。使用FreeRTOS的话就不需要了,所以将这两行代码删除即可。; m. U$ ?: f0 D, g" H
: W* F$ G6 c. r( k- void USART1_IRQHandler(void) //串口1中断服务程序
# g4 {0 b* @7 k; y7 \ - {3 B7 o# m% t! h- H! y" |4 a' @
- u8 Res;
; u( w( w# R3 b
, {7 M7 s3 |% A3 [- if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //接收中断(接收到的数据必须是0x0d 0x0a结尾) [/ a3 U& h& ^ H
- {
2 E( d0 F; v4 s& @' T/ @ - Res =USART_ReceiveData(USART1);//(USART1->DR); //读取接收到的数据- u! ?$ d! ]3 f$ T7 M
- + T1 T* A2 }" m) G/ {
- if((USART_RX_STA&0x8000)==0)//接收未完成, v2 \% L$ ^" [0 D" z. J
- {+ t9 `0 T/ v1 k @5 |+ n+ C9 x
- if(USART_RX_STA&0x4000)//接收到了0x0d0 J2 g/ ^( ?& p" q0 n/ ?/ P
- {
. t$ o4 ^! j# e ?' I( C - if(Res!=0x0a)USART_RX_STA=0;//接收错误,重新开始1 S) Y6 j' u$ t a
- else USART_RX_STA|=0x8000; //接收完成了 : b% d. B- T" {3 q6 i
- }: h0 |5 X5 F- C( q! s Q
- else //还没收到0X0D0 z% @: ~' [. y' { v
- {1 T+ ~# b( y: j
- if(Res==0x0d)USART_RX_STA|=0x4000;
" S6 S! I% l; q3 H/ s - else
2 Y/ X2 i4 E2 M+ [. G - {
/ o3 z0 v; Q$ H+ {. y) U - USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ;
; v! b! \" s' y1 f, ~- o - USART_RX_STA++;
6 T0 v$ h, b* W5 d: e; u+ z - if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收数据错误,重新开始接收 2 Q, E6 U5 ]1 z7 J
- }
" F/ [& z- V4 S! x/ ^& F+ U2 X" D7 Z - }
$ T# H2 _/ P' V& N7 W/ t - } - K. W L9 H$ B0 J# P
- }
' s. B9 J) f# v w - }
复制代码 2 \& I N7 M C( p$ M% e
4.3 修改delay.c文件
( k+ H& @ f0 x0 q N1 t0 J
! q0 B K6 i$ i: Fdelay.c文件修改的就比较大了,因为涉及到FreeRTOS的系统时钟,delay.c文件里面有4个函数。
# f% X" h* C4 d; \) e. A& Y1 l
( Z( r: j: |5 u3 K' G首先来看看SysTick_Handler(),次函数是滴答定时器的中断服务函数,代码如下:
6 M5 g1 j, w% f; D% K* d6 n' ?1 ?# U9 |( r
- extern void xPortSysTickHandler(void);
. N4 l+ D4 `* a' B1 g
y$ z, A" [/ l8 a; y/ {- //systick中断服务函数,使用OS时用到
7 {( J0 C! J* ^; N: s3 {6 o - void SysTick_Handler(void)
' u: Z5 p" B$ p$ Z* r - {
7 j; R; t6 s0 _ - if(xTaskGetSchedulerState()!=taskSCHEDULER_NOT_STARTED)//系统已经运行
7 }- U/ b) i6 B1 x9 z% _6 o - {
0 f+ `- J( |. E" D- P - xPortSysTickHandler();- z& R6 }% O+ I9 Z- m
- }
8 K# ~7 d' w' s: j! F, P - }
复制代码 : z2 z* O7 ]1 p/ P" \ l0 m5 g
delay_init()是用来初始化滴答定时器和延时函数,代码如下:/ |& Q& y1 |; A0 C' T! {
5 A9 }5 p: r; m
- //初始化延迟函数1 K# a6 {# F. V' a G, @
- //SYSTICK的时钟固定为AHB时钟,基础例程里面SYSTICK时钟频率为AHB/8
/ S; I* e9 u' n; \! S$ o - //这里为了兼容FreeRTOS,所以将SYSTICK的时钟频率改为AHB的频率!
5 {. H* c( R9 M$ s. H# H$ J6 F. _ - //SYSCLK:系统时钟频率6 f) h7 Y% p# K4 M* W5 @. v0 [
- void delay_init(u8 SYSCLK)) h3 Q% \( A. x7 j& |! y* ~
- {
6 B" W# M) g+ H7 K2 `3 ?" S5 u$ [ - u32 reload;
- @$ P% Y3 r3 t; p$ | - SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK);
; ~( d0 O$ u) ^$ B! D - fac_us=SYSCLK; //不论是否使用OS,fac_us都需要使用
7 X4 C9 M; F! d! d/ d - reload=SYSCLK; //每秒钟的计数次数 单位为M
5 {, S0 P, b* h- I& C. {, M - reload*=1000000/configTICK_RATE_HZ; //根据configTICK_RATE_HZ设定溢出时间
, l* K8 F0 j9 i5 J* P2 J- w - //reload为24位寄存器,最大值:16777216,在168M下,约合0.0998s左右
2 L- L/ r& J* p3 ^& c - fac_ms=1000/configTICK_RATE_HZ; //代表OS可以延时的最少单位/ S* i, W1 q4 T2 k3 J1 i
- SysTick->CTRL|=SysTick_CTRL_TICKINT_Msk;//开启SYSTICK中断
9 [7 j4 y9 R% g/ }' F# U7 `' H. @: j) c - SysTick->LOAD=reload; //每1/configTICK_RATE_HZ断一次
" m. s& h) v: M7 ?0 \" R' q - SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk; //开启SYSTICK
# u8 [. g+ K" h- a - }
复制代码
# A& x& ]8 b. k) A$ Z延时函数定义如下:
& h0 r5 k+ w+ s8 G& u' q( o* _# F+ k1 b, U
- //延时nus( e/ X+ H7 E/ }8 m; ?
- //nus:要延时的us数.
0 q% X( s* B G" |" u! u: P - //nus:0~204522252(最大值即2^32/fac_us@fac_us=168)
9 N4 v! x0 h' E& w3 N' l - void delay_us(u32 nus)& ]; ]. x( _2 ?
- {
$ f2 \0 I+ P" |1 j; E - u32 ticks; T) w& J* B/ Y1 [
- u32 told,tnow,tcnt=0;
( r0 @$ o6 P" S9 g4 T; ~ - u32 reload=SysTick->LOAD; //LOAD的值
4 G6 w+ q- e' Z% B1 e4 c. A - ticks=nus*fac_us; //需要的节拍数6 U+ W$ D7 l9 E, ^3 j6 o8 y
- told=SysTick->VAL; //刚进入时的计数器值
5 U/ ]) E/ [: I' l - while(1) o% b% @9 Z% ]; w6 S# f* \+ d+ O
- {+ [+ }8 F3 D+ \0 b
- tnow=SysTick->VAL;6 z0 d9 ]0 l- k& S4 R0 @
- if(tnow!=told). i& c" X& s2 l
- {
* \& V$ G# N/ K. l' b3 l0 e - if(tnow<told)tcnt+=told-tnow; //这里注意一下SYSTICK是一个递减的计数器就可以了.. M4 d! K' t1 U& m
- else tcnt+=reload-tnow+told;
" ~9 B9 }/ G7 i3 O - told=tnow;
7 a& [7 t- }2 e" D2 K9 k$ G/ h - if(tcnt>=ticks)break; //时间超过/等于要延迟的时间,则退出.
- ]1 o0 K+ T" b3 C# C - }
$ {" I [9 @7 r5 F! V' |7 { - };9 y/ i8 T5 [8 l* h0 P U- ^
- }: ]9 i8 e1 v( e& M4 A. P# M
- //延时nms/ k; I! E: ?4 s- e9 h: G
- //nms:要延时的ms数9 @) [. z2 l& I# Y( Q5 q+ U4 p; U
- //nms:0~65535
& a8 @5 }7 b4 F Y K; ? - void delay_ms(u32 nms)$ ]" U' M) L0 q5 S& ~$ I
- {+ L K/ o& N' g$ n) v, L
- if(xTaskGetSchedulerState()!=taskSCHEDULER_NOT_STARTED)//系统已经运行2 j4 V' E6 f1 w" I! R$ @
- {
! [9 ]6 ^. ]3 {- f8 _ - if(nms>=fac_ms) //延时的时间大于OS的最少时间周期2 y7 p, r. c& x/ k" h: Y- V
- {
: U% ^7 Y9 x, Z& T% ?. u4 r - vTaskDelay(nms/fac_ms); //FreeRTOS延时
, c- W, ^" X) a* E( p, H; g - }
: K- F+ t2 `9 ?, B1 a6 a - nms%=fac_ms; //OS已经无法提供这么小的延时了,采用普通方式延时7 r0 u* A1 D7 M( g2 i! g
- }' W9 n1 ?7 g" Z! ^3 Q2 Z
- delay_us((u32)(nms*1000)); //普通方式延时
- |8 x: r! h- @! `, l - }
' Y3 V5 x O5 H( {8 j, k: m
& J8 \, H" i: T: ]3 q1 s9 l7 H- //延时nms,不会引起任务调度: U' Z: k3 B! A0 ]) O6 x7 P
- //nms:要延时的ms数6 c* Z% Z6 p* z K/ Q
- void delay_xms(u32 nms)( v* S+ C# q. V
- {
$ C4 A' b* Z$ W9 k: ]8 y8 `+ Y - u32 i;3 l6 p0 I1 N8 G
- for(i=0;i<nms;i++) delay_us(1000);" [" `( P. ^1 e5 h" z
- }
复制代码 0 |- {4 O- _1 G+ R0 I
编译之后出现如下错误
0 f$ S+ v% F- _% V2 o2 M: i
' M0 d8 k5 [! u5 b0 E8 B- ..\OBJ\01Test.axf: Error: L6200E: Symbol SysTick_Handler multiply defined (by port.o and delay.o).
复制代码 6 h' M" O1 v- \9 H
在port.c和delay.c中有重复定义的函数,SysTick_Handler只能二选一,很明显delay.c中的SysTick_Handler()保留下来,打开FreeRTOSConfig.h文件,找到如下宏定义:
% f! `" e. i4 d- #define xPortSysTickHandler SysTick_Handler
复制代码
7 l& ^% T+ j0 ~0 a# m( l4 p$ X注释上面的宏定义。
, h: U/ K* w J* F6 M) G3 s/ c- k8 L$ K/ B. v. ?
5. 测试程序设计
7 z5 |$ x" l2 r8 U main.c文件
2 e9 I( _9 H! W% p+ c5 ^; W: A( R5 U3 G$ T R1 [3 K
- #include "sys.h"
# l4 o( L8 i: `$ J# y# g1 ` - #include "delay.h"* c" u* }, w% ~2 C
- #include "usart.h"
# O1 E! H6 s W/ g$ F - #include "led.h"+ G% n3 o5 l7 f& {2 g
- #include "FreeRTOS.h"
6 ^: k/ Q7 C4 l. j! g - #include "task.h"0 l+ p- b. ?1 }+ E, N2 H% C9 E& S
- ! S' U8 N4 k2 p# `) y) A
- //任务优先级
: G4 P( d+ i, u# } - #define START_TASK_PRIO 16 |4 w- A! T6 F% @) H; Z- q
- //任务堆栈大小
, L# b) W! t& ?" G) p% F - #define START_STK_SIZE 128/ i8 `8 d4 _' @0 L. Z; u7 A+ w
- //任务句柄
3 t7 ~ l" [$ v# G - TaskHandle_t StartTask_Handler;6 U- E8 @0 ~% P" E
- //任务函数
& Q" v9 E- h6 S3 A. e" e - void start_task(void *pvParameters);, u, L* L; ]) ~2 {
- F2 ]* z, {$ ?# {" a* n- //任务优先级+ p4 P& ~1 e/ h! p
- #define LED0_TASK_PRIO 2
- o c+ q0 v8 a4 l5 l0 u - //任务堆栈大小
. ?! T1 e8 @. v# E0 `! _6 Q - #define LED0_STK_SIZE 502 x0 I' I2 |) K$ [9 S- {3 L
- //任务句柄
3 }! U* h7 L2 K - TaskHandle_t LED0Task_Handler;6 V7 i* D- L. G0 |
- //任务函数# k9 r4 {4 P4 o& z4 x2 ^
- void led0_task(void *pvParameters);% H j( E. k/ M* q) f- K
- 5 I: Z# D4 Z8 m
- //任务优先级
) ]6 L6 x6 e4 L- s/ U8 p. u' A5 D9 j - #define LED1_TASK_PRIO 3
/ w, M9 X9 }' m; o+ M' k - //任务堆栈大小1 z5 w& _7 a2 o) G# U
- #define LED1_STK_SIZE 502 `7 R7 x4 C. o1 q' _! Z
- //任务句柄( {) o V/ q% |5 g% Z
- TaskHandle_t LED1Task_Handler;
) e6 r6 u9 ?' d! Y - //任务函数" K o$ Y4 X/ c5 E9 Z- e* M
- void led1_task(void *pvParameters);
& E! ~6 u# f& ~, G/ w }4 C: G - : G; ^+ e8 m% n2 G5 y% q& a
- //任务优先级
4 I4 a7 I. M3 P. z - #define FLOAT_TASK_PRIO 4" t' B3 m8 g0 A! J2 o
- //任务堆栈大小6 n+ ^- R& y2 b0 I8 o
- #define FLOAT_STK_SIZE 128! j% }0 D; y1 H S1 B& K+ P" m
- //任务句柄# U$ d4 s# h% b: V5 N% L8 c- g$ \
- TaskHandle_t FLOATTask_Handler;
5 G$ O$ i& b( c; e - //任务函数
8 x6 x+ g4 g& n. t- o - void float_task(void *pvParameters);' n5 s& q( s8 N7 Z f0 l. ]
' M: `- N/ T7 |1 S- int main(void)* H( }' R% f7 r/ A2 |* {
- {
! `, _" z, {; B- V, O7 | - NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4+ w/ _, b/ a# a4 N& w3 d
- delay_init(168); //初始化延时函数) r H# }+ P: Y# }# ]/ l. s
- uart_init(115200); //初始化串口
2 |8 |6 J! j/ Q; l% X" k/ a - LED_Init(); //初始化LED端口
( X1 d9 H% @5 D" y- C& h( v) f" Q- s/ T
8 U( ], b* U; A$ u- //创建开始任务
" K5 a7 H9 k6 Y# l" F - xTaskCreate((TaskFunction_t )start_task, //任务函数
+ w' k7 k. a" d - (const char* )"start_task", //任务名称
% {" |* P7 d( m5 u2 M: ? - (uint16_t )START_STK_SIZE, //任务堆栈大小1 t+ F( Y. x d$ R
- (void* )NULL, //传递给任务函数的参数
: u8 w$ l6 b6 l& z* M - (UBaseType_t )START_TASK_PRIO, //任务优先级5 H( m' A: m, e4 u; u& F) k
- (TaskHandle_t* )&StartTask_Handler); //任务句柄 0 M! h/ j! _* m% P0 R+ v9 Q
- vTaskStartScheduler(); //开启任务调度
) J: Y0 B" m! @$ z% N% | - }
4 B3 a6 w) w2 l- m! h S
) y/ N: ^/ O8 X$ W+ Q; k- //开始任务任务函数
5 |# x3 z$ M/ t - void start_task(void *pvParameters)
+ [: Y3 U( P8 S* \ - {
! w9 G4 A/ C- D, t) Z! q - taskENTER_CRITICAL(); //进入临界区
) m2 P* F6 n3 }% j; s8 h' ^ - //创建LED0任务
' k" q( v1 U% B* J. r& v - xTaskCreate((TaskFunction_t )led0_task,
! \0 c: u; m- K( k- N: V6 z- T - (const char* )"led0_task",
3 j6 L# A6 N7 @3 ` {# _. L - (uint16_t )LED0_STK_SIZE,: ]) C. |( T- x3 r
- (void* )NULL,
. {7 n' v, u; m: G+ H7 P - (UBaseType_t )LED0_TASK_PRIO,: O2 C! p" |5 `* |" G6 h
- (TaskHandle_t* )&LED0Task_Handler);
- `/ l7 \6 k) ]% K/ g - //创建LED1任务
( P: @7 ]1 X* C( t9 \ - xTaskCreate((TaskFunction_t )led1_task,4 G; N( C e( K P
- (const char* )"led1_task",
& V; N( t7 \% v2 b1 b# v - (uint16_t )LED1_STK_SIZE," _9 K. ~, Q0 E
- (void* )NULL,/ m0 i/ ?/ G% I7 V" G5 E; L
- (UBaseType_t )LED1_TASK_PRIO,
" e1 q6 ^- c# [& v# D - (TaskHandle_t* )&LED1Task_Handler);$ J4 s/ }+ O; o$ z) |1 s
- //浮点测试任务) |9 r* [4 X+ P/ c
- xTaskCreate((TaskFunction_t )float_task,
- y1 Z7 H. l6 F" S: G/ |. f - (const char* )"float_task",
4 p' }1 G8 i4 N- C3 h- u& ` - (uint16_t )FLOAT_STK_SIZE,' s6 d! x3 \: a
- (void* )NULL,
; F9 Z! @4 D! d9 c - (UBaseType_t )FLOAT_TASK_PRIO, U, _8 A4 e( o- D$ }
- (TaskHandle_t* )&FLOATTask_Handler);1 j7 n% l+ M9 p* ^, I0 {
- vTaskDelete(StartTask_Handler); //删除开始任务
3 D; W/ d9 G5 ]# \, p9 A- p$ K - taskEXIT_CRITICAL(); //退出临界区+ n2 `; S7 ~" E+ U% @/ ], R
- }+ b% B% j9 u: j8 ^+ B. N
. ?& j' D+ ^# Y5 C% h3 j' K8 W5 s- //LED0任务函数4 u7 P/ t: v9 ~. I/ Q
- void led0_task(void *pvParameters)
5 _; Y h& h2 | - {$ @- k; c; v. y7 E0 t8 W- I: y
- while(1)
' T* B% ~- g/ h% r - {; N, o; n7 _+ u1 ?" t7 K5 K
- LED0=~LED0;& j; R# F c& I" @3 A# v
- vTaskDelay(500);" v. R; Y8 a- y7 v9 p4 @7 _. ?
- }. Y S- q# O! {" a+ J
- }. I! a5 K+ j5 T0 f$ ?! p* t/ R1 I
! P+ o1 |0 U/ [3 `1 T- //LED1任务函数1 i8 `7 N6 U3 E4 J. l: f9 i
- void led1_task(void *pvParameters)0 U; |: n( j9 z) B9 t
- {
_2 ]+ u0 u& I) q! Q - while(1)/ S! q! M) @9 g! P2 U
- {% X( P- t3 S, O3 \
- LED1=0;
, \/ B5 l& f7 `; k - vTaskDelay(200);) G! w: G7 t( F. j
- LED1=1;
. ?" y! o* U# e - vTaskDelay(800);
+ R, H! x9 C, C, q, l0 E3 G$ X& a - }
$ ~+ u% X- F- O - }
9 h+ R, M0 f1 g - 7 R. |1 i R) M2 X* p$ ^7 q
- //浮点测试任务5 u" N* t+ }4 V# t% c
- void float_task(void *pvParameters)
( T, {5 g2 P2 y; [ - {# y1 S2 J5 y8 |% o: B2 \
- static float float_num=0.00;
) \$ r& K! E5 ]& d, M - while(1)+ C) W: h( f/ u/ X
- {! y4 F4 N6 M3 E
- float_num+=0.01f;$ [5 u# V2 M$ L j" i5 }5 ^& W" [
- printf("float_num的值为: %.4f\r\n",float_num);% p: Z4 v; O1 g+ o
- vTaskDelay(1000);7 ^, f: a+ k! x) p* {2 ]
- }
2 ?4 w0 }6 x+ {# V8 {7 T7 j - }0 H1 ~6 S. p- k2 g
复制代码
& A. d# F2 r8 I- j实验结果:
7 c4 D( a4 ^9 _! U+ R0 E+ z; p1 ]( n+ a: {5 M
LED0和LED1开始闪烁,LED0均匀闪烁,LED1亮的时间短,灭的时间长。然后串口不断有数据输出。+ O( P; L7 T7 K0 h1 |. ~2 q* U
# Z/ q- d+ h/ P* D
' n1 Y- U& k' ~$ `
# |+ }+ k) K5 D5 R J" G |