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

【经验分享】STM32 FreeRTOS移植

[复制链接]
STMCU小助手 发布时间:2022-6-17 14:05
01. 简介5 _" }& H5 j2 F  ?  j& G# _
我们已经了解了FreeRTOS,接下来我们就把FreeRTOS移植到我们所使用的平台上,我们使用的板子是STM32F407。
  @4 s7 Z/ _8 c, R: o
$ ~, X) w; I- U( f" v# @02. 预备工作
2 W; X2 Q% r! v) v. D1 U3 E2 a要移植FreeRTOS,肯定需要一个基础工程,基础工程越简单越好,我们就用之前的跑马灯实验作为基础工程。
: o- q7 o+ x: Z4 u: `& J# k
7 N2 c, C9 g3 o6 Q9 R, L) R7 v% v下载好FreeRTOS源码/ _' o9 z% r3 b  A

; P/ h% }; G/ n( s, }7 K03. FreeRTOS移植0 `. w' u. W7 H7 b( Z
3.1 创建FreeRTOS文件夹3 s+ m& ^4 [7 @6 a, V2 X

) z9 L" S6 t8 D+ z
00}X}$K)}CADG2RL3Z7C56F.png . N- b' n: S/ v  m8 I/ M% O

& }; X+ K" i3 Z8 |# f0 \3.2 将FreeRTOS的源码添加到FreeRTOS目录中
: W2 z. }3 M- {3 m" q
2 t+ v1 C, t: _, \
FreeRTOSv10.4.1\FreeRTOS\Source
: m" H. G% b8 _# s: g" ~& Z6 Z$ P/ n. |% a2 l
{V4TDKKZ2E`AU)KP]HTY~I2.png 7 Q' _1 M6 {/ G0 V" M, L

$ x1 Y/ w2 s8 g0 N3.3 删除portable文件夹中相关的文件和目录
* u: b4 g1 a3 J/ D: W
$ ?% z. O& |1 g我们只需要留下keil、MemMang和RVDS这三个文件夹,其余的都可以删除的。2 V" l6 [5 X2 E
7 ?$ e: t; H$ o8 |' ?8 O' l
GDN3NO`LRC`6GSX4HVX1HB0.png ; {: z& E- r# E6 ]

. N7 _% k- S6 O7 e5 w1 e! j3.4 向工程分组中添加文件7 F& i$ @, K$ }, @8 s

( Z) q+ p; U; Y, D0 ^" ?打开基础工程,新建分组FreeRTOS_CORE和FreeRTOS_PORTABLE,然后向这两个分组中添加文件。
& B/ W) R& i4 [: P% n' s6 |7 j% N& r- f
`@M$Q12J3[(7H~`}ZSQ@A.png
* }1 ]& w1 g3 v- l- r& J! Y$ q/ ~6 ^+ ]( ^
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文件是五种不同的内存管理方法。' |7 i! S0 F( m: M

7 q2 h0 Y! h& Z这5个文件都可以用来作为FreeRTOS的内存管理文件,只是它们的实现原理不同,各有利弊。后面我们再来讨论FreeRTOS的内存管理。
4 T$ A0 O* s3 N5 v& Q& j7 r- K$ p: c' z; r
3.5 添加响应的头文件路径
5 X2 r+ T0 b- J4 n7 h9 Z7 O+ U6 [9 q+ Q" D. h
{BKC5M]ZD5@P1DLI]MOK{AA.png - M3 y% n) p# k5 H, R* u) E' v# F

6 I! H1 m# t1 x( y3.6 找不到头文件的错误
  1. ..\FreeRTOS\croutine.c(27): error:  #5: cannot open source input file "FreeRTOS.h": No such file or directory
复制代码

3 n- q4 v6 n: H  o5 D" l: ^' F解决办法
/ F$ ]" \: k) o& R1 v% |: E
0 E; }# h2 e5 t4 L! ]. t: ]FreeRTOS.h头文件在FreeRTOS\include目录中。
  m9 G9 w5 G8 t6 T" T" ~8 Z* p3 q/ x: u. d- H
5}S}(NU)4M4A6TT4R1GC~[X.png 9 |+ w+ a: T! n. w
  z  p- P1 n) O
再次编译发现又有头文件找不到FreeRTOSConfig.h文件0 q  x: e+ b0 _  s. p' b. L

3 k5 D0 F) Y+ ^4 j
  1. ..\FreeRTOS\include\FreeRTOS.h(57): error:  #5: cannot open source input file "FreeRTOSConfig.h": No such file or directory
复制代码

1 \% C( m7 h' p. q* {6 I在项目文件中搜索该头文件发现找不到,这个时候我们可以找找FreeRTOS的官方移植工程汇总会不会有这个文件,打开FreeRTOS针对STM32F407的移植工程文件,文件夹是H:\02_stm32\其它资料\FreeRTOS\FreeRTOSv10.4.1\FreeRTOS\Demo\CORTEX_M4F_STM32F407ZG-SK。
# l( s: m* j7 x$ F/ |* q1 P$ j$ A/ T  }1 L* L
HQ4XZORL6(%_UV}VR}(E6LS.png - ?4 C; P6 K/ K( b

+ s. A8 v$ e2 b, l9 ]7 M找到该文件之后,我们直接将它复制到FreeRTOS源码中的include文件夹下。FreeRTOSConfig.h文件是配置文件,一般的操作系统都有裁剪、配置功能,而这些裁剪及配置都是通过一个文件来完成的,基本都是通过宏定义来完成对系统的配置和裁剪的。* H% K* @2 `/ C4 a
/ _/ r, G0 a, S9 q& r) P
3.7 SystemCoreClock没有定义的错误" X# {/ c1 `4 Q7 g8 e$ h
& ]; {0 n' P  d
  1. ..\FreeRTOS\portable\RVDS\ARM_CM4F\port.c(732): error:  #20: identifier "SystemCoreClock" is undefined
复制代码

8 ?- R/ N6 w; S/ uSystemCoreClock未定义,因为在FreeRTOSConfig.h中使用了SystemCoreClock来标记MCU的频率。
8 i# C# L" {5 {$ e3 [  \  P9 v0 }  @) w+ B. _" k  J( I
R}})9]N4BSC_24~}KZO]9NB.png ) U; R. S1 E; ?

