准备工作

下载FreeRTOS系统

在移植之前,我们首先要获取到FreeRTOS的官方的源码包。这里我们提供两个下载链接:

一个是官网:http://www.freertos.org/
另外一个是代码托管网站:https://sourceforge.net/projects/freertos/files/FreeRTOS/

这里我们演示如何在代码托管网站里面下载。打开网站链接之后,我们选择FreeRTOS的最新版本V9.0.0(2016年),尽管现在FreeRTOS的版本已经更新到V10.0.1了,但是我们还是选择V9.0.0,因为内核很稳定,并且网上资料很多,因为V10.0.0版本之后是亚马逊收购了FreeRTOS之后才出来的版本,主要添加了一些云端组件,我们本书所讲的FreeRTOS是实时内核,采用V9.0.0版本足以。

注:本次自己测试使用的是官网中下载最新版本 FreeRTOSv202112.00

  • 打开官网后如图所示,并点击右上角绿色按钮
  • 跳转到如下页面之后,点击下载即可
  • 下载完成之后解压,如图所示

简单介绍FreeRTOS

FreeRTOS包含Demo例程和内核源码(比较重要,我们就需要提取该目录下的大部分文件)。
Source文件夹里面包含的是FreeRTOS内核的源代码,我们移植FreeRTOS的时候就需要这部分源代码;
Demo 文件夹里面包含了FreeRTOS官方为各个单片机移植好的工程代码,FreeRTOS为了推广自己,会给各种半导体厂商的评估板写好完整的工程程序,这些程序就放在Demo这个目录下,这部分Demo非常有参考价值。

Source文件夹

这里我们再重点分析下FreeRTOS/ Source文件夹下的文件,①和③包含的是FreeRTOS的通用的头文件和C文件,这两部分的文件试用于各种编译器和处理器,是通用的。需要移植的头文件和C文件放在②portblle这个文件夹。

portblle文件夹,是与编译器相关的文件夹,在不同的编译器中使用不同的支持文件。①中的KEIL就是我们就是我们使用的编译器,其实KEIL里面的内容跟RVDS里面的内容一样,所以我们只需要③RVDS文件夹里面的内容即可,里面包含了各种处理器相关的文件夹,从文件夹的名字我们就非常熟悉了,我们学习的STM32有M0、M3、M4等各种系列,FreeRTOS是一个软件,单片机是一个硬件,FreeRTOS要想运行在一个单片机上面,它们就必须关联在一起。MemMang文件夹下存放的是跟内存管理相关的源文件。

使用简单基本STM32工程

要移植 FreeRTOS,肯定需要一个基础工程,基础工程越简单越好,这里我使用的是STM32F103RET6的板子,例程则选择最基础的OLED实验来作为基础工程
复制 stm32f103vet6_OLED_I2C 到工程位置即可

移植过程参考一(这个测试)

注意:这个方法是跑不起来,进不去任务,是因为配置文件不对,在使用了后边方法修改的配置文件后才实现!

2.1、添加FreeRTOS 源码

在基础工程中新建一个名为 FreeRTOS 的文件夹,如图所示

创建 FreeRTOS 文件夹以后就可以将 FreeRTOS 的源码添加到这个文件夹中,打开刚刚下载的FreeRTOS源码,找到路径为:FreeRTOSv202111.00\FreeRTOS\Source下的文件

将所有.c文件以及include、portable两个文件夹复制到基础工程下刚刚创建的FreeRTOS 文件夹中,如下图所示:

复制完成之后打开portable文件夹,我们只需要留下 keil、MemMang 和 RVDS 这三个文件夹,其他的都可以删除掉,删除完成之后如下图所示

接着打开FreeRTOS源码,找到路径为:FreeRTOSv202111.00\FreeRTOS\Demo\CORTEX_STM32F103_Keil下的 FreeRTOSConfig.h文件,将其复制到基础工程路径为:实验1 LED灯闪烁\FreeRTOS\include下,如下图所示:

2.2、向工程分组中添加文件

打开基础工程,新建分组 FreeRTOS_CORE 和 FreeRTOS_PORTABLE

接着往这两个分组里添加文件,FreeRTOS_CORE 分组添加路径为:实验1 LED灯闪烁\FreeRTOS下的所有.c文件

FreeRTOS_PORTABLE分组添加路径为:实验1 LED灯闪烁\FreeRTOS\portable\MemMang下的heap_4.c文件以及路径为:实验1 LED灯闪烁\FreeRTOS\portable\RVDS\ARM_CM3下的port.c文件

添加完成如下图所示:

2.3、添加相对应的头文件路径

添加完 FreeRTOS 源码中的 C 文件以后还要添加 FreeRTOS 源码的头文件路径,头文件路径如下图所示:

2.4、修改BasicSYSTEM文件

SYSTEM 文件夹里面的文件一开始是针对UCOS 而编写的,所以如果使用 FreeRTOS 的话 就需要做相应的修改。本来打算让 SYSTEM 文件夹也支持 FreeRTOS,但是这样的话会导致 SYSTEM 里面的文件太过于复杂,这样非常不利于初学者学习,所以这里就专门针对 FreeRTOS 修改了 SYSTEM 里面的文件。
1、修改 sys.h 文件 sys.h 文件修改很简单,在 sys.h 文件里面用宏 SYSTEM_SUPPORT_OS 来定义是否使用 OS, 我们使用了 FreeRTOS,所以应该将宏 SYSTEM_SUPPORT_OS 改为 1。

1
2
3
//0,不支持 os
//1,支持 os
#define SYSTEM_SUPPORT_OS 1 //定义系统文件夹是否支持OS

2、修改 usart.c 文件 usart.c 文件修改也很简单,usart.c 文件有两部分要修改,一个是添加 FreeRTOS.h 头文件, 默认是添加的 UCOS 中的 includes.h 头文件,修改以后如下:

