前言
- 芯片:GD32F450ZGT6
- 编译环境:keil5
- STM32或者其他芯片步骤基本一样。
- 其他编译器,只是添加文件和头文件路径等操作有些许区别,freertos内容一致。
freertos源码下载
官网链接:FreeRTOS - Market leading RTOS (Real Time Operating System) for embedded systems with Internet of Things extensions官网如果下载慢的话,可以从我上传的资源下载。
基础工程准备
准备自己所使用芯片的基础工程,配置好时钟即可,也可以有串口、按键等等,总之越简单越好。
我使用立创eda的梁山派开发板作为例子,所以使用了它的工程,包括时钟、串口等。
freertos移植
源码移植与裁剪
- 在工程内新建文件夹 freertos
- 将前文下载的freertos源码复制到新建的文件夹内,路径与复制内容如下图
- 裁剪源码,打开portable文件夹,只保留下图选中的MemMang和RVDS,其他文件全删除。使用IAR和GCC的,分别保留IAR和GCC文件夹,Keil文件夹打开后会发现指向了RVDS。我使用Keil,所以保留RVDS。
- MemMang中删除除了heap_4.c的全部文件
- RVDS中选择自己的芯片内核文件,我用的M4的芯片,所以删除除了ARM_CM4F之外的全部文件
- 打开工程,选择添加组,添加文件夹freertos
- 慢双击文件夹 可以修改目录中文件夹的名字
- 将裁剪后的freertos源码添加到工程
- MemMang与RVDS进去文件夹内,将heap_4.c和port.c一起添加进来,如下图
- 添加头文件路径,依次4部,选择freertos内的include文件夹和ARM_CM4F文件夹
- 添加配置文件,去下载好的freertos的源码的demo文件内,选择与自己芯片使用相同内核的Demo,我使用的M4的内核,选择了如下图选择的Demo
- 复制它们的FreeRTOSConfig.h 到我们工程的freertos-include内
- 到此,源码的移植与裁剪结束,之后在工程内完成配置,解决报错,即可使用。
freertos配置与编译错误解决
提前说明,不同版本的FreeRTOS、不同的系统,移植过程会有不同的报错,但是都有现成的解决资料。移植的时候需要根据实际情况修改,以下为我本次移植出现的问题,如果你出现了其他问题,直接搜索解决即可。
- 第一次编译工程,报错:..\freertos\portable\RVDS\ARM_CM4F\port.c(734): error: #20: identifier "SystemCoreClock" is undefined
- 解决步骤1:在FreeRTOSConfig.h中添加芯片头文件 #include "gd32f4xx.h"
- 解决步骤2:将下面代码替换工程内的代码
-
#if defined(__ICCARM__) || defined(__CC_ARM) || defined(__GNUC__) #include <stdint.h> extern uint32_t SystemCoreClock; #endif
- 第二次编译,报错消失,出现新的报错,这三个错误是因为这三个中断函数,freertos有自己的实现,去芯片的库内注释掉即可。
.\Objects\GD32F450.axf: Error: L6200E: Symbol SVC_Handler multiply defined (by port.o and gd32f4xx_it.o).
.\Objects\GD32F450.axf: Error: L6200E: Symbol PendSV_Handler multiply defined (by port.o and gd32f4xx_it.o).
.\Objects\GD32F450.axf: Error: L6200E: Symbol SysTick_Handler multiply defined (by port.o and gd32f4xx_it.o).
- 补充说明:关于SysTic_Handler()的配置,不同版本的FreeRTOS有所不同
- V10.4.4 SysTic_Handler()函数由FreeRTOS实现,FreeRTOSConfig.h中定义了其宏
xPortSysTickHandler的实现在port.c中,我们根据芯片选择的port.c和portmacro.h,所有FreeRTOS知道我们的MCU寄存器地址,它会根据我们FreeRTOSConfig.h配置的Tick频率以及系统时钟,去配置SysTick。
总结一下:比较新的一些版本的FreeRTOS中,我们只需要
1.正确选择移植文件
2.注释工程内的SysTic_Handler()函数
3.配置好FreeRTOSConfig.h的系统时钟和Tick频率
就可以完成操作系统的时钟配置
- V9.0.0 这个比较老的版本,在工程的SysTic_Handler()函数内调用xPortSysTickHandler()函数,因为FreeRTOSConfig.h没有SysTic_Handler()的宏,本质没什么区别。xPortSysTickHandler同样在port.c内实现,FreeRTOS同样会去配置系统时钟,我们也是只需要修改FreeRTOSConfig.h即可
- 第三次编译,以上报错消失,出现新的报错,如下。
-
.\Objects\GD32F450.axf: Error: L6218E: Undefined symbol vApplicationIdleHook (referred from tasks.o). .\Objects\GD32F450.axf: Error: L6218E: Undefined symbol vApplicationStackOverflowHook (referred from tasks.o). .\Objects\GD32F450.axf: Error: L6218E: Undefined symbol vApplicationTickHook (referred from tasks.o). .\Objects\GD32F450.axf: Error: L6218E: Undefined symbol vApplicationMallocFailedHook (referred from heap_4.o).
- 解决方法:以上功能在FreeRTOSConfig.h打开了,但是工程内并没有实现,直接去关掉即可,按照我下图实现。如何有需要,自行打开即可。
- 再编译,全部报错消失。
-
freertos验证
测试代码
使用freertos的文件需要引用头文件
#include "FreeRTOS.h"
#include "task.h"
在头文件中验证如下:
#include "gd32f4xx.h"
#include "systick.h"
#include <stdio.h>
#include <stdint.h>
#include "main.h"
#include "sys.h"
#include "lcd_169_spi.h"
#include "gd32f4xx_rcu.h"
#include "FreeRTOS.h"
#include "task.h"
static void Task1(void * pvParameters)
{
char task1Str[10] = {0};
int task1Count = 0;
lcd169_init();
lcd169_draw_rectangle_filled(0,0,LCD_X_PIXEL,LCD_Y_PIXEL,WHITE);
lcd169_sl_display_ascii_strings(60,10,SONG_24,"freertos测试",RED,WHITE);
while(1)
{
task1Count++;
sprintf(task1Str,"task1 : %d \r\n",task1Count);
lcd169_sl_display_ascii_strings(10,210,SONG_24,task1Str,RED,WHITE);
if(task1Count == 10000)
{
task1Count = 0;
}
vTaskDelay(1000);
}
}
static void Task2(void * pvParameters)
{
char task2Str[10] = {0};
int task2Count = 0;
while(1)
{
task2Count++;
sprintf(task2Str,"task2 : %d \r\n",task2Count);
lcd169_sl_display_ascii_strings(10,250,SONG_24,task2Str,RED,WHITE);
if(task2Count == 10000)
{
task2Count = 0;
}
vTaskDelay(1000);
}
}
static void TaskCreate()
{
TaskHandle_t xHandle = NULL;
TaskHandle_t xHandle2 = NULL;
xTaskCreate( Task1,
"Task1Name",
1000,
NULL,
1,
&xHandle
);
xTaskCreate( Task2,
"Task2Name",
1000,
NULL,
1,
&xHandle2
);
/* 启动调度器 */
vTaskStartScheduler();
}
int main(void)
{
systick_config();
TaskCreate();
while(1) {}
}
测试结果
两个任务分别累加计数,实验现象与预期一致,freertos移植成功。
移植验证可以使用屏幕、串口、led、GPIO等等都可以,或者直接使用keil内的逻辑分析仪 或者 IAR的 live watch,只需要能验证任务正常调度即可。