FreeRTOS移植
准备工作
基础工程
移植之前需要先准备一个 基本的可用的 GD32工程模板,用于后续的移植,如下所示:
该工程为通过串口 每隔 100ms 输出一个 Running 的工程模板
下载 FreeRTOS源码
FreeRTOS下载途径有很多, 可以通过 FreeRTOS官网 或 Github 下载到 FreeRTOS 内核源码,其目录结构如下:
.
|-- CMakeLists.txt
|-- GitHub-FreeRTOS-Kernel-Home.url
|-- History.txt
|-- LICENSE.md
|-- MISRA.md
|-- Quick_Start_Guide.url
|-- README.md
|-- croutine.c
|-- cspell.config.yaml
|-- event_groups.c
|-- include
|-- list.c
|-- manifest.yml
|-- portable
|-- queue.c
|-- sample_configuration
|-- stream_buffer.c
|-- tasks.c
`-- timers.c
其中,后续工程需要移植的主要包括:
*.c
文件: FreeRTOS 的内核源文件include
目录: 包含 FreeRTOS 源码相关头文件portable
目录: 包含 FreeRTOS 的一些组件和不同架构 CPU 的支持
另外还需要下载 Demo(Github可以下载到), 通过 Demo 目录中的内容来辅助移植
开始移植
添加代码
首先在工程中新建两个文件夹, 我这里分别命名为
- FreeRTOS: 包括 FreeRTOS 内核的源码
- FreeRTOS_Port: 包括 FreeRTOS 与 GD32 工程相关的接口文件
如下所示:
随后在 模板工程所在目录下添加一个名为 ThirdParty
的目录, 用于存放第三方源码, 如下所示:
将 FreeRTOS 中的源文件添加到工程中的 FreeRTOS 中, 如下所示:
另外还要添加 portable
目录下 接口 相关的文件, 先浏览 keil 目录, 发现只有个文件名为 See-also-the-RVDS-directory
…
那就跳转到 RVDS
目录下, 可以看到 FreeRTOS 不同 ARM 内核架构的支持,我移植的板子主控芯片为 GD32F103VCT6
,属于 Cortex-M3 内核
因此将该目录下的 port.c
和 portmacro.h
添加进工程中,我将其添加到了 FreeRTOS_Port 中, 此时还需要添加一个 FreeRTOS 内存管理 用到的源文件, 在 protable/MemMang
目录下,我选择添加 heap4.c
,其中各个文件的具体区别可自行搜索, 代码添加完成后如下所示:
添加完成后需要将 FreeRTOS 源码目录下的 include
目录添加进编译配置中,如下所示:
我使用的主控芯片前面提到过是 GD32F103VCT6, 系统时钟默认配置为 108MHz, 因此将该数值更改为 108000000
此时再执行编译, 发现可以编译通过, 无报错无警告 😃
测试
添加简单的线程测试, 我这里选用了两个简单的测试线程, 分别为
- LED 闪烁线程
- 串口 LOG 打印线程
实现如下:
// LED 线程
#include "gd32f10x_rcu.h"
#include "gd32f10x_gpio.h"
#include "FreeRTOS.h"
#include "task.h"
#include "freertos_task_led.h"
void freertos_task_led(void *args)
{
/* 初始化 LED */
rcu_periph_clock_enable(RCU_GPIOE);
gpio_init(GPIOE, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_2);
for(;;) {
gpio_bit_set(GPIOE, GPIO_PIN_2);
vTaskDelay(500);
gpio_bit_reset(GPIOE, GPIO_PIN_2);
vTaskDelay(500);
}
}
每隔半秒翻转一次LED的状态
#include "gd32f10x_rcu.h"
#include "gd32f10x_gpio.h"
#include "FreeRTOS.h"
#include "task.h"
#include "generic_log.h"
#include "freertos_task_log.h"
void freertos_task_log(void *args)
{
/* 初始化 LED */
rcu_periph_clock_enable(RCU_GPIOE);
gpio_init(GPIOE, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_2);
for(;;) {
LOG("Task LOG Running");
vTaskDelay(1000);
}
}
每隔 1s 通过串口发送一个 Task LOG Running
的字符串, 创建任务函数如下:
#include "FreeRTOS.h"
#include "task.h"
#include "freertos_task_led.h"
#include "freertos_task_log.h"
#include "freertos_task.h"
TaskHandle_t freertos_task_led_handle;
TaskHandle_t freertos_task_log_handle;
void freertos_task_init(void)
{
xTaskCreate(freertos_task_led, "task_led", 64, NULL, 5, &freertos_task_led_handle);
xTaskCreate(freertos_task_log, "task_log", 64, NULL, 5, &freertos_task_log_handle);
vTaskStartScheduler();
}
创建了两个任务, 并开启 RTOS 调度, 此时编译可正常通过, 再 main
函数调用 freertos_task_init
, 如下所示:
编译无报错,无警报,烧录进入板子后可以发现串口没有打印,该闪烁的 LED 也没有一点动静, 这是因为还没有将主控的 Tick 与 RTOS 的调度器对接, 这里需要更改 启动文件 startup_gd32f10x_hd.s
, 将中断向量表中的以下三个处理函数更改为 FreeRTOS 中的处理函数, 如下所示:
原函数名 | 更改之后的函数名 |
---|---|
SVC_Handler | vPortSVCHandler |
PendSV_Handler | xPortPendSVHandler |
SysTick_Handler | xPortSysTickHandler |
这三个函数出自前面移植时添加的源文件 port.c
中, 在该源文件中有所定义
此时再编译, 无错误无警报, 烧录后 串口打印, LED也在正常闪烁, FreeRTOS 移植成功
参考文章
在这里感谢以上伙伴的文章分享!