1
2
3
//0,不支持 os
//1,支持 os
#define SYSTEM_SUPPORT_OS 1 //定义系统文件夹是否支持OS

另外一个就是 USART1 的中断服务函数,在使用 UCOS 的时候进出中断的时候需要添加 OSIntEnter()和 OSIntExit(),使用 FreeRTOS 的话就不需要了,所以将这两行代码删除掉,修改以后如下:
维持了原有,因为用不到

3、修改 delay.c 文件 delay.c 文件修改的就比较大了,因为涉及到 FreeRTOS 的系统时钟,delay.c 文件里面有 4 个函数,先来看一下函数 SysTick_Handler(),此函数是滴答定时器的中断服务函数,代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//如果使用 os,则包括下面的头文件即可.
#if SYSTEM_SUPPORT_OS
#include "FreeRTOS.h" // os 使用
#endif

#include "task.h"
#include "event_groups.h"

extern void xPortSysTickHandler(void);
//systick 中断服务函数,使用 os 时用到
void SysTick_Handler(void)
{
if(xTaskGetSchedulerState()!=taskSCHEDULER_NOT_STARTED)//系统已经运行
{
xPortSysTickHandler();
}
}

FreeRTOS 的心跳就是由滴答定时器产生的,根据 FreeRTOS 的系统时钟节拍设置好滴答定 时器的周期,这样就会周期触发滴答定时器中断了。在滴答定时器中断服务函数中调用 FreeRTOS 的API函数 xPortSysTickHandler()。 delay_init()是用来初始化滴答定时器和延时函数,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//初始化延迟函数 
//SYSTICK 的时钟固定为 AHB 时钟,基础例程里面 SYSTICK 的时钟固定为HCLK时钟的1/8
//这里为了兼容FreeTROS,所以将 SYSTICK 的时钟频率改为 AHB 的频率
//SYSCLK:系统时钟频率
void delay_init()
{
u32 reload;
SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK); //选择外部时钟 HCLK
fac_us=SystemCoreClock/1000000; //不论是否使用 OS,fac_us 都需要使用
reload=SystemCoreClock/1000000; //每秒钟的计数次数 单位为 M
reload*=1000000/configTICK_RATE_HZ;//根据 configTICK_BATE_HZ 设定溢出时间
//时间 reload 为 24 位寄存器,最大值:
//16777216,在 72M 下,约合 0.233s 左右
fac_ms=1000/configTICK_RATE_HZ; //代表 os 可以延时的最少单位
SysTick->CTRL|=SysTick_CTRL_TICKINT_Msk; //开启SYSTICK中断
SysTick->LOAD=reload; //每1/configTICK_BATE_HZ 秒中断一次
SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk; //开启SYSTICK
}

前面我们说了 FreeRTOS 的系统时钟是由滴答定时器提供的,那么肯定要根据 FreeRTOS 的 系统时钟节拍来初始化滴答定时器了,delay_init()就是来完成这个功能的。FreeRTOS 的系统时 钟节拍由宏 configTICK_RATE_HZ 来设置,这个值我们可以自由设置,但是一旦设置好以后我 们就要根据这个值来初始化滴答定时器,其实就是设置滴答定时器的中断周期。在基础例程中 滴答定时器的时钟频率设置的是 AHB 的 1/8,这里为了兼容 FreeRTOS 将滴答定时器的时钟频 率改为了 AHB,也就是 72MHz!这一点一定要注意! 接下来的三个函数都是延时的,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
//延时 nus
//nus:为要延时的 us 数.
//nus:0~204522252(最大值即 2^32/fac_us@fac_us=168)
void delay_us(u32 nus)
{
u32 ticks;
u32 told,tnow,tcnt=0;
u32 reload=SysTick->LOAD; //LOAD的值
ticks=nus*fac_us; //需要的节拍数
// tcnt=0;
told=SysTick->VAL; //刚进入时的计数器值
while(1)
{
tnow=SysTick->VAL;
if(tnow!=told)
{
//这里注意一下 SYSTICK 是一个递减的计数器就可以了.
if(tnow<told)tcnt+=told-tnow;
else tcnt+=reload-tnow+told;
told=tnow;
if(tcnt>=ticks)break;//时间超过/等于要延迟的时间,则退出.
}
};
}

//延时 nms,会引起任务调度
//nms:要延时的 ms 数
//nms:0~65535
void delay_ms(u16 nms)
{
if(xTaskGetSchedulerState()!=taskSCHEDULER_NOT_STARTED)//系统已经运行
{
if(nms>=fac_ms)//延时的时间大于 os 的最少时间周期
{
vTaskDelay(nms/fac_ms);//FreeRTOS 延时
}
nms%=fac_ms; //os 已经无法提供这么小的延时了,
//采用普通方式延时
}
delay_us((u32)(nms*1000)); //普通方式延时,此时ucos无法启动调度.
}


//延时 nms,不会引起任务调度
//nms:要延时的 ms 数
void delay_xms(u32 nms)
{
u32 i;
for(i=0;i<nms;i++) delay_us(1000);
}

delay_us()是 us 级延时函数,delay_ms 和 delay_xms()都是 ms 级的延时函数,delay_us()和 delay_xms()不会导致任务切换。delay_ms()其实就是对 FreeRTOS 中的延时函数 vTaskDelay()的 简单封装,所以在使用 delay_ms()的时候就会导致任务切换。 delay.c 修改完成以后编译一下,会提示如下图所示错误:

上图的错误提示表示在 port.c、delay.c 和 stm32f10x_it.c 中三个重复定义的函数: SysTick_Handler()、SVC_Handler()和 PendSV_Handler(),这三个函数分别为滴答定时器中断服 务函数、SVC 中断服务函数和 PendSV 中断服务函数,将 stm32f10x_it.c 中的三个函数屏蔽掉, 如下图所示:

