文章目录
前言
FreeRTOS可以拆分成两个部分来看。Free:就是免费的;RTOS的全称是Real Operating System,中文名就是实时操作系统,所以FreeRTOS其实就是一个免费的实时类的操作系统。FreeRTOS是RTOS系统的一种,FreeRTOS十分的小巧,可以在资源有限的微控制器中运行,当然了 FreeRTOS不仅局限于在微控制器中使用。
本篇博客是STM32下基于FreeRTOS的多任务程序,使用到的板子是正点原子的STM32f103精英版。
FreeRTOS移植
tips:其实很多商家已经完成了FreeRTOS的移植工作,比如正点原子家的板子就直接有已经完成移植的代码。
这里博主也自己完成了一次FreeRTOS的移植。如果你也想自己实操一次,就可以跟着博主继续本内容;如果你想直接进入多任务程序,请下滑到下一个大标题中。^ _ ^
准备工作
首先需要下载FreeRTOS的源码
FreeRTOS的源码可以在官网下载。如果你买的是正点原子的板子,问商家给的资料里面应该也有FreeRTOS的源码(软件资料 ->14FreeRTOS学习资料 ->FreeRTOS源码)。
然后还需要一个Keil5的基础工程(越简单越好)
可以准备一个跑马灯的工程(在Keil5环境下),在此就不再赘述跑马灯的工程了。跑马灯程序打开的初始工程界面如下图所示:
移植
新建FreeRTOS的文件夹
在基础工程中新建一个名为 FreeRTOS的文件夹,在文件夹中添加我们需要的FreeRTOS源码。需要的文件都可以在FreeRTOS源码中找到,需要的文件如下图所示(其中,portable文件夹中,我们只需要 留下keil、MemMang和RVDS这三个文件夹,其他的都可以删除掉):
将所需要的文件复制到新建的文件夹中即可。
同时,工程目录中也需要添加新的文件夹和文件,所需要添加的如下图所示:
添加路径
打开准备好的基础工程,点击目标工程,添加头文件的路径,如下图所示:
点击Include Paths后面的三个点,选择所需要添加的路径,所添加的路径如下图所示:
注意:这些文件都是在本工程下的哦~不要添加路径添加到了从官网上下载的FreeRTOS源码中。
出现问题:若此时对程序进行编译是可以通过的,但是构建时就会出现错误,出现的错误提示是找不到“FreeRTOSConfig.h”头文件。
分析原因:这是由于后面添加的文件中调用了“FreeRTOSConfig.h”这个头文件,但是在添加的路径中找不到这个头文件。
解决问题:找遍了添加的文件中,发现是没有“FreeRTOSConfig.h”的。再在官网上面下载的源码中找到了“FreeRTOSConfig.h”文件。
添加配置文件
在FreeRTOS的源码路径中有“FreeRTOSConfig.h”文件,路径如下图所示:
然后复制文件,粘贴到基础工程中的FreeRTOS文件中去,如下图所示:
添加完成之后,再次构建就不会报错了。
这个“FreeRTOSConfig.h”头文件是一个配置文件,是直接在官网中下载的,可以直接复制粘贴使用;也可以根据自己的需求来进行配置。
tips:由于正点原子SYSTEM文件夹里面的文件一开始是针对UCOS而编写的,所以如果使用FreeRTOS的话就需要做相应的修改 。
正点原子提供的资料中“STM32F1 FreeRTOS开发手册”中有详细修改步骤,在这里不再赘述。(提取码:hahy)
完成以上操作就完成了FreeRTOS的移植。完成了FreeRTOS的移植之后,我们就来进行下一项。
多任务程序
正点原子提供的资料中有模板,在其中修改成为自己想要完成的程序即可。
开始任务
模板中有一个开始任务,设开始任务的优先级为1,其他任务依次。
//任务优先级
#define START_TASK_PRIO 1
//任务堆栈大小
#define START_STK_SIZE 128
//任务句柄
TaskHandle_t StartTask_Handler;
//任务函数
void start_task(void *pvParameters);
在主函数中
//创建开始任务
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(); //开启任务调度
从这个代码中就可以看出来,直接在开始任务函数中调用其他函数即可,就不用再将其他函数编写到主函数中。
在开始任务的基础上,其他任务只需要在此基础上添加三个地方:
①声明;②函数编写;③开始任务函数调用
以下面三个任务为例:
task1:每间隔500ms闪烁(变化)一次LED
声明
//任务优先级
#define LED0_TASK_PRIO 2
//任务堆栈大小
#define LED0_STK_SIZE 50
//任务句柄
TaskHandle_t LED0Task_Handler;
//任务函数
void led0_task(void *pvParameters);
函数编写
//LED0任务函数
void led0_task(void *pvParameters)
{
while(1)
{
LED0=~LED0;
printf("task1:LED\r\n");
vTaskDelay(500);
}
}
开始任务函数调用
//创建LED1任务
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);
task2:每间隔2000ms,向串口发送一次指令数据“HELLO WORLD!"
声明
//任务优先级
#define HELLO_TASK_PRIO 3
//任务堆栈大小
#define HELLO_STK_SIZE 50
//任务句柄
TaskHandle_t HELLOTask_Handler;
//任务函数
void HELLO_WORLD_task(void *pvParameters);
函数编写
void HELLO_WORLD_task(void *pvParameters)
{
while(1)
{
printf("task2:HELLO WORLD!\r\n");
vTaskDelay(2000);
}
}
开始任务函数调用
//创建HELLO_WORLD任务
xTaskCreate((TaskFunction_t )HELLO_WORLD_task,
(const char* )"HELLO_WORLD_task",
(uint16_t )HELLO_STK_SIZE,
(void* )NULL,
(UBaseType_t )HELLO_TASK_PRIO,
(TaskHandle_t* )&HELLOTask_Handler);
task3:每间隔5000ms,从AHT20采集一次温湿度数据(不考虑硬件情况,仅写出整个多任务框架模拟代码)。
声明
//任务优先级
#define AHT20_TASK_PRIO 3
//任务堆栈大小
#define AHT20_STK_SIZE 50
//任务句柄
TaskHandle_t AHT20Task_Handler;
//任务函数
void AHT20_task(void *pvParameters);
函数编写
void AHT20_task(void *pvParameters)
{
printf("task3:AHT20\r\n");
vTaskDelay(5000);
}
tips:由于无硬件支持,这里只是一个框架。
开始函数调用
//创建AHT20任务
xTaskCreate((TaskFunction_t )AHT20_task,
(const char* )"AHT20_task",
(uint16_t )AHT20_STK_SIZE,
(void* )NULL,
(UBaseType_t )AHT20_TASK_PRIO,
(TaskHandle_t* )&AHT20Task_Handler);
实验结果
LED灯闪烁,时间间隔500ms。
串口调试助手。博主使用的是正点原子官方提供的串口调试助手,在使用串口调试助手前,需要提前安装CH340驱动。在硬件上,需要用跳帽将PA9和PA10与RXD和TXD盖起来,才能在串口显示接收的数据,如下图所示:
总结
实操了一次移植,感觉棒棒哒~需要使用串口调试助手的话,需要提前安装驱动CH340,然后再使用串口调试助手。
- 完成移植 √
- 完成多任务程序 √