1 下载源码
直接在官网下载或github下载,这里给出官网链接https://www.freertos.org/zh-cn-cmn-s/index.html
我这里下载的是最新版的。此外,还可以在官网下载参考手册和资料:https://www.freertos.org/zh-cn-cmn-s/FreeRTOS-quick-start-guide.html
下载完成后打开压缩包,内有三个文件夹:
FreeRTOS里是基础的源码和一些例程,FreeRTOS-Plus里是一些增加了其他功能,如TCP等的源码,这里我们只需要用到FreeRTOS里的文件。
在FreeRTOS里有四个文件夹:
对于移植freeRTOS,需要的文件夹只有两个:Source和Demo。Source里当然是最重要的源码;Demo里有各种架构、编译方式下的例程,感兴趣可以打开看看,例如我们的stm32f103:
2 开始移植
2.1 新建KEIL工程
使用模板新建一个keil工程,我这里直接使用的正点原子的模板工程:
2.2 在工程中添加FreeRTOS源码
1在工程下新建三个文件夹,如下图:
2将FreeRTOS文件夹下的Source->include文件夹下所有文件复制过来放入新建的include文件夹里;
Source文件夹内的7个.c文件放入src里;
Source->portable->RVDS下的ARM_CM3文件夹复制到port文件夹下。Source->portable下包含各类编译器所需要的port.c文件,这里我们是用keil编译,选择keil文件夹,但是我们通过文件夹里的.txt文件知道,keil环境所需的port.c与RVDS一样,所以进入RVDS文件夹,由于stm32f103采用的是arm cortex-m3所以需要的是ARM_CM3里的文件
Source->portable下的MemMang文件夹复制到port文件夹。MemMang文件夹里的几个.c是几种不同的内存管理方式,感兴趣的uu自己去了解,我们后面只用到heap_4.c一种。
现在我们还缺一个FreeRTOSConfig.h。这个文件是用来根据我们的平台以及需求对freeRTOS进行配置的。这里我们先使用Demo->CORTEX_STM32F103_KEIL里的FreeRTOSConfig.h,将其复制到项目工程的USER文件夹,后续我们会对它进行修改。
2.3 KEIL里的配置
打开keil工程文件,左侧AddGroup,新建FreeRTOS/src和FreeRTOS/port两个文件夹。将以下文件分别加入项目工程中的FreeRTOS/src、FreeRTOS/port、USER:
添加头文件路径:点击魔术棒按钮,选择C/C++,点击include path后的三个点:
增加刚刚有头文件的两个路径:include和port\ARM_CM3
这样工程的内容就算配置好了
2.4 修改代码内容
修改FreeRTOSConfig.h
关于FreeRTOSConfig.h这个文件每个宏定义的具体意义大家可以参考https://www.freertos.org/zh-cn-cmn-s/a00110.html#configUSE_STATS_FORMATTING_FUNCTIONS,我这里就不具体讲解了。
我们现在只需要在demo的基础上在FreeRTOSConfig.h里增加以下代码:
#define xPortPendSVHandler PendSV_Handler
#define vPortSVCHandler SVC_Handler
#define xPortSysTickHandler SysTick_Handler
修改stm32f10x_it.c
由于我们在freeRTOS中已经实现了PendSV_Handler、SVX_handler、SysTick_Handler,那我们现在就需要把stm32f10x_it.c里原来的这几个函数注释掉。注释完大家可以尝试编译一下,应该到现在是可以编译通过了的。
3 测试
目前为止我们就成功移植了FreeRTOS啦!是不是还挺简单!
那现在就写一个程序来测试一下吧!
下面是我的测试程序大家可以参考:
/* FreeRTOS头文件 */
#include "FreeRTOS.h"
#include "task.h"
#include "led.h"
static void AppTaskCreate(void);/* AppTask任务 */
/* 创建任务句柄 */
static TaskHandle_t AppTask_Handle1 = NULL;
static TaskHandle_t AppTask_Handle2 = NULL;
static void AppTask1(void* parameter)
{
while (1)
{
LED0=!LED0;
vTaskDelay(500); /* 延时500个tick */
}
}
static void AppTask2(void* parameter)
{
while (1)
{
LED1=!LED1;
vTaskDelay(1000); /* 延时500个tick */
}
}
int main(void)
{
BaseType_t xReturn = pdPASS;/* 定义一个创建信息返回值,默认为pdPASS */
/* 开发板硬件初始化 */
LED_Init();
/* 创建AppTaskCreate任务 */
xReturn = xTaskCreate((TaskFunction_t )AppTask1, /* 任务入口函数 */
(const char* )"AppTask1",/* 任务名字 */
(uint16_t )512, /* 任务栈大小 */
(void* )NULL,/* 任务入口函数参数 */
(UBaseType_t )1, /* 任务的优先级 */
(TaskHandle_t* )&AppTask_Handle1);/* 任务控制块指针 */
xReturn = xTaskCreate((TaskFunction_t )AppTask2, /* 任务入口函数 */
(const char* )"AppTask2",/* 任务名字 */
(uint16_t )512, /* 任务栈大小 */
(void* )NULL,/* 任务入口函数参数 */
(UBaseType_t )1, /* 任务的优先级 */
(TaskHandle_t* )&AppTask_Handle2);/* 任务控制块指针 */
/* 启动任务调度 */
if(pdPASS == xReturn)
vTaskStartScheduler(); /* 启动任务,开启调度 */
else
return -1;
while(1); /* 正常不会执行到这里 */
}
注意哦,这里我们的AppTask1和AppTask2里面都是死循环,然而两个任务都可以执行,看起来好像是“并发”的一样,这就是我们操作系统在成功切换调度任务啦!