再次编译代码,应该没有错误了,如果还是错误的话自行根据错误类型修改!至此,SYSTEM 文件夹就修改完成了,接下来我们进行跑马灯测试,测试代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "oled_bmp.h"
#include "oled_font.h"
#include "oled.h"

#include "led.h"
#include "FreeRTOS.h"
#include "task.h"

//任务优先级
#define START_TASK_PRIO 1
//任务堆栈大小
#define START_STK_SIZE 128
//任务句柄
TaskHandle_t StartTask_Handler;
//任务函数
void start_task(void *pvParameters);

//任务优先级
#define LED0_TASK_PRIO 2
//任务堆栈大小
#define LED0_STK_SIZE 50
//任务句柄
TaskHandle_t LED0Task_Handler;
//任务函数
void led0_task(void *pvParameters);

//任务优先级
#define LED1_TASK_PRIO 3
//任务堆栈大小
#define LED1_STK_SIZE 50
//任务句柄
TaskHandle_t LED1Task_Handler;
//任务函数
void led1_task(void *pvParameters);





int main(void)
{
//// delay_init();
// OLED_Init();
////
// //OLED_ShowString(0,0,"ABCDEFG",8);
// //OLED_ShowString(0,1,"ABCDEFG",8);
//
// OLED_ShowString(0,0,"ABCDEFG",16);
// OLED_ShowString(0,2,"这一切足够了",16);
//
// OLED_Draw12864BMP(BMP1);
// while(1)
// {
//
// }

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4
delay_init(); //延时函数初始化
// uart_init(115200); //初始化串口
LED_Init();

//创建开始任务
xTaskCreate((TaskFunction_t )start_task, //任务函数
(const char* )"start_task", //任务名称
(uint16_t )START_STK_SIZE, //任务堆栈大小
(void* )NULL, //传递给任务函数的参数
(UBaseType_t )START_TASK_PRIO, //任务优先级
(TaskHandle_t* )&StartTask_Handler); //任务句柄
vTaskStartScheduler(); //开启任务调度
// while(1);
// LED_Init();
}


//开始任务任务函数
void start_task(void *pvParameters)
{
taskENTER_CRITICAL(); //进入临界区
//创建LED0任务
xTaskCreate((TaskFunction_t )led0_task, //任务函数
(const char* )"led0_task", //任务名称
(uint16_t )LED0_STK_SIZE, //任务堆栈大小
(void* )NULL, //传递给任务函数的参数
(UBaseType_t )LED0_TASK_PRIO, //任务优先级
(TaskHandle_t* )&LED0Task_Handler); //任务句柄

//创建LED0任务
xTaskCreate((TaskFunction_t )led1_task, //任务函数
(const char* )"led1_task", //任务名称
(uint16_t )LED1_STK_SIZE, //任务堆栈大小
(void* )NULL, //传递给任务函数的参数
(UBaseType_t )LED1_TASK_PRIO, //任务优先级
(TaskHandle_t* )&LED1Task_Handler); //任务句柄
vTaskDelete(StartTask_Handler);//删除开始任务
taskEXIT_CRITICAL(); //退出临界区
}

//LED0任务函数
void led0_task(void *pvParameters)
{
vTaskDelay(800);
while(1)
{
LED0=~LED0;
// GPIO_SetBits(GPIOA,GPIO_Pin_8);
vTaskDelay(500);
// GPIO_ResetBits(GPIOA,GPIO_Pin_8);
// vTaskDelay(500);
}
}

//LED1任务函数
void led1_task(void *pvParameters)
{
vTaskDelay(800);
while(1)
{
LED1=0;
vTaskDelay(200);
LED1=1;
vTaskDelay(800);
}
}

测试代码中创建了 3 个任务:LED0 测试任务、LED1 测试任务和浮点测试任务,它们的任务函数分别为:led0_task()、led1_task()。led0_task()和 led1_task()任务很简单,就是让 LED0 和 LED1 周期性闪烁
编译并下载代码到 STM32F103VET6 开发板中,下载进去以后会看到 LED0 和 LED1 开始闪烁, LED0 均匀闪烁,那是因为我们在 LED0 的任务代码中设置好的 LED0 亮 500ms,灭 500ms。 LED1 亮的时间短,灭的时间长,这是因为在 LED1 的任务代码中设置好的亮 200ms,灭 800ms。

设置配置文件

到这里还是没能跑起来,还需要参考后边设置的 FreeRTOSConfig.h 后才能工作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
/*
* FreeRTOS V202112.00
* Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* http://www.FreeRTOS.org
* http://aws.amazon.com/freertos
*
* 1 tab == 4 spaces!
*/

#ifndef FREERTOS_CONFIG_H
#define FREERTOS_CONFIG_H


#include "stm32f10x.h"

/*-----------------------------------------------------------
* Application specific definitions.
*
* These definitions should be adjusted for your particular hardware and
* application requirements.
*
* THESE PARAMETERS ARE DESCRIBED WITHIN THE 'CONFIGURATION' SECTION OF THE
* FreeRTOS API DOCUMENTATION AVAILABLE ON THE FreeRTOS.org WEB SITE.
*
* See http://www.freertos.org/a00110.html
*----------------------------------------------------------*/
/************************************************************************
* FreeRTOS基础配置配置选项
*********************************************************************/
/* 置1:RTOS使用抢占式调度器;置0:RTOS使用协作式调度器(时间片)
*
* 注:在多任务管理机制上,操作系统可以分为抢占式和协作式两种。
* 协作式操作系统是任务主动释放CPU后,切换到下一个任务。
* 任务切换的时机完全取决于正在运行的任务。
*/
#define configUSE_PREEMPTION 1
//1使能时间片调度(默认式使能的)
#define configUSE_TIME_SLICING 1
/* 某些运行FreeRTOS的硬件有两种方法选择下一个要执行的任务:
* 通用方法和特定于硬件的方法(以下简称“特殊方法”)。
*
* 通用方法:
* 1.configUSE_PORT_OPTIMISED_TASK_SELECTION 为 0 或者硬件不支持这种特殊方法。
* 2.可以用于所有FreeRTOS支持的硬件
* 3.完全用C实现,效率略低于特殊方法。
* 4.不强制要求限制最大可用优先级数目
* 特殊方法:
* 1.必须将configUSE_PORT_OPTIMISED_TASK_SELECTION设置为1。
* 2.依赖一个或多个特定架构的汇编指令(一般是类似计算前导零[CLZ]指令)。
* 3.比通用方法更高效
* 4.一般强制限定最大可用优先级数目为32
* 一般是硬件计算前导零指令,如果所使用的,MCU没有这些硬件指令的话此宏应该设置为0!
*/
#define configUSE_PORT_OPTIMISED_TASK_SELECTION 1

