1. 前言
本文是基于FreeRTOSv9.0.0版本的实时系统,移植到STM32F103芯片平台上。移植环境使用的是MDK5.32版本,我在移植之前就已经构建好了一个裸机工程源码了,而且已经确保了这份裸机代码是没有问题的。开始移植之前我强烈建议一定要确认自己的裸机测试代码是没有问题的,我一开始没有确认裸机代码,然后移植的时候出现了bug,以为是移植FreeRTOS的原因,然后一直找移植过程中是不是出了问题,最后才发现原来是自己裸机测试代码本身的问题。
2. 源码获取
获取源码可以直接到FreeRTOS的官网获取,官网:https://www.freertos.org/ 。或者到https://sourceforge.net/projects/freertos/files/FreeRTOS/这个网站下载都可以。我已经下载了v9.0.0版本了。
下载后得到的文件其实就是一个类似压缩包的可执行程序,双击解压就会在本目录下得到FreeRTOS解压后的源码,如下图所示:
3. FreeRTOS目录结构
我们解压出来后,就得到了FreeRTOS的源码了,里面包含了所需要的全部源码资料。这里对FreeRTOSv9.0.0版本的源码目录结构简单介绍一下。解压出来后,目录结构如下图:
其中FreeRTOS这个目录才是我们要关心的,里面包含核心部分的代码。而FreeRTOS-Plus目录的内容主要是一些组件和演示例程我们移植过程中不用去管。下面我列一个目录结构说明一下,这样比较清晰,如下:
├─FreeRTOS
│ ├─Demo // 集成开发环境完整的Demo模板
│ ├─License // 许可证相关
│ └─Source // FreeRTOS的源码,非常重要,移植就是使用该目录里面的源码了
│ ├─include // 源码的头文件
│ └─portable // 不同硬件平台相关的代码
│ ├─MemMang // FreeRTOS内存管理方案,我们选择其中一个文件即可
│ │ heap_1.c
│ │ heap_2.c
│ │ heap_3.c
│ │ heap_4.c
│ │ heap_5.c
│ ├─RVDS // ARM架构相关的代码,我们在里面选择其中的CM3架构目录即可
│ └─其他 // 其他架构、开发平台相关的代码
│ croutine.c // 协线程(协程)文件,和任务类似,在系统资源比较缺乏下使用
│ event_groups.c // 事件标志组
│ list.c // 列表结构描述,在内核整体控制上都使用了列表格式数据处理,一切数据结构的基础
│ queue.c // 队列,任务和任务之间的通讯处理
│ tasks.c // 所有任务相关函数
│ timers.c // 软件定时器,以任务形式存在
│ stream_buffer.c // 流缓冲区文件,该文件是v10.0.0新增的,v9.0.0没有
└─FreeRTOS-Plus // FreeRTOS生态相关的文件,基本用不到
其中最核心的文件有两个:
- FreeRTOS/Source/tasks.c
- FreeRTOS/Source/list.c
其他的核心文件可以根据功能可选。
4. 移植过程
既然已经准备好了源码,而且对源码目录也有了一个大概的了解,那我们就可以开始移植了。
4.1 添加源码
我们在裸机驱动的工程目录下添加一个freeRTOS目录,里面把把v9.0.0版本的Source目录下的源码拷贝到该目录下,如下图:
上面也介绍过portable目录下是不同的硬件平台相关的代码。里面我们只保留 keil、MemMang 和 RVDS这三个文件夹即可,其他我们使用不到。
4.2 Keil工程里面添加源码
我们打开自己验证OK的裸机驱动工程,然后在Keil中加入刚刚的那些FreeRTOS源码,如下图所示:
其中heap_4.c文件就是FreeRTOS的内存管理文件,在MemMang 目录下的。而port.c文件是在RVDS/ARM_CM3目录下的一个文件。
添加完这些文件之后,我们记得在Keil工程里面添加上相应的头文件路径,不然会报错说找不到各种头文件。
4.3 编译解决错误
我们把FreeRTOS的源码添加进Keil工程里面后,就可以先编译看看能不能通过了。编译后发现错误如下:
F:\STM32_Study_20210528\freeRTOS_Template\freeRTOS\include\FreeRTOS.h(98): error: #5: cannot open source input file "FreeRTOSConfig.h": No such file or directory
#include "FreeRTOSConfig.h"
报错其实就是说找不到FreeRTOSConfig.h这个头文件,那我们就要去添加这个头文件了。该头文件是位于源码目录下的FreeRTOSv9.0.0\FreeRTOS\Demo\CORTEX_STM32F103_Keil这个目录里面了,我们在CORTEX_STM32F103_Keil这个目录下把FreeRTOSConfig.h头文件拷贝到我们工程目录的freeRTOS\include目录下就行,然后继续编译。
解决完头文件找不到的错误后,再次编译发现已经完全没有错误了。
5. 移植验证
移植完之后,我们创建一个简单的任务进行验证。我们就写一个串口发送任务,每隔1秒钟像串口发送一次数据。代码如下:
#include <stdio.h>
#include "bsp_usart.h"
#include "FreeRTOS.h"
#include "task.h"
TaskHandle_t StartTask_Handler; // 任务句柄
/* 任务函数 */
void start_task(void *pvParameters)
{
int i = 1;
while (1)
{
vTaskDelay(1000);
printf("i = %d\n", i++);
}
}
int main()
{
USART_Config();
printf("FreeRTOS test\n");
/* 创建任务 */
xTaskCreate((TaskFunction_t )start_task, // 任务函数
(const char* )"start_task", // 任务名称
(uint16_t )128, // 任务堆栈大小
(void* )NULL, // 传递给任务函数的参数
(UBaseType_t )1, // 任务优先级
(TaskHandle_t* )&StartTask_Handler); // 任务句柄
vTaskStartScheduler(); // 开启任务调度
}
编译可以正常通过,下载到芯片运行,然后查看串口输出,我们观察到串口输出只打印了"FreeRTOS test"这个字符串,如下:
其实原因就在于我们并没有使用系统提供的几个和任务调度有关的中断函数接口。其实FreeRTOS已经帮我们提供了vPortSVCHandler、xPortPendSVHandler、xPortSysTickHandler这几个和任务调度有关的中断服务函数了,我们必须使用这几个中断服务函数去替换启动文件中的SVC_Handler、PendSV_Handler、SysTick_Handler的中断服务函数,这样FreeRTOS才能正常开始任务调度。修改如下图所示:
我们修改后再次编译下载,就可以看到串口每隔1秒钟打印输出一次数据了,如下所示:
看到上面的打印,说明我们的任务函数时正常运行了的,也就是说我们移植成功了。