/ z. S6 j5 a. V0 N; x+ g$ z这是一个条件编译,只有定义了__ICCARM__以后下边的代码才有效。+ @" i  I8 `6 K3 R+ D

  z( U5 O# j# K  `% H+ ^3 ^
  1. /* Ensure stdint is only used by the compiler, and not the assembler. */3 j! ?$ Q  E, a% {; z. ~/ O
  2. #ifdef __ICCARM__
    " ~% Z3 ^, g& _, P
  3.         #include <stdint.h>* h2 }5 D8 M+ Q8 j6 K
  4.         extern uint32_t SystemCoreClock;
    " Z8 M$ ^$ e8 g# e2 v
  5. #endif
复制代码

" b/ S% l1 m+ k8 P  m修改这个条件编译,修改后的代码如下:
; D3 ]% Q- K& p; I( j. G2 S' e1 [7 n1 v. c
  1. /* Ensure stdint is only used by the compiler, and not the assembler. */( ?" V4 |+ l5 a8 v/ a" p4 t
  2. #if defined(__ICCARM__) || defined(__CC_ARM) || defined(__GNUC__)6 `6 d+ Q: l# ~2 A0 B4 y: ~
  3.         #include <stdint.h>
    3 H1 x4 c  T1 Z" N% h: d) ?/ [
  4.         extern uint32_t SystemCoreClock;
    ) b/ A8 I; x: U- `& l  P
  5. #endif
复制代码
. L: [! r; Y* D7 s$ `
3.8 重复定义的错误
  1. ..\OBJ\01Test.axf: Error: L6200E: Symbol PendSV_Handler multiply defined (by port.o and stm32f4xx_it.o).
    8 u) x# W% R  a
  2. ..\OBJ\01Test.axf: Error: L6200E: Symbol SVC_Handler multiply defined (by port.o and stm32f4xx_it.o).9 ?- |5 n7 k3 F' }
  3. ..\OBJ\01Test.axf: Error: L6200E: Symbol SysTick_Handler multiply defined (by port.o and stm32f4xx_it.o).
复制代码

- ^5 F% m/ t7 I8 z1 D" J/ Oport.c和stm32f4xx_it.c两个文件中有重复定义的函数:PendSV_Handler(), SVC_Handler(), SysTick_Handler()。屏蔽掉stm32f4xx_it.c中的PendSV_Handler(), SVC_Handler(), SysTick_Handler()三个函数。再次编译,发现如下未定义错误。
; C' t# |& X# l) c9 h9 {! |, _5 f5 A! s. J2 w% p" C0 z
3.9 未定义错误! b0 b0 E. K0 f2 Z! e  Y8 N
: T+ E7 g1 V4 n( [2 ?
  1. Build started: Project: 01Test
    % A) g) w1 Q7 c$ @$ y- b1 i6 a" }
  2. *** Using Compiler 'V5.06 update 6 (build 750)', folder: 'D:\Keil_v5\ARM\ARMCC\Bin'3 y8 Z! y) J4 e- u
  3. Build target '01Test'
    " U' v* H( c) ~5 ]- n+ {% q
  4. compiling stm32f4xx_it.c...
    ; C- w- Q8 G+ O' _( `. d6 ]
  5. linking...
    8 B$ ?3 o) \0 m4 E2 \0 S$ P
  6. ..\OBJ\01Test.axf: Error: L6218E: Undefined symbol vApplicationIdleHook (referred from tasks.o).' N% Z1 _" E8 g4 m( L4 g4 M4 r
  7. ..\OBJ\01Test.axf: Error: L6218E: Undefined symbol vApplicationStackOverflowHook (referred from tasks.o).
    + ]) ^( [- d, k) n
  8. ..\OBJ\01Test.axf: Error: L6218E: Undefined symbol vApplicationTickHook (referred from tasks.o).4 B9 J; f! s4 {
  9. ..\OBJ\01Test.axf: Error: L6218E: Undefined symbol vApplicationMallocFailedHook (referred from heap_4.o)./ c. ]7 M( B1 b, r  o8 Y
  10. Not enough information to list image symbols.
    7 C' g: G* N- n, }
  11. Not enough information to list load addresses in the image map.
    ; ^  F8 X9 P; [8 C. u
  12. Finished: 2 information, 0 warning and 4 error messages.
    - L5 t' f( x+ V% h- ^2 D2 M3 p0 w7 z2 X
  13. "..\OBJ\01Test.axf" - 4 Error(s), 0 Warning(s).! U2 ?% [) y4 p2 y, a) e" Y
  14. Target not created.
    6 G! W$ [* M; c) r/ m4 s$ L2 L$ S
  15. Build Time Elapsed:  00:00:05
复制代码

) @, s' n7 q- h! m9 K( n发现这些未定义的函数,它们都是Hook结尾的,这些函数有个共同的名称:钩子函数,这是因为在FreeRTOSConfig.h中开启了这些钩子函数,但是没有定义这些钩子函数导致的。我们在FreeRTOSConfig.h中关闭这些钩子函数就行了,关闭的方法很简单,将相应的宏定义改为0即可。这里将宏configUSE_IDLE_HOOK、configUSE_TICK_HOOK、configUSE_MALLOC_FAILED_HOOK和configCHECK_FOR_STACK_OVERFLOW定义为0。$ B8 Z" D/ R0 {* W3 w
1 Z; w& j" g/ m  I
最后编译一下,应该就没有错误了,如果还有错误的话大家自行根据错误类型查找和修改错误。0 ]( m. F' V0 Q

; I5 C; B0 R# K6 r: {' d% v04. SYSTEM文件修改
* I6 e' c! [( I) L
4.1 修改sys.h文件
2 b9 ~1 i* b# Q4 q+ S8 S
+ G" K0 I( h. f0 M; ]3 R0 f9 A
使用FreeRTOS,我们应该将宏SYSTEM_SUPPORT_OS改为1。
  1. //0,不支持ucos4 v. ^$ `% v' g) o1 J% P" x/ m/ P: |
  2. //1,支持ucos
    ' s( I7 t. ]& a- p
  3. #define SYSTEM_SUPPORT_OS                1                //定义系统文件夹是否支持OS
复制代码

3 u( |  A; {5 Z( z" w" ~! d4 T1 b4.2 修改usart.c文件
5 Z7 z1 o; r) _: @3 [) g4 B/ f" s6 ]$ J  n, O
usart.c文件修改也很简单,usart.c文件有两部分要修改,一个是添加FreeRTOS.h头文件,默认是添加的UCOS中的includes.h头文件。修改之后内容如下:
% V4 Y4 I6 e# e5 c  O% W- m6 z9 C& N# r
  1. //如果使用ucos,则包括下面的头文件即可.4 K/ D$ C3 L: L3 H/ o
  2. #if SYSTEM_SUPPORT_OS. i* p$ T# u4 `
  3. #include "FreeRTOS.h"        //os使用                                          8 q0 @9 B+ F4 u. h" C9 V; w' u3 {8 s
  4. #endif
复制代码
3 Y, \; k$ E1 m2 M8 ]: J
修改USART1中的中断服务函数,在使用UCOS的时候需要进出中断的时候添加OSIntEnter()和OSIntExit()。使用FreeRTOS的话就不需要了,所以将这两行代码删除即可。
  j5 `5 l, m$ {$ w: ^+ [8 t
; ~( d+ E1 F! h
  1. void USART1_IRQHandler(void)                        //串口1中断服务程序* G! q# _3 F% `9 F# \' [/ T3 c
  2. {0 n& Q9 e1 `% r
  3.         u8 Res;% s  U6 i: W) y: q3 s! Z

  4. 3 X6 `6 Z7 J% ~7 N
  5.         if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)  //接收中断(接收到的数据必须是0x0d 0x0a结尾)4 X) j& \7 F9 v2 ~% F; w
  6.         {' U' Z1 Q# w' g; C( y
  7.                 Res =USART_ReceiveData(USART1);//(USART1->DR);        //读取接收到的数据/ l& h$ x; e0 o( K! B5 {
  8.                
    * v& R0 x8 y; R* @
  9.                 if((USART_RX_STA&0x8000)==0)//接收未完成% P- [8 `2 {4 V$ G# K; N
  10.                 {( i/ I* @. F. r
  11.                         if(USART_RX_STA&0x4000)//接收到了0x0d
    $ o+ J' G5 i2 v- g
  12.                         {
    9 s- R9 D! Z9 r" k- T4 n, {& G
  13.                                 if(Res!=0x0a)USART_RX_STA=0;//接收错误,重新开始
    " w9 v/ J/ o  u" T7 O& J. m& w7 L+ O
  14.                                 else USART_RX_STA|=0x8000;        //接收完成了
    ) m& D+ T& A0 i% b7 U
  15.                         }% x4 D) C) N6 P/ g* C' L$ [
  16.                         else //还没收到0X0D5 }3 [6 K9 R7 C' R% K
  17.                         {        
    7 l, c. h8 i: t6 v. o
  18.                                 if(Res==0x0d)USART_RX_STA|=0x4000;
    + y, r* I$ k& `$ C2 u& Q
  19.                                 else1 B  H8 [9 W6 ~7 |" ?( f' r4 |
  20.                                 {
    5 q0 v2 k% l1 ~' t
  21.                                         USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ;
    ' ]- Z8 M; ^6 H7 A# X/ D& H
  22.                                         USART_RX_STA++;
    ! f  J( }4 x8 p8 O
  23.                                         if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收数据错误,重新开始接收         
    % t  w& j8 ~9 K$ H* y7 j
  24.                                 }                 3 @0 ?0 _5 f! x) Y! j
  25.                         }
    - l9 @1 }/ X9 y: n
  26.                 }                    
    1 ?- Q" a/ l* Z2 B
  27.   } " w; I- q1 M9 D
  28. }
复制代码

$ _% B# f/ J) i# A9 }6 a4.3 修改delay.c文件
" x# K& X- Q  J9 S2 V5 e2 w9 d0 _
: x  c( p5 P' }delay.c文件修改的就比较大了,因为涉及到FreeRTOS的系统时钟,delay.c文件里面有4个函数。- k7 c% f% u' t5 O* c. ^

/ T& j9 L- k; {: ^6 s3 z" n, A首先来看看SysTick_Handler(),次函数是滴答定时器的中断服务函数,代码如下:0 l4 ]+ y! ?* O9 m& U9 f! Q7 I
+ ]- ^, E" f) w$ r" `+ Y. s
  1. extern void xPortSysTickHandler(void);0 u0 ^+ q  T& ]4 G4 H$ K) S

  2. . l  X  O: x7 c, m
  3. //systick中断服务函数,使用OS时用到
    9 E* v& q/ x/ F$ j
  4. void SysTick_Handler(void)) w5 x3 i- M: Q0 |6 ]/ T: K) R/ V
  5. {        
    - F; C' b1 v6 L2 E2 B$ f; _3 ^" i
  6.     if(xTaskGetSchedulerState()!=taskSCHEDULER_NOT_STARTED)//系统已经运行$ |9 g/ `* r0 S' q- `
  7.     {( D' S: T: g! m9 V" C
  8.         xPortSysTickHandler();        
    $ Z0 f" n- d6 n, H# A# O
  9.     }
    * P7 W1 D4 x, H0 o
  10. }
复制代码

4 W4 a, `2 X1 Vdelay_init()是用来初始化滴答定时器和延时函数,代码如下:
/ G1 ]$ E: v+ G7 P, ]- R* n, r
1 J6 J: c5 x, Q
  1. //初始化延迟函数7 N- C: J6 q6 P0 |0 F3 S
  2. //SYSTICK的时钟固定为AHB时钟,基础例程里面SYSTICK时钟频率为AHB/8
    ' l4 h3 }% R. b: J& Q: P9 Z9 {
  3. //这里为了兼容FreeRTOS,所以将SYSTICK的时钟频率改为AHB的频率!
    - l# p! K" b% k3 x7 ]
  4. //SYSCLK:系统时钟频率
    4 X3 Q; H! z5 M" h- f; Q
  5. void delay_init(u8 SYSCLK). x: ]. @( i. B+ A& s8 I, h3 r
  6. {' T, E' @3 t) H
  7.         u32 reload;
    + Z: W" t; g. u: D& @- M3 E! U
  8.          SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK); ' W1 T  A$ g  x7 H
  9.         fac_us=SYSCLK;                                                        //不论是否使用OS,fac_us都需要使用+ D  `* S$ g. Z& ?! e& m
  10.         reload=SYSCLK;                                                        //每秒钟的计数次数 单位为M           
    & u3 C5 y6 k5 b4 |3 }7 _& W
  11.         reload*=1000000/configTICK_RATE_HZ;                //根据configTICK_RATE_HZ设定溢出时间3 V8 V0 F/ u* ^( }' r# t
  12.                                                                                         //reload为24位寄存器,最大值:16777216,在168M下,约合0.0998s左右        ( h5 S* L# w% S2 R6 e# Q0 i
  13.         fac_ms=1000/configTICK_RATE_HZ;                        //代表OS可以延时的最少单位           
    ! @, i7 b( }. e4 ]) c% H: q
  14.         SysTick->CTRL|=SysTick_CTRL_TICKINT_Msk;//开启SYSTICK中断
    . g5 X; z" k+ k
  15.         SysTick->LOAD=reload;                                         //每1/configTICK_RATE_HZ断一次        
    8 ^( @; [0 m- o
  16.         SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk; //开启SYSTICK     , l! V. Q2 r& P# k# E% v% A3 F$ D
  17. }         
复制代码
5 G1 @8 @$ f3 K  W! y) `2 l
延时函数定义如下:
, \: f9 b. d0 I. O" ]8 S( R, v- N8 _( j& ~. a1 N
  1. //延时nus
    3 T6 N* t! [# y& q1 c
  2. //nus:要延时的us数.        
    9 i' G2 I" u! P
  3. //nus:0~204522252(最大值即2^32/fac_us@fac_us=168)                                                                              
    / _; g" i. I% H  ?
  4. void delay_us(u32 nus)
    + c- U1 @/ R- o
  5. {               
    7 m) K8 E. O: G& |0 G8 P
  6.         u32 ticks;
    & P8 D" N7 b5 X6 G
  7.         u32 told,tnow,tcnt=0;
    % k2 ]  W9 x8 {' `
  8.         u32 reload=SysTick->LOAD;                                //LOAD的值                     
    & |  A% P4 P) T% [: }
  9.         ticks=nus*fac_us;                                                 //需要的节拍数
    8 {& H; a" l+ \6 ]
  10.         told=SysTick->VAL;                                        //刚进入时的计数器值, G' b0 p4 c) I8 c
  11.         while(1)# y/ K9 m  \! ~: B" Z
  12.         {
    + K* S( ]# x2 C7 }1 n6 N/ H
  13.                 tnow=SysTick->VAL;        
    7 w8 }1 i+ y6 ?7 i- j
  14.                 if(tnow!=told)
    + k1 i- [( z$ l; G& ^
  15.                 {            
    : p! O1 E( a% b8 n. ^0 M/ f+ M
  16.                         if(tnow<told)tcnt+=told-tnow;        //这里注意一下SYSTICK是一个递减的计数器就可以了.
    , p% L( b6 G2 Z: }3 ]) B/ f
  17.                         else tcnt+=reload-tnow+told;            " S& p6 T9 L: C0 ~. g0 P
  18.                         told=tnow;
    ; u2 b9 g& q9 V6 g+ E$ E& j
  19.                         if(tcnt>=ticks)break;                        //时间超过/等于要延迟的时间,则退出.
    ; G$ h; z, d, L8 _1 o" c: ]7 a
  20.                 }  * G" e5 H  {- y7 U6 L
  21.         };                                                                                    
    / r/ w% S* T5 D. A* A0 M( `
  22. }
    ; T) k* H/ V) L  [6 b: o: J
  23. //延时nms# h4 e$ {9 f  K3 W$ J$ `
  24. //nms:要延时的ms数0 T7 T' C( E8 F, N; p4 [! J3 N
  25. //nms:0~65535
    6 X! G- d, G' h. Z0 S
  26. void delay_ms(u32 nms)
    0 `$ {# [! L0 R' S% S# ]
  27. {        
    % T4 h7 a  ~* i3 U& G& c2 n6 ~
  28.         if(xTaskGetSchedulerState()!=taskSCHEDULER_NOT_STARTED)//系统已经运行
    " u0 _9 R) w; U: \0 g4 o
  29.         {                4 G. O! R( U# S
  30.                 if(nms>=fac_ms)                                                //延时的时间大于OS的最少时间周期 : V' H8 N! b0 ?' O4 b5 D) |. j
  31.                 { # Y2 ~1 A/ c, }: @8 X, E( a/ j4 k
  32.                            vTaskDelay(nms/fac_ms);                         //FreeRTOS延时2 E/ `3 c  h$ h/ B
  33.                 }+ W5 a% p% }# J& k
  34.                 nms%=fac_ms;                                                //OS已经无法提供这么小的延时了,采用普通方式延时    ' j  R* ~! a, s+ j# `3 H
  35.         }
    2 s/ T* ]# M! K5 l$ W0 n
  36.         delay_us((u32)(nms*1000));                                //普通方式延时
    ( b: J+ `# S5 d6 L+ t5 H
  37. }2 J$ V% X; ]) S2 K* W
  38. " t2 w2 ?8 v6 _3 ]% v5 f
  39. //延时nms,不会引起任务调度2 n) ?; B: u+ ~* b
  40. //nms:要延时的ms数; V2 \/ G5 c/ i  D. t! P' ^' W
  41. void delay_xms(u32 nms)3 ~" z8 @! b" b, S8 s
  42. {. D, g- O- F- T+ U1 {
  43.         u32 i;) f+ Y  ]( e4 e0 U# j0 x0 z
  44.         for(i=0;i<nms;i++) delay_us(1000);
    ' W! i- L1 B( M' I( v
  45. }
复制代码
( l1 J4 z6 I9 k# ?- f5 q
编译之后出现如下错误0 ^' i7 a  v' Q/ E
& x3 N( _1 X" }2 p
  1. ..\OBJ\01Test.axf: Error: L6200E: Symbol SysTick_Handler multiply defined (by port.o and delay.o).
复制代码

1 _& |4 H; G+ {6 J2 _, q在port.c和delay.c中有重复定义的函数,SysTick_Handler只能二选一,很明显delay.c中的SysTick_Handler()保留下来,打开FreeRTOSConfig.h文件,找到如下宏定义:3 X  i' ~: c0 w; H7 Y& G
) m8 {. n9 S, v4 H* `* _/ ]8 R
  1. #define xPortSysTickHandler SysTick_Handler