/* 置1:使能低功耗tickless模式;置0:保持系统节拍(tick)中断一直运行
* 假设开启低功耗的话可能会导致下载出现问题,因为程序在睡眠中,可用以下办法解决
*
* 下载方法:
* 1.将开发版正常连接好
* 2.按住复位按键,点击下载瞬间松开复位按键
*
* 1.通过跳线帽将 BOOT 0 接高电平(3.3V)
* 2.重新上电,下载
*
* 1.使用FlyMcu擦除一下芯片,然后进行下载
* STMISP -> 清除芯片(z)
*/
#define configUSE_TICKLESS_IDLE 0

/*
* 写入实际的CPU内核时钟频率,也就是CPU指令执行频率,通常称为Fclk
* Fclk为供给CPU内核的时钟信号,我们所说的cpu主频为 XX MHz,
* 就是指的这个时钟信号,相应的,1/Fclk即为cpu时钟周期;
*/
#define configCPU_CLOCK_HZ (SystemCoreClock)

//RTOS系统节拍中断的频率。即一秒中断的次数,每次中断RTOS都会进行任务调度
#define configTICK_RATE_HZ (( TickType_t )1000)

//可使用的最大优先级
#define configMAX_PRIORITIES (32)

//空闲任务使用的堆栈大小
#define configMINIMAL_STACK_SIZE ((unsigned short)128)

//任务名字字符串长度
#define configMAX_TASK_NAME_LEN (16)

//系统节拍计数器变量数据类型,1表示为16位无符号整形,0表示为32位无符号整形
#define configUSE_16_BIT_TICKS 0

//空闲任务放弃CPU使用权给其他同优先级的用户任务
#define configIDLE_SHOULD_YIELD 1

//启用队列
#define configUSE_QUEUE_SETS 1

//开启任务通知功能,默认开启
#define configUSE_TASK_NOTIFICATIONS 1

//使用互斥信号量
#define configUSE_MUTEXES 1

//使用递归互斥信号量
#define configUSE_RECURSIVE_MUTEXES 1

//为1时使用计数信号量
#define configUSE_COUNTING_SEMAPHORES 1

/* 设置可以注册的信号量和消息队列个数 */
#define configQUEUE_REGISTRY_SIZE 10

#define configUSE_APPLICATION_TASK_TAG 0

/*****************************************************************
FreeRTOS与内存申请有关配置选项
*****************************************************************/
//支持动态内存申请
#define configSUPPORT_DYNAMIC_ALLOCATION 1
//支持静态内存
#define configSUPPORT_STATIC_ALLOCATION 0
//系统所有总的堆大小
#define configTOTAL_HEAP_SIZE ((size_t)(36*1024))


/***************************************************************
FreeRTOS与钩子函数有关的配置选项
**************************************************************/
/* 置1:使用空闲钩子(Idle Hook类似于回调函数);置0:忽略空闲钩子
*
* 空闲任务钩子是一个函数,这个函数由用户来实现,
* FreeRTOS规定了函数的名字和参数:void vApplicationIdleHook(void ),
* 这个函数在每个空闲任务周期都会被调用
* 对于已经删除的RTOS任务,空闲任务可以释放分配给它们的堆栈内存。
* 因此必须保证空闲任务可以被CPU执行
* 使用空闲钩子函数设置CPU进入省电模式是很常见的
* 不可以调用会引起空闲任务阻塞的API函数
*/
#define configUSE_IDLE_HOOK 0

/* 置1:使用时间片钩子(Tick Hook);置0:忽略时间片钩子
*
*
* 时间片钩子是一个函数,这个函数由用户来实现,
* FreeRTOS规定了函数的名字和参数:void vApplicationTickHook(void )
* 时间片中断可以周期性的调用
* 函数必须非常短小,不能大量使用堆栈,
* 不能调用以”FromISR" 或 "FROM_ISR”结尾的API函数
*/
/*xTaskIncrementTick函数是在xPortSysTickHandler中断函数中被调用的。因此,vApplicationTickHook()函数执行的时间必须很短才行*/
#define configUSE_TICK_HOOK 0

//使用内存申请失败钩子函数
#define configUSE_MALLOC_FAILED_HOOK 0

/*
* 大于0时启用堆栈溢出检测功能,如果使用此功能
* 用户必须提供一个栈溢出钩子函数,如果使用的话
* 此值可以为1或者2,因为有两种栈溢出检测方法 */
#define configCHECK_FOR_STACK_OVERFLOW 0

/********************************************************************
FreeRTOS与运行时间和任务状态收集有关的配置选项
**********************************************************************/
//启用运行时间统计功能
#define configGENERATE_RUN_TIME_STATS 0
//启用可视化跟踪调试
#define configUSE_TRACE_FACILITY 0
/* 与宏configUSE_TRACE_FACILITY同时为1时会编译下面3个函数
* prvWriteNameToBuffer()
* vTaskList(),
* vTaskGetRunTimeStats()
*/
#define configUSE_STATS_FORMATTING_FUNCTIONS 1

