你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

【经验分享】FreeRTOS移植

[复制链接]
STMCU小助手 发布时间:2022-3-27 16:00
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" ~
WE_]8WG(B]SX[2SKP_@6ZQ2.png
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
@3[$A2Y(0@2GXG_P2}XV2[I.png
. 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
KW@JN5TQ]4@3D6_3AHAM)JS.png % 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 $X]UIM1K`TWXO[5Z6X8YMKB.png
$ 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 7Z6AA}Y_AUGQN9S5CQOB%SV.png   I) q1 V4 m  N1 j) ?) P; R$ Q' I8 s

# e6 P/ O% G& ?$ S3.6 找不到头文件的错误
$ Y5 H; i4 P7 P7 z8 t9 ~
  1. ..\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 \
$JH~[U5%OOT}96{]]@[EQ(V.png 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
  1. ..\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 YK}9`1[T(6EC)E}@~T~OZ)2.png
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
  1. ..\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
]ZSA_30S`DVKZEPGF{RP802.png 2 r! f$ z: ?) p" D

  k2 j, M; {# _6 d4 e这是一个条件编译,只有定义了__ICCARM__以后下边的代码才有效。% n2 v' m$ S- n

" F0 w# k1 y7 r
  1. /* Ensure stdint is only used by the compiler, and not the assembler. */3 s$ Y& g0 n; J. O% V
  2. #ifdef __ICCARM__8 }* |& Q) \* p9 c9 m# V8 \
  3. #include <stdint.h>
      p2 p0 {" Z$ ^
  4. extern uint32_t SystemCoreClock;' h2 B6 V0 {# l( w) |1 x% ~5 ~9 e
  5. #endif
复制代码
5 g# S0 J1 Q- [. ?/ l1 ^
修改这个条件编译,修改后的代码如下:
4 r5 `7 F& N& T9 T$ |
  1. /* Ensure stdint is only used by the compiler, and not the assembler. */
    9 p# H' I9 W3 E" L: u8 d8 z3 L( Z
  2. #if defined(__ICCARM__) || defined(__CC_ARM) || defined(__GNUC__). n& k! @8 I$ D/ f1 ^
  3. #include <stdint.h>
    # r5 l7 `; d2 J
  4. extern uint32_t SystemCoreClock;: U1 k2 s$ f& p- G. j, Q
  5. #endif