复制代码

. c* F, O: f- r) m" {8 i1 h$ w/ B注释上面的宏定义。
7 c) q9 c  E9 v9 V
& s' v5 D) f0 J9 ~6 T05. 测试程序设计
1 f; s8 ]7 c- j! @main.c文件
1 \/ A, q4 M" h9 g5 n: f  ^/ |) S4 w5 j, y& |3 [
  1. #include "sys.h") P! Z( \- \" `5 h0 H
  2. #include "delay.h"0 o3 S* z! O4 r& U( x6 h
  3. #include "usart.h"
    5 \8 e1 Z, P! F
  4. #include "led.h". Q) I. l3 x. y8 W$ v
  5. #include "FreeRTOS.h"4 M* Q: K# z0 F( K
  6. #include "task.h"; {# g# u$ R: Q, a2 c* _. C

  7. - ?3 C1 W: [9 D4 @" a- b
  8. //任务优先级
    - E  P4 c, U$ T
  9. #define START_TASK_PRIO                1' C3 S0 [* I+ I! X. O% s
  10. //任务堆栈大小        ( P3 D/ {* s4 b& E- c( `
  11. #define START_STK_SIZE                 128  . @! L7 E' D! J# ?
  12. //任务句柄
    / D) o$ u4 |, z! T+ Y3 F
  13. TaskHandle_t StartTask_Handler;9 R9 Y) u6 x9 B! K
  14. //任务函数3 T7 i8 Q& Z0 S7 {
  15. void start_task(void *pvParameters);. p% Q( H* e/ e8 v& ^% v

  16. ' p' W9 {" W! z. ?& ]0 L
  17. //任务优先级# o# w+ b5 g7 E
  18. #define LED0_TASK_PRIO                2
    ! c5 l* Z: R# ^) ^- a& F3 L+ i( g: n9 F0 R
  19. //任务堆栈大小        6 _  s9 p9 Y: ~+ D$ r2 c8 Z8 ?6 n
  20. #define LED0_STK_SIZE                 50  
    # B) D" z( c7 o% X5 E2 R' {
  21. //任务句柄
    5 C8 r) X# r% T! X% v
  22. TaskHandle_t LED0Task_Handler;
    4 r: e) p! {; y) I) ]; F0 \
  23. //任务函数
    : Y" o% y2 a- g: f/ y8 M
  24. void led0_task(void *pvParameters);
    9 ?4 L8 c+ C! z! K& P
  25. 3 L8 H6 R$ C& i: ^
  26. //任务优先级  Y3 H6 X5 R, m- `
  27. #define LED1_TASK_PRIO                3
    ' o# u# Q$ \- c
  28. //任务堆栈大小        8 J: _' D/ W* w2 y
  29. #define LED1_STK_SIZE                 50  
    " N1 Q6 W0 U. P7 g7 W+ M
  30. //任务句柄' R6 T) P* a# b9 |/ _
  31. TaskHandle_t LED1Task_Handler;) T& U, E, U5 X  m+ Z. l* Z
  32. //任务函数
    ; ]* U2 P. Z( m
  33. void led1_task(void *pvParameters);' r6 C( F3 n; U' ^* T8 y
  34. 5 A1 e8 n' K5 s
  35. //任务优先级
    - K" ]6 u0 Z/ p- ]7 b0 ~
  36. #define FLOAT_TASK_PRIO                4
    ! {  v6 T0 V  {+ u& p( A; q
  37. //任务堆栈大小        # K; i7 f0 R  s( Z& C0 N
  38. #define FLOAT_STK_SIZE                 128
    ' N7 ?' s' J* v1 Q6 X7 O7 ]8 Q) T
  39. //任务句柄
    3 ~- e- S0 X7 R3 i
  40. TaskHandle_t FLOATTask_Handler;
    & U9 l$ o9 I3 C
  41. //任务函数
    3 O1 H/ E( ~$ y6 x# z. U6 d. A
  42. void float_task(void *pvParameters);0 q. [; [/ T' O* @% t

  43. / U% r5 E6 \* ?
  44. int main(void)
    & N9 d, P7 I* C+ u) I, N
  45. {
    2 P4 m# X" r+ _2 ?
  46.         NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4
    2 X! k+ W- `0 b1 V2 n
  47.         delay_init(168);                //初始化延时函数+ h) g8 @: K& q5 `, g
  48.         uart_init(115200);             //初始化串口
    , m6 P$ I% @+ F7 _' o
  49.         LED_Init();                        //初始化LED端口
    6 e4 X( s$ [  Y) h! L" [; s4 W0 e
  50.         
    1 L+ V- }9 |0 r. K
  51.         //创建开始任务8 ?9 q' F* x9 w: w
  52.     xTaskCreate((TaskFunction_t )start_task,            //任务函数
    5 r# m6 E6 z/ }
  53.                 (const char*    )"start_task",          //任务名称9 F" Q6 M8 E1 T. N% a
  54.                 (uint16_t       )START_STK_SIZE,        //任务堆栈大小
    . X4 ]1 d0 W% p4 d, T* d* u' E
  55.                 (void*          )NULL,                  //传递给任务函数的参数" i' _# D. [/ f. m$ G  P1 j
  56.                 (UBaseType_t    )START_TASK_PRIO,       //任务优先级
    0 T' H7 g; {! ]# o9 E  d2 m
  57.                 (TaskHandle_t*  )&StartTask_Handler);   //任务句柄              6 Z1 O% W  s9 k+ i# {/ o5 w# r2 G
  58.     vTaskStartScheduler();          //开启任务调度
    1 `: Q; l- x/ ^  h- U
  59. }8 X# }9 m/ S* \+ R
  60. $ }( b0 \0 i/ x+ a6 t# T
  61. //开始任务任务函数5 a" n# c" s5 z' g2 G+ L# d
  62. void start_task(void *pvParameters)
    : q1 [0 x, u, N9 r
  63. {1 B4 i  Q& b- J7 C: O
  64.     taskENTER_CRITICAL();           //进入临界区6 n8 |, m* X  {+ y! y4 E8 N0 Z) b
  65.     //创建LED0任务
    ' V5 c$ ^% o+ |% |$ n1 `: F% ^
  66.     xTaskCreate((TaskFunction_t )led0_task,            
    ' h- y# ]$ l( M
  67.                 (const char*    )"led0_task",           
    : H/ R: B$ y  H3 A7 a
  68.                 (uint16_t       )LED0_STK_SIZE, # n3 i* @' Y; g5 e' W: G: {/ p
  69.                 (void*          )NULL,                                
    ' E/ `( \( Q$ [# A3 o- r3 |
  70.                 (UBaseType_t    )LED0_TASK_PRIO,        
    , C# Y# b- R- i4 M
  71.                 (TaskHandle_t*  )&LED0Task_Handler);   + {1 L: I4 S% y: L
  72.     //创建LED1任务
    % p" A- n$ F. L9 l; ?
  73.     xTaskCreate((TaskFunction_t )led1_task,     
    3 M) ~/ t; Y0 t
  74.                 (const char*    )"led1_task",   
    % e% X5 d+ ^1 {  r
  75.                 (uint16_t       )LED1_STK_SIZE,
    1 O" Q5 }/ Z5 t+ ?8 m' ]# p! m% g
  76.                 (void*          )NULL,
    3 ?+ L* W6 u! D9 C4 z$ g& h
  77.                 (UBaseType_t    )LED1_TASK_PRIO,
    : o/ W5 d+ \# z
  78.                 (TaskHandle_t*  )&LED1Task_Handler);        
    ! h5 L# S4 Y) d/ O$ i! p
  79.     //浮点测试任务
    2 w$ |: a3 l  N
  80.     xTaskCreate((TaskFunction_t )float_task,     ; d  l& b: z+ h1 g$ S2 h
  81.                 (const char*    )"float_task",   
    ; L1 S$ c5 _+ x, |& l% a
  82.                 (uint16_t       )FLOAT_STK_SIZE, 3 K4 w9 z/ c  z$ r( E0 D
  83.                 (void*          )NULL,; t7 Y" J( k9 p
  84.                 (UBaseType_t    )FLOAT_TASK_PRIO,
    ; U* Y5 V5 d" v* d" p
  85.                 (TaskHandle_t*  )&FLOATTask_Handler);  8 r& s& h2 p! h3 a6 s2 O
  86.     vTaskDelete(StartTask_Handler); //删除开始任务
    - m% ~* V) ^2 s( b) }
  87.     taskEXIT_CRITICAL();            //退出临界区+ g& h  u  D; x" X
  88. }" z. c  G, c1 z/ E2 f7 A6 L6 ~

  89. ! d8 J# Z1 P! M
  90. //LED0任务函数
    2 t) @7 n* ?; s) t- o
  91. void led0_task(void *pvParameters)
    6 W* t  h& r( P1 w$ M
  92. {( o& a3 b! M- z& R6 c9 y
  93.     while(1)
    ! @* J6 \% Q/ a5 s+ O# x' d/ I
  94.     {
    2 z9 m2 B, y- ~' u% x1 Z
  95.         LED0=~LED0;& _% N2 |- T% [! w6 U9 S
  96.         vTaskDelay(500);
    7 z$ P( B/ w$ d
  97.     }
    ( X7 u7 _% a0 M8 m, z. C, d
  98. }   
    * m7 X8 w) f  u1 ~
  99. 2 V4 r2 n/ T" b9 I' j
  100. //LED1任务函数, c1 K0 U+ G3 O; m
  101. void led1_task(void *pvParameters)/ S- c: u  n- d* y
  102. {
    ; {1 d0 o1 Z5 |& Y. ?+ r, n) s
  103.     while(1)9 v2 }7 h  F- f2 i. y; v5 {2 L5 |
  104.     {/ d2 @% a9 P' z% A8 a# n
  105.         LED1=0;# i1 o0 t1 L, l" p0 h: Y
  106.         vTaskDelay(200);2 x) s2 Z/ C. o- S9 }1 x3 Z* _
  107.         LED1=1;$ v% R# I5 k5 c8 x
  108.         vTaskDelay(800);# ~; P: ^- h$ \
  109.     }8 t; p5 k. g: r" f4 Q( l! E- @
  110. }
    + J  K1 N% f3 a' p1 G

  111. 8 w9 [. }0 _' G" ~5 I) K
  112. //浮点测试任务
    ) B! t/ }, y1 V2 R! t* i
  113. void float_task(void *pvParameters)
    ; D1 c$ }: a- K+ Q- T
  114. {& x0 Q& j! R) k4 |
  115.         static float float_num=0.00;# ]* Z5 ^9 {8 x' S/ l3 J* ?
  116.         while(1)2 ]& r- Q* S! t: c6 S- Y
  117.         {
    : X1 ^% r) H! M3 Q- |8 J) Z: Y; Y: n: c
  118.                 float_num+=0.01f;0 I- B) e# k% R7 i2 Q
  119.                 printf("float_num的值为: %.4f\r\n",float_num);
    8 V+ Z: L0 e9 z' m9 j
  120.         vTaskDelay(1000);1 O9 I4 S- f& B/ S
  121.         }
    6 E; f. T2 ]0 [$ r
  122. }
复制代码

5 a. |1 v- C0 D$ p, h1 Q实验结果:
8 D* H# w0 T1 K1 f1 s) O- D

5 x  I2 T3 g  c4 a$ G  f8 qLED0和LED1开始闪烁,LED0均匀闪烁,LED1亮的时间短,灭的时间长。然后串口不断有数据输出。
, b- }) r' P' g) s; i% `( s+ s; c$ ~. h! K2 r
  N9 u5 U0 H0 x- s
赞 收藏 评论0 发布时间:2022-6-17 14:05

举报

0个回答

所属标签

相似技术帖

官网相关资源

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