/********************************************************************
FreeRTOS与协程有关的配置选项
*********************************************************************/
//启用协程,启用协程以后必须添加文件croutine.c
#define configUSE_CO_ROUTINES 0
//协程的有效优先级数目
#define configMAX_CO_ROUTINE_PRIORITIES ( 2 )

/***********************************************************************
FreeRTOS与软件定时器有关的配置选项
**********************************************************************/
//启用软件定时器
#define configUSE_TIMERS 1
//软件定时器优先级
#define configTIMER_TASK_PRIORITY (configMAX_PRIORITIES-1)
//软件定时器队列长度
#define configTIMER_QUEUE_LENGTH 10
//软件定时器任务堆栈大小
#define configTIMER_TASK_STACK_DEPTH (configMINIMAL_STACK_SIZE*2)

/************************************************************
FreeRTOS可选函数配置选项
************************************************************/
#define INCLUDE_xTaskGetSchedulerState 1
#define INCLUDE_vTaskPrioritySet 1
#define INCLUDE_uxTaskPriorityGet 1
#define INCLUDE_vTaskDelete 1
#define INCLUDE_vTaskCleanUpResources 1
#define INCLUDE_vTaskSuspend 1
#define INCLUDE_vTaskDelayUntil 1
#define INCLUDE_vTaskDelay 1
#define INCLUDE_eTaskGetState 1
#define INCLUDE_xTimerPendFunctionCall 1
//#define INCLUDE_xTaskGetCurrentTaskHandle 1
//#define INCLUDE_uxTaskGetStackHighWaterMark 0
//#define INCLUDE_xTaskGetIdleTaskHandle 0


/******************************************************************
FreeRTOS与中断有关的配置选项
******************************************************************/
#ifdef __NVIC_PRIO_BITS
#define configPRIO_BITS __NVIC_PRIO_BITS
#else
#define configPRIO_BITS 4
#endif
//中断最低优先级
#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 15

//系统可管理的最高中断优先级
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 5

#define configKERNEL_INTERRUPT_PRIORITY ( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) ) /* 240 */

#define configMAX_SYSCALL_INTERRUPT_PRIORITY ( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )

/****************************************************************
FreeRTOS与中断服务函数有关的配置选项
****************************************************************/
#define xPortPendSVHandler PendSV_Handler
#define vPortSVCHandler SVC_Handler


/* 以下为使用Percepio Tracealyzer需要的东西,不需要时将 configUSE_TRACE_FACILITY 定义为 0 */
#if ( configUSE_TRACE_FACILITY == 1 )
#include "trcRecorder.h"
#define INCLUDE_xTaskGetCurrentTaskHandle 1 // 启用一个可选函数(该函数被 Trace源码使用,默认该值为0 表示不用)
#endif





//#define configUSE_IDLE_HOOK 0
//#define configUSE_TICK_HOOK 0
//#define configCPU_CLOCK_HZ ( ( unsigned long ) 72000000 )
//#define configTICK_RATE_HZ ( ( TickType_t ) 1000 )
//#define configMAX_PRIORITIES ( 5 )
//#define configMINIMAL_STACK_SIZE ( ( unsigned short ) 128 )
//#define configTOTAL_HEAP_SIZE ( ( size_t ) ( 17 * 1024 ) )
//#define configMAX_TASK_NAME_LEN ( 16 )
//#define configUSE_TRACE_FACILITY 0
//#define configUSE_16_BIT_TICKS 0
//#define configIDLE_SHOULD_YIELD 1

/* Co-routine definitions. */
//#define configUSE_CO_ROUTINES 0
//#define configMAX_CO_ROUTINE_PRIORITIES ( 2 )

/* Set the following definitions to 1 to include the API function, or zero
to exclude the API function. */

//#define INCLUDE_vTaskPrioritySet 1
//#define INCLUDE_uxTaskPriorityGet 1
//#define INCLUDE_vTaskDelete 1
//#define INCLUDE_vTaskCleanUpResources 0
//#define INCLUDE_vTaskSuspend 1
//#define INCLUDE_vTaskDelayUntil 1
//#define INCLUDE_vTaskDelay 1

//#define INCLUDE_xTaskGetSchedulerState 1
//#define INCLUDE_xTaskGetCurrentTaskHandle 1

/* This is the raw value as per the Cortex-M3 NVIC. Values can be 255
(lowest) to 0 (1?) (highest). */
//#define configKERNEL_INTERRUPT_PRIORITY 255
/* !!!! configMAX_SYSCALL_INTERRUPT_PRIORITY must not be set to zero !!!!
See http://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html. */
//#define configMAX_SYSCALL_INTERRUPT_PRIORITY 191 /* equivalent to 0xb0, or priority 11. */


/* This is the value being used as per the ST library which permits 16
priority values, 0 to 15. This must correspond to the
configKERNEL_INTERRUPT_PRIORITY setting. Here 15 corresponds to the lowest
NVIC value of 255. */
#define configLIBRARY_KERNEL_INTERRUPT_PRIORITY 15

#endif /* FREERTOS_CONFIG_H */

移植过程参考二(超详细说明)

提取源码

  1. 首先在我们的STM32裸机工程模板根目录下新建一个文件夹,命名为“FreeRTOS”,并且在FreeRTOS文件夹下新建两个空文件夹,分别命名为“src”与“port”,src文件夹用于保存FreeRTOS中的核心源文件,也就是我们常说的‘.c文件’,port文件夹用于保存内存管理以及处理器架构相关代码,这些代码FreeRTOS官方已经提供给我们的,直接使用即可,在前面已经说了,FreeRTOS是软件,我们的开发版是硬件,软硬件必须有桥梁来连接,这些与处理器架构相关的代码,可以称之为RTOS硬件接口层,它们位于FreeRTOS/Source/Portable文件夹下。
  2. 打开FreeRTOS V9.0.0源码,在“FreeRTOSv9.0.0\FreeRTOS\Source”目录下找到所有的‘.c文件’,将它们拷贝到我们新建的src文件夹中,