复制代码
$ S; g/ g0 `- g' f  E# M
3.8 重复定义的错误
) b" K! V; j. E, t1 x( p5 v: x/ K2 G: {
  1. ..\OBJ\01Test.axf: Error: L6200E: Symbol PendSV_Handler multiply defined (by port.o and stm32f4xx_it.o).6 m, z# {0 K" B3 M; }
  2. ..\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
  3. ..\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$ |
  1. Build started: Project: 01Test
    . J/ n; E4 l# w% F5 |8 M$ p
  2. *** Using Compiler 'V5.06 update 6 (build 750)', folder: 'D:\Keil_v5\ARM\ARMCC\Bin'
    % u2 \2 K0 E% \6 k9 ]; X! v+ Y
  3. Build target '01Test'6 W7 z% S$ f5 t
  4. compiling stm32f4xx_it.c...
    1 @2 l5 @: O4 N$ f2 I' F5 l
  5. linking...
    9 N6 S6 j) L; J
  6. ..\OBJ\01Test.axf: Error: L6218E: Undefined symbol vApplicationIdleHook (referred from tasks.o).
    : ~4 p5 W* f8 M3 A: r& Q# Z
  7. ..\OBJ\01Test.axf: Error: L6218E: Undefined symbol vApplicationStackOverflowHook (referred from tasks.o).0 K, t' |" V( O3 O) _+ {
  8. ..\OBJ\01Test.axf: Error: L6218E: Undefined symbol vApplicationTickHook (referred from tasks.o).
    & \( r) t2 t  b! \4 X+ P0 x
  9. ..\OBJ\01Test.axf: Error: L6218E: Undefined symbol vApplicationMallocFailedHook (referred from heap_4.o).
    + j" W& ~3 A7 b3 H0 K! n9 _
  10. Not enough information to list image symbols.
    7 P9 f, S2 X& N* C
  11. Not enough information to list load addresses in the image map.
    1 @# M+ ]/ x+ D$ a
  12. Finished: 2 information, 0 warning and 4 error messages.
    , K5 A7 i  t4 L( `  [& A8 K+ ?
  13. "..\OBJ\01Test.axf" - 4 Error(s), 0 Warning(s).
    4 l0 b- Q2 E  Z2 q
  14. Target not created." w" V& _: O* Q1 I& X. }. F: c! Q% u
  15. 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。
  1. //0,不支持ucos
    , E1 o$ y5 m; V% ?  w8 w( A! A$ Z' \
  2. //1,支持ucos! s8 [, O: x' M" A
  3. #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
  1. //如果使用ucos,则包括下面的头文件即可.! C" Q$ w9 Y; |
  2. #if SYSTEM_SUPPORT_OS4 v  m4 X( b5 l; ?# j- H8 \
  3. #include "FreeRTOS.h"        //os使用
    4 }2 D  p( I! [6 j! K4 d+ L& k
  4. #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
  1. void USART1_IRQHandler(void)                        //串口1中断服务程序
    # g4 {0 b* @7 k; y7 \
  2. {3 B7 o# m% t! h- H! y" |4 a' @
  3. u8 Res;
    ; u( w( w# R3 b

  4. , {7 M7 s3 |% A3 [
  5. if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)  //接收中断(接收到的数据必须是0x0d 0x0a结尾)  [/ a3 U& h& ^  H
  6. {
    2 E( d0 F; v4 s& @' T/ @
  7.         Res =USART_ReceiveData(USART1);//(USART1->DR);        //读取接收到的数据- u! ?$ d! ]3 f$ T7 M
  8. + T1 T* A2 }" m) G/ {
  9.         if((USART_RX_STA&0x8000)==0)//接收未完成, v2 \% L$ ^" [0 D" z. J
  10.         {+ t9 `0 T/ v1 k  @5 |+ n+ C9 x
  11.                 if(USART_RX_STA&0x4000)//接收到了0x0d0 J2 g/ ^( ?& p" q0 n/ ?/ P
  12.                 {
    . t$ o4 ^! j# e  ?' I( C
  13.                         if(Res!=0x0a)USART_RX_STA=0;//接收错误,重新开始1 S) Y6 j' u$ t  a
  14.                         else USART_RX_STA|=0x8000;        //接收完成了 : b% d. B- T" {3 q6 i
  15.                 }: h0 |5 X5 F- C( q! s  Q
  16.                 else //还没收到0X0D0 z% @: ~' [. y' {  v
  17.                 {1 T+ ~# b( y: j
  18.                         if(Res==0x0d)USART_RX_STA|=0x4000;
    " S6 S! I% l; q3 H/ s
  19.                         else
    2 Y/ X2 i4 E2 M+ [. G
  20.                         {
    / o3 z0 v; Q$ H+ {. y) U
  21.                                 USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ;
    ; v! b! \" s' y1 f, ~- o
  22.                                 USART_RX_STA++;
    6 T0 v$ h, b* W5 d: e; u+ z
  23.                                 if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收数据错误,重新开始接收          2 Q, E6 U5 ]1 z7 J
  24.                         }                 
    " F/ [& z- V4 S! x/ ^& F+ U2 X" D7 Z
  25.                 }
    $ T# H2 _/ P' V& N7 W/ t
  26.         }                    - K. W  L9 H$ B0 J# P
  27. }
    ' s. B9 J) f# v  w
  28. }
复制代码
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
  1. extern void xPortSysTickHandler(void);
    . N4 l+ D4 `* a' B1 g

  2.   y$ z, A" [/ l8 a; y/ {
  3. //systick中断服务函数,使用OS时用到
    7 {( J0 C! J* ^; N: s3 {6 o
  4. void SysTick_Handler(void)
    ' u: Z5 p" B$ p$ Z* r
  5. {
    7 j; R; t6 s0 _
  6. if(xTaskGetSchedulerState()!=taskSCHEDULER_NOT_STARTED)//系统已经运行
    7 }- U/ b) i6 B1 x9 z% _6 o
  7. {
    0 f+ `- J( |. E" D- P
  8. xPortSysTickHandler();- z& R6 }% O+ I9 Z- m
  9. }
    8 K# ~7 d' w' s: j! F, P
  10. }
复制代码
: z2 z* O7 ]1 p/ P" \  l0 m5 g
delay_init()是用来初始化滴答定时器和延时函数,代码如下:/ |& Q& y1 |; A0 C' T! {
5 A9 }5 p: r; m
  1. //初始化延迟函数1 K# a6 {# F. V' a  G, @
  2. //SYSTICK的时钟固定为AHB时钟,基础例程里面SYSTICK时钟频率为AHB/8
    / S; I* e9 u' n; \! S$ o
  3. //这里为了兼容FreeRTOS,所以将SYSTICK的时钟频率改为AHB的频率!
    5 {. H* c( R9 M$ s. H# H$ J6 F. _
  4. //SYSCLK:系统时钟频率6 f) h7 Y% p# K4 M* W5 @. v0 [
  5. void delay_init(u8 SYSCLK)) h3 Q% \( A. x7 j& |! y* ~
  6. {
    6 B" W# M) g+ H7 K2 `3 ?" S5 u$ [
  7. u32 reload;
    - @$ P% Y3 r3 t; p$ |
  8. SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK);
    ; ~( d0 O$ u) ^$ B! D
  9. fac_us=SYSCLK;                                                        //不论是否使用OS,fac_us都需要使用
    7 X4 C9 M; F! d! d/ d
  10. reload=SYSCLK;                                                        //每秒钟的计数次数 单位为M
    5 {, S0 P, b* h- I& C. {, M
  11. reload*=1000000/configTICK_RATE_HZ;                //根据configTICK_RATE_HZ设定溢出时间
    , l* K8 F0 j9 i5 J* P2 J- w
  12. //reload为24位寄存器,最大值:16777216,在168M下,约合0.0998s左右
    2 L- L/ r& J* p3 ^& c
  13. fac_ms=1000/configTICK_RATE_HZ;                        //代表OS可以延时的最少单位/ S* i, W1 q4 T2 k3 J1 i
  14. SysTick->CTRL|=SysTick_CTRL_TICKINT_Msk;//开启SYSTICK中断
    9 [7 j4 y9 R% g/ }' F# U7 `' H. @: j) c
  15. SysTick->LOAD=reload;                                         //每1/configTICK_RATE_HZ断一次
    " m. s& h) v: M7 ?0 \" R' q
  16. SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk; //开启SYSTICK
    # u8 [. g+ K" h- a
  17. }
复制代码

# A& x& ]8 b. k) A$ Z延时函数定义如下:
& h0 r5 k+ w+ s8 G& u' q( o* _# F+ k1 b, U
  1. //延时nus( e/ X+ H7 E/ }8 m; ?
  2. //nus:要延时的us数.
    0 q% X( s* B  G" |" u! u: P
  3. //nus:0~204522252(最大值即2^32/fac_us@fac_us=168)
    9 N4 v! x0 h' E& w3 N' l
  4. void delay_us(u32 nus)& ]; ]. x( _2 ?
  5. {        
    $ f2 \0 I+ P" |1 j; E
  6. u32 ticks;  T) w& J* B/ Y1 [
  7. u32 told,tnow,tcnt=0;
    ( r0 @$ o6 P" S9 g4 T; ~
  8. u32 reload=SysTick->LOAD;                                //LOAD的值
    4 G6 w+ q- e' Z% B1 e4 c. A
  9. ticks=nus*fac_us;                                                 //需要的节拍数6 U+ W$ D7 l9 E, ^3 j6 o8 y
  10. told=SysTick->VAL;                                        //刚进入时的计数器值
    5 U/ ]) E/ [: I' l
  11. while(1)  o% b% @9 Z% ]; w6 S# f* \+ d+ O
  12. {+ [+ }8 F3 D+ \0 b
  13. tnow=SysTick->VAL;6 z0 d9 ]0 l- k& S4 R0 @
  14. if(tnow!=told). i& c" X& s2 l
  15. {
    * \& V$ G# N/ K. l' b3 l0 e
  16. if(tnow<told)tcnt+=told-tnow;        //这里注意一下SYSTICK是一个递减的计数器就可以了.. M4 d! K' t1 U& m
  17. else tcnt+=reload-tnow+told;
    " ~9 B9 }/ G7 i3 O
  18. told=tnow;
    7 a& [7 t- }2 e" D2 K9 k$ G/ h
  19. if(tcnt>=ticks)break;                        //时间超过/等于要延迟的时间,则退出.
    - ]1 o0 K+ T" b3 C# C
  20. }
    $ {" I  [9 @7 r5 F! V' |7 {
  21. };9 y/ i8 T5 [8 l* h0 P  U- ^
  22. }: ]9 i8 e1 v( e& M4 A. P# M
  23. //延时nms/ k; I! E: ?4 s- e9 h: G
  24. //nms:要延时的ms数9 @) [. z2 l& I# Y( Q5 q+ U4 p; U
  25. //nms:0~65535
    & a8 @5 }7 b4 F  Y  K; ?
  26. void delay_ms(u32 nms)$ ]" U' M) L0 q5 S& ~$ I
  27. {+ L  K/ o& N' g$ n) v, L
  28. if(xTaskGetSchedulerState()!=taskSCHEDULER_NOT_STARTED)//系统已经运行2 j4 V' E6 f1 w" I! R$ @
  29. {        
    ! [9 ]6 ^. ]3 {- f8 _
  30. if(nms>=fac_ms)                                                //延时的时间大于OS的最少时间周期2 y7 p, r. c& x/ k" h: Y- V
  31. {
    : U% ^7 Y9 x, Z& T% ?. u4 r
  32. vTaskDelay(nms/fac_ms);                         //FreeRTOS延时
    , c- W, ^" X) a* E( p, H; g
  33. }
    : K- F+ t2 `9 ?, B1 a6 a
  34. nms%=fac_ms;                                                //OS已经无法提供这么小的延时了,采用普通方式延时7 r0 u* A1 D7 M( g2 i! g
  35. }' W9 n1 ?7 g" Z! ^3 Q2 Z
  36. delay_us((u32)(nms*1000));                                //普通方式延时
    - |8 x: r! h- @! `, l
  37. }
    ' Y3 V5 x  O5 H( {8 j, k: m

  38. & J8 \, H" i: T: ]3 q1 s9 l7 H
  39. //延时nms,不会引起任务调度: U' Z: k3 B! A0 ]) O6 x7 P
  40. //nms:要延时的ms数6 c* Z% Z6 p* z  K/ Q
  41. void delay_xms(u32 nms)( v* S+ C# q. V
  42. {
    $ C4 A' b* Z$ W9 k: ]8 y8 `+ Y
  43. u32 i;3 l6 p0 I1 N8 G
  44. for(i=0;i<nms;i++) delay_us(1000);" [" `( P. ^1 e5 h" z
  45. }
复制代码
0 |- {4 O- _1 G+ R0 I
编译之后出现如下错误
0 f$ S+ v% F- _% V2 o2 M: i
' M0 d8 k5 [! u5 b0 E8 B
  1. ..\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
  1. #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
  1. #include "sys.h"
    # l4 o( L8 i: `$ J# y# g1 `
  2. #include "delay.h"* c" u* }, w% ~2 C
  3. #include "usart.h"
    # O1 E! H6 s  W/ g$ F
  4. #include "led.h"+ G% n3 o5 l7 f& {2 g
  5. #include "FreeRTOS.h"
    6 ^: k/ Q7 C4 l. j! g
  6. #include "task.h"0 l+ p- b. ?1 }+ E, N2 H% C9 E& S
  7. ! S' U8 N4 k2 p# `) y) A
  8. //任务优先级
    : G4 P( d+ i, u# }
  9. #define START_TASK_PRIO                16 |4 w- A! T6 F% @) H; Z- q
  10. //任务堆栈大小
    , L# b) W! t& ?" G) p% F
  11. #define START_STK_SIZE                 128/ i8 `8 d4 _' @0 L. Z; u7 A+ w
  12. //任务句柄
    3 t7 ~  l" [$ v# G
  13. TaskHandle_t StartTask_Handler;6 U- E8 @0 ~% P" E
  14. //任务函数
    & Q" v9 E- h6 S3 A. e" e
  15. void start_task(void *pvParameters);, u, L* L; ]) ~2 {

  16. - F2 ]* z, {$ ?# {" a* n
  17. //任务优先级+ p4 P& ~1 e/ h! p
  18. #define LED0_TASK_PRIO                2
    - o  c+ q0 v8 a4 l5 l0 u
  19. //任务堆栈大小
    . ?! T1 e8 @. v# E0 `! _6 Q
  20. #define LED0_STK_SIZE                 502 x0 I' I2 |) K$ [9 S- {3 L
  21. //任务句柄
    3 }! U* h7 L2 K
  22. TaskHandle_t LED0Task_Handler;6 V7 i* D- L. G0 |
  23. //任务函数# k9 r4 {4 P4 o& z4 x2 ^
  24. void led0_task(void *pvParameters);% H  j( E. k/ M* q) f- K
  25. 5 I: Z# D4 Z8 m
  26. //任务优先级
    ) ]6 L6 x6 e4 L- s/ U8 p. u' A5 D9 j
  27. #define LED1_TASK_PRIO                3
    / w, M9 X9 }' m; o+ M' k
  28. //任务堆栈大小1 z5 w& _7 a2 o) G# U
  29. #define LED1_STK_SIZE                 502 `7 R7 x4 C. o1 q' _! Z
  30. //任务句柄( {) o  V/ q% |5 g% Z
  31. TaskHandle_t LED1Task_Handler;
    ) e6 r6 u9 ?' d! Y
  32. //任务函数" K  o$ Y4 X/ c5 E9 Z- e* M
  33. void led1_task(void *pvParameters);
    & E! ~6 u# f& ~, G/ w  }4 C: G
  34. : G; ^+ e8 m% n2 G5 y% q& a
  35. //任务优先级
    4 I4 a7 I. M3 P. z
  36. #define FLOAT_TASK_PRIO                4" t' B3 m8 g0 A! J2 o
  37. //任务堆栈大小6 n+ ^- R& y2 b0 I8 o
  38. #define FLOAT_STK_SIZE                 128! j% }0 D; y1 H  S1 B& K+ P" m
  39. //任务句柄# U$ d4 s# h% b: V5 N% L8 c- g$ \
  40. TaskHandle_t FLOATTask_Handler;
    5 G$ O$ i& b( c; e
  41. //任务函数
    8 x6 x+ g4 g& n. t- o
  42. void float_task(void *pvParameters);' n5 s& q( s8 N7 Z  f0 l. ]

  43. ' M: `- N/ T7 |1 S
  44. int main(void)* H( }' R% f7 r/ A2 |* {
  45. {
    ! `, _" z, {; B- V, O7 |
  46. NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4+ w/ _, b/ a# a4 N& w3 d
  47. delay_init(168);                //初始化延时函数) r  H# }+ P: Y# }# ]/ l. s
  48. uart_init(115200);             //初始化串口
    2 |8 |6 J! j/ Q; l% X" k/ a
  49. LED_Init();                        //初始化LED端口
    ( X1 d9 H% @5 D" y- C& h( v) f" Q- s/ T

  50. 8 U( ], b* U; A$ u
  51. //创建开始任务
    " K5 a7 H9 k6 Y# l" F
  52. xTaskCreate((TaskFunction_t )start_task,            //任务函数
    + w' k7 k. a" d
  53.             (const char*    )"start_task",          //任务名称
    % {" |* P7 d( m5 u2 M: ?
  54.             (uint16_t       )START_STK_SIZE,        //任务堆栈大小1 t+ F( Y. x  d$ R
  55.             (void*          )NULL,                  //传递给任务函数的参数
    : u8 w$ l6 b6 l& z* M
  56.             (UBaseType_t    )START_TASK_PRIO,       //任务优先级5 H( m' A: m, e4 u; u& F) k
  57.             (TaskHandle_t*  )&StartTask_Handler);   //任务句柄            0 M! h/ j! _* m% P0 R+ v9 Q
  58. vTaskStartScheduler();          //开启任务调度
    ) J: Y0 B" m! @$ z% N% |
  59. }
    4 B3 a6 w) w2 l- m! h  S

  60. ) y/ N: ^/ O8 X$ W+ Q; k
  61. //开始任务任务函数
    5 |# x3 z$ M/ t
  62. void start_task(void *pvParameters)
    + [: Y3 U( P8 S* \
  63. {
    ! w9 G4 A/ C- D, t) Z! q
  64. taskENTER_CRITICAL();           //进入临界区
    ) m2 P* F6 n3 }% j; s8 h' ^
  65. //创建LED0任务
    ' k" q( v1 U% B* J. r& v
  66. xTaskCreate((TaskFunction_t )led0_task,   
    ! \0 c: u; m- K( k- N: V6 z- T
  67. (const char*    )"led0_task",   
    3 j6 L# A6 N7 @3 `  {# _. L
  68. (uint16_t       )LED0_STK_SIZE,: ]) C. |( T- x3 r
  69. (void*          )NULL,                        
    . {7 n' v, u; m: G+ H7 P
  70. (UBaseType_t    )LED0_TASK_PRIO,: O2 C! p" |5 `* |" G6 h
  71. (TaskHandle_t*  )&LED0Task_Handler);
    - `/ l7 \6 k) ]% K/ g
  72. //创建LED1任务
    ( P: @7 ]1 X* C( t9 \
  73. xTaskCreate((TaskFunction_t )led1_task,4 G; N( C  e( K  P
  74. (const char*    )"led1_task",
    & V; N( t7 \% v2 b1 b# v
  75. (uint16_t       )LED1_STK_SIZE," _9 K. ~, Q0 E
  76. (void*          )NULL,/ m0 i/ ?/ G% I7 V" G5 E; L
  77. (UBaseType_t    )LED1_TASK_PRIO,
    " e1 q6 ^- c# [& v# D
  78. (TaskHandle_t*  )&LED1Task_Handler);$ J4 s/ }+ O; o$ z) |1 s
  79. //浮点测试任务) |9 r* [4 X+ P/ c
  80. xTaskCreate((TaskFunction_t )float_task,
    - y1 Z7 H. l6 F" S: G/ |. f
  81. (const char*    )"float_task",
    4 p' }1 G8 i4 N- C3 h- u& `
  82. (uint16_t       )FLOAT_STK_SIZE,' s6 d! x3 \: a
  83. (void*          )NULL,
    ; F9 Z! @4 D! d9 c
  84. (UBaseType_t    )FLOAT_TASK_PRIO,  U, _8 A4 e( o- D$ }
  85. (TaskHandle_t*  )&FLOATTask_Handler);1 j7 n% l+ M9 p* ^, I0 {
  86. vTaskDelete(StartTask_Handler); //删除开始任务
    3 D; W/ d9 G5 ]# \, p9 A- p$ K
  87. taskEXIT_CRITICAL();            //退出临界区+ n2 `; S7 ~" E+ U% @/ ], R
  88. }+ b% B% j9 u: j8 ^+ B. N

  89. . ?& j' D+ ^# Y5 C% h3 j' K8 W5 s
  90. //LED0任务函数4 u7 P/ t: v9 ~. I/ Q
  91. void led0_task(void *pvParameters)
    5 _; Y  h& h2 |
  92. {$ @- k; c; v. y7 E0 t8 W- I: y
  93. while(1)
    ' T* B% ~- g/ h% r
  94. {; N, o; n7 _+ u1 ?" t7 K5 K
  95. LED0=~LED0;& j; R# F  c& I" @3 A# v
  96. vTaskDelay(500);" v. R; Y8 a- y7 v9 p4 @7 _. ?
  97. }. Y  S- q# O! {" a+ J
  98. }. I! a5 K+ j5 T0 f$ ?! p* t/ R1 I

  99. ! P+ o1 |0 U/ [3 `1 T
  100. //LED1任务函数1 i8 `7 N6 U3 E4 J. l: f9 i
  101. void led1_task(void *pvParameters)0 U; |: n( j9 z) B9 t
  102. {
      _2 ]+ u0 u& I) q! Q
  103. while(1)/ S! q! M) @9 g! P2 U
  104. {% X( P- t3 S, O3 \
  105. LED1=0;
    , \/ B5 l& f7 `; k
  106. vTaskDelay(200);) G! w: G7 t( F. j
  107. LED1=1;
    . ?" y! o* U# e
  108. vTaskDelay(800);
    + R, H! x9 C, C, q, l0 E3 G$ X& a
  109. }
    $ ~+ u% X- F- O
  110. }
    9 h+ R, M0 f1 g
  111. 7 R. |1 i  R) M2 X* p$ ^7 q
  112. //浮点测试任务5 u" N* t+ }4 V# t% c
  113. void float_task(void *pvParameters)
    ( T, {5 g2 P2 y; [
  114. {# y1 S2 J5 y8 |% o: B2 \
  115. static float float_num=0.00;
    ) \$ r& K! E5 ]& d, M
  116. while(1)+ C) W: h( f/ u/ X
  117. {! y4 F4 N6 M3 E
  118. float_num+=0.01f;$ [5 u# V2 M$ L  j" i5 }5 ^& W" [
  119. printf("float_num的值为: %.4f\r\n",float_num);% p: Z4 v; O1 g+ o
  120. vTaskDelay(1000);7 ^, f: a+ k! x) p* {2 ]
  121. }
    2 ?4 w0 }6 x+ {# V8 {7 T7 j
  122. }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
赞 收藏 评论0 发布时间:2022-3-27 16:00

举报

0个回答

所属标签

相似技术帖

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版