在这里插入图片描述

  1. 打开FreeRTOS V9.0.0源码,在“FreeRTOSv9.0.0\FreeRTOS\Source\portable”目录下找到“MemMang”文件夹与“RVDS”文件夹,将它们拷贝到我们新建的port文件夹中

在这里插入图片描述

  1. 打开FreeRTOS V9.0.0源码,在“FreeRTOSv9.0.0\ FreeRTOS\Source”目录下找到“include”文件夹,它是我们需要用到FreeRTOS的一些头文件,将它直接拷贝到我们新建的FreeRTOS文件夹中,完成这一步之后就可以看到我们新建的FreeRTOS文件夹已经有3个文件夹,这3个文件夹就包含FreeRTOS的核心文件,至此,FreeRTOS的源码就提取完成。

在这里插入图片描述

添加到工程

添加FreeRTOSConfig.h文件
FreeRTOSConfig.h文件是FreeRTOS的工程配置文件,因为FreeRTOS是可以裁剪的实时操作内核,应用于不同的处理器平台,用户可以通过修改这个FreeRTOS内核的配置头文件来裁剪FreeRTOS的功能,所以我们把它拷贝一份放在user这个文件夹下面。
打开FreeRTOSv9.0.0源码,在“FreeRTOSv9.0.0\FreeRTOS\Demo”文件夹下面找到“CORTEX_STM32F103_Keil”这个文件夹,双击打开,在其根目录下找到这个“FreeRTOSConfig.h”文件,然后拷贝到我们工程的user文件夹下即可,等下我们需要对这个文件进行修改。

创建工程分组
接下来我们在mdk里面新建FreeRTOS/src和FreeRTOS/port两个组文件夹,其中FreeRTOS/src用于存放src文件夹的内容,FreeRTOS/port用于存放port\MemMang文件夹 与port\RVDS\ARM_CM3文件夹的内容。
然后我们将工程文件中FreeRTOS的内容添加到工程中去,按照已经新建的分组添加我们的FreeRTOS工程源码。
在FreeRTOS/port分组中添加MemMang文件夹中的文件只需选择其中一个即可,我们选择“heap_4.c”,这是FreeRTOS的一个内存管理源码文件。
添加完成后:

在这里插入图片描述

** 添加头文件路径**
FreeRTOS的源码已经添加到开发环境的组文件夹下面,编译的时候需要为这些源文件指定头文件的路径,不然编译会报错。FreeRTOS的源码里面只有FreeRTOS\include和FreeRTOS\port\RVDS\ARM_CM3这两个文件夹下面有头文件,只需要将这两个头文件的路径在开发环境里面指定即可。同时我们还将FreeRTOSConfig.h这个头文件拷贝到了工程根目录下的user文件夹下,所以user的路径也要加到开发环境里面。
在这里插入图片描述

修改FreeRTOSConfig.h

FreeRTOSConfig.h是直接从demo文件夹下面拷贝过来的,该头文件对裁剪整个FreeRTOS所需的功能的宏均做了定义,有些宏定义被使能,有些宏定义被失能,一开始我们只需要配置最简单的功能即可。要想随心所欲的配置FreeRTOS的功能,我们必须对这些宏定义的功能有所掌握,下面我们先简单的介绍下这些宏定义的含义,然后再对这些宏定义进行修改。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
#ifndef FREERTOS_CONFIG_H
#define FREERTOS_CONFIG_H

#include "stm32f10x.h"
#include "bsp_usart.h"


//针对不同的编译器调用不同的stdint.h文件
#if defined(__ICCARM__) || defined(__CC_ARM) || defined(__GNUC__)
#include <stdint.h>
extern uint32_t SystemCoreClock;
#endif

//断言
#define vAssertCalled(char,int) printf("Error:%s,%d\r\n",char,int)
#define configASSERT(x) if((x)==0) vAssertCalled(__FILE__,__LINE__)

/************************************************************************
* FreeRTOS基础配置配置选项
*********************************************************************/
/* 置1:RTOS使用抢占式调度器;置0:RTOS使用协作式调度器(时间片)
*
* 注:在多任务管理机制上,操作系统可以分为抢占式和协作式两种。
* 协作式操作系统是任务主动释放CPU后,切换到下一个任务。
* 任务切换的时机完全取决于正在运行的任务。
*/
#define configUSE_PREEMPTION 1

//1使能时间片调度(默认式使能的)
#define configUSE_TIME_SLICING 1

/* 某些运行FreeRTOS的硬件有两种方法选择下一个要执行的任务:
* 通用方法和特定于硬件的方法(以下简称“特殊方法”)。
*
* 通用方法:
* 1.configUSE_PORT_OPTIMISED_TASK_SELECTION 为 0 或者硬件不支持这种特殊方法。
* 2.可以用于所有FreeRTOS支持的硬件
* 3.完全用C实现,效率略低于特殊方法。
* 4.不强制要求限制最大可用优先级数目
* 特殊方法:
* 1.必须将configUSE_PORT_OPTIMISED_TASK_SELECTION设置为1。
* 2.依赖一个或多个特定架构的汇编指令(一般是类似计算前导零[CLZ]指令)。
* 3.比通用方法更高效
* 4.一般强制限定最大可用优先级数目为32
* 一般是硬件计算前导零指令,如果所使用的,MCU没有这些硬件指令的话此宏应该设置为0!
*/
#define configUSE_PORT_OPTIMISED_TASK_SELECTION 1

/* 置1:使能低功耗tickless模式;置0:保持系统节拍(tick)中断一直运行
* 假设开启低功耗的话可能会导致下载出现问题,因为程序在睡眠中,可用以下办法解决
*
* 下载方法:
* 1.将开发版正常连接好
* 2.按住复位按键,点击下载瞬间松开复位按键
*
* 1.通过跳线帽将 BOOT 0 接高电平(3.3V)
* 2.重新上电,下载
*
* 1.使用FlyMcu擦除一下芯片,然后进行下载
* STMISP -> 清除芯片(z)
*/
#define configUSE_TICKLESS_IDLE 0

/*
* 写入实际的CPU内核时钟频率,也就是CPU指令执行频率,通常称为Fclk
* Fclk为供给CPU内核的时钟信号,我们所说的cpu主频为 XX MHz,
* 就是指的这个时钟信号,相应的,1/Fclk即为cpu时钟周期;
*/
#define configCPU_CLOCK_HZ (SystemCoreClock)

//RTOS系统节拍中断的频率。即一秒中断的次数,每次中断RTOS都会进行任务调度
#define configTICK_RATE_HZ (( TickType_t )1000)

//可使用的最大优先级
#define configMAX_PRIORITIES (32)

//空闲任务使用的堆栈大小
#define configMINIMAL_STACK_SIZE ((unsigned short)128)

//任务名字字符串长度
#define configMAX_TASK_NAME_LEN (16)

//系统节拍计数器变量数据类型,1表示为16位无符号整形,0表示为32位无符号整形
#define configUSE_16_BIT_TICKS 0

//空闲任务放弃CPU使用权给其他同优先级的用户任务
#define configIDLE_SHOULD_YIELD 1

//启用队列
#define configUSE_QUEUE_SETS 1

//开启任务通知功能,默认开启
#define configUSE_TASK_NOTIFICATIONS 1

//使用互斥信号量
#define configUSE_MUTEXES 1

//使用递归互斥信号量
#define configUSE_RECURSIVE_MUTEXES 1

//为1时使用计数信号量
#define configUSE_COUNTING_SEMAPHORES 1

/* 设置可以注册的信号量和消息队列个数 */
#define configQUEUE_REGISTRY_SIZE 10

#define configUSE_APPLICATION_TASK_TAG 0


/*****************************************************************
FreeRTOS与内存申请有关配置选项
*****************************************************************/
//支持动态内存申请
#define configSUPPORT_DYNAMIC_ALLOCATION 1
//支持静态内存
#define configSUPPORT_STATIC_ALLOCATION 0
//系统所有总的堆大小
#define configTOTAL_HEAP_SIZE ((size_t)(36*1024))


/***************************************************************
FreeRTOS与钩子函数有关的配置选项
**************************************************************/
/* 置1:使用空闲钩子(Idle Hook类似于回调函数);置0:忽略空闲钩子
*
* 空闲任务钩子是一个函数,这个函数由用户来实现,
* FreeRTOS规定了函数的名字和参数:void vApplicationIdleHook(void ),
* 这个函数在每个空闲任务周期都会被调用
* 对于已经删除的RTOS任务,空闲任务可以释放分配给它们的堆栈内存。
* 因此必须保证空闲任务可以被CPU执行
* 使用空闲钩子函数设置CPU进入省电模式是很常见的
* 不可以调用会引起空闲任务阻塞的API函数
*/
#define configUSE_IDLE_HOOK 0

/* 置1:使用时间片钩子(Tick Hook);置0:忽略时间片钩子
*
*
* 时间片钩子是一个函数,这个函数由用户来实现,
* FreeRTOS规定了函数的名字和参数:void vApplicationTickHook(void )
* 时间片中断可以周期性的调用
* 函数必须非常短小,不能大量使用堆栈,
* 不能调用以”FromISR" 或 "FROM_ISR”结尾的API函数
*/
/*xTaskIncrementTick函数是在xPortSysTickHandler中断函数中被调用的。因此,vApplicationTickHook()函数执行的时间必须很短才行*/
#define configUSE_TICK_HOOK 0

//使用内存申请失败钩子函数
#define configUSE_MALLOC_FAILED_HOOK 0

/*
* 大于0时启用堆栈溢出检测功能,如果使用此功能
* 用户必须提供一个栈溢出钩子函数,如果使用的话
* 此值可以为1或者2,因为有两种栈溢出检测方法 */
#define configCHECK_FOR_STACK_OVERFLOW 0


/********************************************************************
FreeRTOS与运行时间和任务状态收集有关的配置选项
**********************************************************************/
//启用运行时间统计功能
#define configGENERATE_RUN_TIME_STATS 0
//启用可视化跟踪调试
#define configUSE_TRACE_FACILITY 0
/* 与宏configUSE_TRACE_FACILITY同时为1时会编译下面3个函数
* prvWriteNameToBuffer()
* vTaskList(),
* vTaskGetRunTimeStats()
*/
#define configUSE_STATS_FORMATTING_FUNCTIONS 1


/********************************************************************
FreeRTOS与协程有关的配置选项
*********************************************************************/
//启用协程,启用协程以后必须添加文件croutine.c
#define configUSE_CO_ROUTINES 0
//协程的有效优先级数目
#define configMAX_CO_ROUTINE_PRIORITIES ( 2 )


/***********************************************************************
FreeRTOS与软件定时器有关的配置选项
**********************************************************************/
//启用软件定时器
#define configUSE_TIMERS 1
//软件定时器优先级
#define configTIMER_TASK_PRIORITY (configMAX_PRIORITIES-1)
//软件定时器队列长度
#define configTIMER_QUEUE_LENGTH 10
//软件定时器任务堆栈大小
#define configTIMER_TASK_STACK_DEPTH (configMINIMAL_STACK_SIZE*2)

/************************************************************
FreeRTOS可选函数配置选项
************************************************************/
#define INCLUDE_xTaskGetSchedulerState 1
#define INCLUDE_vTaskPrioritySet 1
#define INCLUDE_uxTaskPriorityGet 1
#define INCLUDE_vTaskDelete 1
#define INCLUDE_vTaskCleanUpResources 1
#define INCLUDE_vTaskSuspend 1
#define INCLUDE_vTaskDelayUntil 1
#define INCLUDE_vTaskDelay 1
#define INCLUDE_eTaskGetState 1
#define INCLUDE_xTimerPendFunctionCall 1
//#define INCLUDE_xTaskGetCurrentTaskHandle 1
//#define INCLUDE_uxTaskGetStackHighWaterMark 0
//#define INCLUDE_xTaskGetIdleTaskHandle 0


/******************************************************************
FreeRTOS与中断有关的配置选项
******************************************************************/
#ifdef __NVIC_PRIO_BITS
#define configPRIO_BITS __NVIC_PRIO_BITS
#else
#define configPRIO_BITS 4
#endif
//中断最低优先级
#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 15

//系统可管理的最高中断优先级
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 5

#define configKERNEL_INTERRUPT_PRIORITY ( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) ) /* 240 */

#define configMAX_SYSCALL_INTERRUPT_PRIORITY ( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )


/****************************************************************
FreeRTOS与中断服务函数有关的配置选项
****************************************************************/
#define xPortPendSVHandler PendSV_Handler
#define vPortSVCHandler SVC_Handler


/* 以下为使用Percepio Tracealyzer需要的东西,不需要时将 configUSE_TRACE_FACILITY 定义为 0 */
#if ( configUSE_TRACE_FACILITY == 1 )
#include "trcRecorder.h"
#define INCLUDE_xTaskGetCurrentTaskHandle 1 // 启用一个可选函数(该函数被 Trace源码使用,默认该值为0 表示不用)
#endif


#endif /* FREERTOS_CONFIG_H */

修改stm32f10x_it.c

SysTick中断服务函数是一个非常重要的函数,FreeRTOS所有跟时间相关的事情都在里面处理,SysTick就是FreeRTOS的一个心跳时钟,驱动着FreeRTOS的运行,就像人的心跳一样,假如没有心跳,我们就相当于“死了”,同样的,FreeRTOS没有了心跳,那么它就会卡死在某个地方,不能进行任务调度,不能运行任何的东西,因此我们需要实现一个FreeRTOS的心跳时钟,FreeRTOS帮我们实现了SysTick的启动的配置:在port.c文件中已经实现vPortSetupTimerInterrupt()函数,并且FreeRTOS通用的SysTick中断服务函数也实现了:在port.c文件中已经实现xPortSysTickHandler()函数,所以移植的时候只需要我们在stm32f10x_it.c文件中实现我们对应(STM32)平台上的SysTick_Handler()函数即可。FreeRTOS为开发者考虑得特别多,PendSV_Handler()与SVC_Handler()这两个很重要的函数都帮我们实现了,在在port.c文件中已经实现xPortPendSVHandler()与vPortSVCHandler()函数,防止我们自己实现不了,那么在stm32f10x_it.c中就需要我们注释掉PendSV_Handler()与SVC_Handler()这两个函数了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//void SVC_Handler(void)
//{
//}

//void PendSV_Handler(void)
//{
//}

extern void xPortSysTickHandler(void);

//systick中断服务函数
void SysTick_Handler(void)
{
#if (INCLUDE_xTaskGetSchedulerState == 1 )
if (xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED)
{
#endif /* INCLUDE_xTaskGetSchedulerState */
xPortSysTickHandler();
#if (INCLUDE_xTaskGetSchedulerState == 1 )
}
#endif /* INCLUDE_xTaskGetSchedulerState */
}

创建任务

这里,我们创建一个单任务,任务使用的栈和任务控制块是在创建任务的时候FreeRTOS动态分配的。
任务必须是一个死循环,否则任务将通过LR返回,如果LR指向了非法的内存就会产生HardFault_Handler,而FreeRTOS指向一个死循环,那么任务返回之后就在死循环中执行,这样子的任务是不安全的,所以避免这种情况,任务一般都是死循环并且无返回值的。
并且每个任务循环主体中应该有阻塞任务的函数,否则就会饿死比它优先级更低的任务!!!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
/* FreeRTOS头文件 */
#include "FreeRTOS.h"
#include "task.h"
/* 开发板硬件bsp头文件 */
#include "bsp_led.h"

static void AppTaskCreate(void);/* AppTask任务 */

/* 创建任务句柄 */
static TaskHandle_t AppTask_Handle = NULL;

int main(void)
{
BaseType_t xReturn = pdPASS;/* 定义一个创建信息返回值,默认为pdPASS */

/* 开发板硬件初始化 */
BSP_Init();

/* 创建AppTaskCreate任务 */
xReturn = xTaskCreate((TaskFunction_t )AppTask, /* 任务入口函数 */
(const char* )"AppTask",/* 任务名字 */
(uint16_t )512, /* 任务栈大小 */
(void* )NULL,/* 任务入口函数参数 */
(UBaseType_t )1, /* 任务的优先级 */
(TaskHandle_t* )&AppTask_Handle);/* 任务控制块指针 */
/* 启动任务调度 */
if(pdPASS == xReturn)
vTaskStartScheduler(); /* 启动任务,开启调度 */
else
return -1;

while(1); /* 正常不会执行到这里 */
}

static void AppTask(void* parameter)
{
while (1)
{
LED1_ON;
vTaskDelay(500); /* 延时500个tick */
LED1_OFF;
vTaskDelay(500); /* 延时500个tick */
}
}


相关链接(侵删)

  1. 超详细的FreeRTOS移植全教程——基于srm32
  2. STM32F1+FreeRTOS系统移植例程
  3. https://blog.csdn.net/cs953575/article/details/118443722

=================我是分割线=================

欢迎到公众号来唠嗑: