梁山派GD32F470ZGT6 FreeRTOS CMake 模板适配指南

GD32F470ZGT6 FreeRTOS CMake 模板适配指南

本文档详细说明如何基于 GD32F470ZGT6 裸机模板,移植 FreeRTOS V10.4.3 实时操作系统。


一、参考标准

参考来源 版本/路径 用途
FreeRTOS 官方 V10.4.3 内核源码、端口代码
FreeRTOS Cortex-M 移植指南 FreeRTOS/Source/portable/GCC/ARM_CM4F/ ARM_CM4F 端口标准实现
FreeRTOS 内存管理 FreeRTOS/Source/portable/MemMang/heap_4.c 动态内存分配方案
FreeRTOS 配置参考 FreeRTOS 官方 Demo 工程 FreeRTOSConfig.h 标准配置
GD32F4xx 标准外设库 V3.3.3 GPIO、时钟初始化
ARM Cortex-M4 权威指南 ARM 官方 NVIC 优先级分组、中断管理

二、FreeRTOS 官方标准解析

2.1 Cortex-M4F 端口要求

FreeRTOS 为 Cortex-M4F 提供了专门的端口(portable/GCC/ARM_CM4F/),关键特性:

特性 说明
端口目录 portable/GCC/ARM_CM4F/
核心文件 port.c --- 上下文切换、PendSV/SysTick 处理
FPU 支持 自动保存/恢复 FPU 寄存器
SysTick 映射 xPortSysTickHandlerSysTick_Handler
PendSV 映射 xPortPendSVHandlerPendSV_Handler
SVC 映射 vPortSVCHandlerSVC_Handler

2.2 中断优先级标准

FreeRTOS 对 Cortex-M 的中断优先级有严格要求:

复制代码
优先级分配(4 bit,共 16 级 0-15):
├── 0-4   : FreeRTOS 管理(内核中断)
├── 5     : configMAX_SYSCALL_INTERRUPT_PRIORITY(系统调用最高优先级)
└── 6-15  : 用户中断(不受 FreeRTOS 管理)

关键规则

  • FreeRTOS 内核中断(PendSV、SysTick)使用最低优先级(15)
  • configMAX_SYSCALL_INTERRUPT_PRIORITY 定义可调用 FreeRTOS API 的最高中断优先级
  • 优先级高于此值的中断不能使用 FreeRTOS API

2.3 内存管理方案

FreeRTOS 提供 5 种内存管理方案:

方案 特点 适用场景
heap_1 只分配,不释放 简单嵌入式系统
heap_2 最佳匹配算法 碎片化不严重的场景
heap_3 包装标准 malloc/free 需要标准 C 库的场景
heap_4 首次匹配 + 合并 通用场景(本模板使用)
heap_5 多区域内存管理 复杂内存布局

三、适配步骤

步骤 1:准备 FreeRTOS 源码

将 FreeRTOS 源码放置到 SDK 目录:

复制代码
sdk/utilities/Third_Party/FreeRTOS/
├── include/           # FreeRTOS 内核头文件
├── tasks.c            # 任务管理
├── queue.c            # 队列
├── list.c             # 链表
├── timers.c           # 软件定时器
├── event_groups.c     # 事件组
├── stream_buffer.c    # 流缓冲区
├── croutine.c         # 协程
└── portable/
    ├── GCC/
    │   └── ARM_CM4F/  # Cortex-M4F 端口
    │       ├── port.c
    │       └── portmacro.h
    └── MemMang/
        └── heap_4.c   # 内存管理方案

步骤 2:创建 FreeRTOSConfig.h

myprog/include/FreeRTOSConfig.h 中创建配置文件:

c 复制代码
#ifndef FREERTOS_CONFIG_H
#define FREERTOS_CONFIG_H

#include "gd32f4xx.h"

/* 重命名 CMSIS 中断处理函数(关键!) */
#define vPortSVCHandler         SVC_Handler
#define xPortPendSVHandler      PendSV_Handler
#define xPortSysTickHandler     SysTick_Handler

关键说明 :FreeRTOS 的 port.c 中定义了 SVC_HandlerPendSV_HandlerSysTick_Handler 函数。由于启动文件的向量表中已经声明了这些函数名,需要通过宏定义将 FreeRTOS 的内部函数名映射到 CMSIS 标准名称。

步骤 3:配置核心参数

c 复制代码
/* 调度器配置 */
#define configUSE_PREEMPTION                    1       /* 抢占式调度 */
#define configUSE_PORT_OPTIMISED_TASK_SELECTION 0       /* 不使用硬件位带优化 */
#define configUSE_TICKLESS_IDLE                 0       /* 不使用 Tickless 低功耗 */

/* 时钟配置 */
#define configCPU_CLOCK_HZ                      (SystemCoreClock)  /* 240MHz */
#define configTICK_RATE_HZ                      1000               /* 1ms tick */

/* 任务配置 */
#define configMAX_PRIORITIES                    5       /* 5 个优先级 (0-4) */
#define configMINIMAL_STACK_SIZE                128     /* 最小栈 128 字 = 512 字节 */
#define configTOTAL_HEAP_SIZE                   (64 * 1024)  /* 总堆 64KB */
#define configMAX_TASK_NAME_LEN                 16      /* 任务名最大长度 */

/* 功能开关 */
#define configUSE_TASK_NOTIFICATIONS            1       /* 任务通知 */
#define configUSE_MUTEXES                       1       /* 互斥量 */
#define configUSE_RECURSIVE_MUTEXES             1       /* 递归互斥量 */
#define configUSE_COUNTING_SEMAPHORES           1       /* 计数信号量 */
#define configUSE_TIMERS                        1       /* 软件定时器 */

步骤 4:配置中断优先级

c 复制代码
/* 中断优先级配置(4 bit NVIC) */
#define configPRIO_BITS                         4   /* GD32F4xx 使用 4 位优先级 */
#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY   15  /* 最低优先级 */
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 5  /* 可调用 API 的最高优先级 */

/* 计算内核优先级值(移位到 8 位格式) */
#define configKERNEL_INTERRUPT_PRIORITY \
    (configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS))
    /* = 15 << 4 = 0xF0 */

#define configMAX_SYSCALL_INTERRUPT_PRIORITY \
    (configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS))
    /* = 5 << 4 = 0x50 */

标准参考:FreeRTOS 官方文档 "Interrupt priority configuration on ARM Cortex-M"。

步骤 5:配置内存分配

c 复制代码
/* 内存分配方案 */
#define configSUPPORT_STATIC_ALLOCATION         0   /* 不支持静态分配 */
#define configSUPPORT_DYNAMIC_ALLOCATION        1   /* 支持动态分配 */
#define configAPPLICATION_ALLOCATED_HEAP        0   /* 堆由 FreeRTOS 管理 */

步骤 6:配置 CMakeLists.txt

cmake 复制代码
# FreeRTOS V10.4.3
set(FREERTOS_ROOT "${SDK_ROOT}/utilities/Third_Party/FreeRTOS")
set(FREERTOS_PORT "${FREERTOS_ROOT}/portable/GCC/ARM_CM4F")
set(FREERTOS_HEAP "${FREERTOS_ROOT}/portable/MemMang")

# 头文件路径
include_directories(
    ${FREERTOS_ROOT}/include
    ${FREERTOS_PORT}
)

# FreeRTOS 源码
set(FREERTOS_SOURCES
    ${FREERTOS_ROOT}/tasks.c
    ${FREERTOS_ROOT}/queue.c
    ${FREERTOS_ROOT}/list.c
    ${FREERTOS_ROOT}/timers.c
    ${FREERTOS_ROOT}/event_groups.c
    ${FREERTOS_ROOT}/stream_buffer.c
    ${FREERTOS_ROOT}/croutine.c
    ${FREERTOS_PORT}/port.c
    ${FREERTOS_HEAP}/heap_4.c
)

# 加入编译
set(PROJECT_SOURCES
    ${PERIPHERAL_SOURCES}
    ${SYSTEM_SOURCES}
    ${STARTUP_SOURCES}
    ${APP_SOURCES}
    ${FREERTOS_SOURCES}
)

步骤 7:实现 main.c

c 复制代码
#include "gd32f4xx.h"
#include "FreeRTOS.h"
#include "task.h"

/* LED 任务 */
static void led_task(void *pvParameters)
{
    while(1) {
        gpio_bit_write(LED1_PORT, LED1_PIN, 1);
        gpio_bit_write(LED2_PORT, LED2_PIN, 0);
        gpio_bit_write(LED3_PORT, LED3_PIN, 0);
        gpio_bit_write(LED4_PORT, LED4_PIN, 0);
        vTaskDelay(pdMS_TO_TICKS(500));
        
        /* 切换下一个 LED ... */
    }
}

int main(void)
{
    SystemCoreClockUpdate();
    
    /* NVIC 优先级分组(FreeRTOS 要求 4 bit 全用于子优先级) */
    NVIC_SetPriorityGrouping(4);
    
    /* GPIO 初始化 */
    rcu_periph_clock_enable(RCU_GPIOE);
    /* ... */
    
    /* 创建 LED 任务 */
    xTaskCreate(led_task, "LED", 
                configMINIMAL_STACK_SIZE * 2,  /* 栈大小 */
                NULL, 2, NULL);                /* 参数、优先级、句柄 */
    
    /* 启动调度器 */
    vTaskStartScheduler();
    
    /* 理论上不会到达这里 */
    while(1) {
        debug_printf("Error: FreeRTOS scheduler stopped!\n");
    }
}

关键说明

  • NVIC_SetPriorityGrouping(4) 配置所有 4 bit 为抢占优先级(0 位子优先级),FreeRTOS 只管理抢占优先级

注意 :不同 CMSIS 版本中 NVIC_SetPriorityGrouping() 的参数含义可能不同。部分系统中 FreeRTOS 官方推荐使用 NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4),建议根据实际平台的 CMSIS 实现验证。实际配置需保证 FreeRTOS 管理的中断(PendSV、SysTick)优先级最低(15)。

  • vTaskStartScheduler() 启动调度器后,不再返回(除非内存不足)
  • 不需要手动配置 SysTick,FreeRTOS 的 port.c 会自动处理

步骤 8:FreeRTOS SysTick 处理(由 port.c 自动处理)

FreeRTOS 的 port.c 内部实现了 SysTick_Handler

c 复制代码
/* port.c 内部实现(不需要用户编写) */
void xPortSysTickHandler(void)
{
    /* 增加 tick 计数 */
    xTaskIncrementTick();
    
    /* 触发 PendSV 进行任务切换 */
    portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT;
}

与裸机的区别 :裸机需要用户手动实现 SysTick_Handler,而 FreeRTOS 的 port.c 已内置实现,通过 FreeRTOSConfig.h 中的宏映射到 SysTick_Handler


四、关键差异对比

项目 裸机模板 FreeRTOS 模板
SysTick 处理 用户手动实现 port.c 自动实现
延时方式 delay_ms() 阻塞等待 vTaskDelay() 非阻塞挂起
中断管理 用户手动配置 FreeRTOS 统一管理
内存分配 静态分配 heap_4.c 动态分配
NVIC 分组 用户自由配置 必须为 NVIC_SetPriorityGrouping(4)
程序入口 main() 循环 vTaskStartScheduler() 启动调度器

五、编译与烧录

5.1 编译

bash 复制代码
mkdir -p build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Debug
make -j8

5.2 烧录

bash 复制代码
sudo ./flash/flash.sh

5.3 验证

通过串口查看启动信息:

复制代码
=== FreeRTOS LED Demo ===

4 个 LED 每 500ms 依次切换,形成跑马灯效果。


六、关键配置汇总

项目 说明
FreeRTOS 版本 V10.4.3 稳定版
端口 ARM_CM4F Cortex-M4F 硬件浮点
内存管理 heap_4.c 首次匹配 + 合并
总堆大小 64KB configTOTAL_HEAP_SIZE
Tick 频率 1000Hz (1ms) configTICK_RATE_HZ
优先级数量 5 configMAX_PRIORITIES
调度方式 抢占式 configUSE_PREEMPTION = 1
最大系统调用优先级 5 configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY
内核中断优先级 0xF0 最低优先级

七、注意事项

  1. 中断函数名映射 :必须在 FreeRTOSConfig.h 中定义 vPortSVCHandlerxPortPendSVHandlerxPortSysTickHandler 宏,否则链接时会找到多个函数定义
  2. NVIC 优先级分组 :必须调用 NVIC_SetPriorityGrouping(4),FreeRTOS 只使用子优先级
  3. 栈大小 :任务栈不能小于 configMINIMAL_STACK_SIZE(128 字 = 512 字节),否则会导致栈溢出
  4. 中断中调用 API :优先级高于 configMAX_SYSCALL_INTERRUPT_PRIORITY 的中断不能使用 FreeRTOS API
  5. heap_4 合并机制:释放的内存块会自动与相邻空闲块合并,减少碎片
相关推荐
嵌入式小站3 小时前
STM32 零基础可移植教程 03:蜂鸣器响一声,LED 跟着翻转一次
stm32·单片机·嵌入式硬件
星夜夏空993 小时前
STM32单片机学习(15) —— PC串口通信实验
stm32·单片机·学习
星夜夏空993 小时前
STM32单片机学习(14) —— STM32的串口外设
stm32·单片机·学习
都在酒里4 小时前
STM32标准库驱动L298N双H桥电机驱动模块(调速/正反转/多模式实战,附完整工程代码)
stm32·单片机·嵌入式硬件
key_3_feng4 小时前
鸿蒙车规级MCU开发方案
单片机·华为·harmonyos
踏着七彩祥云的小丑5 小时前
嵌入式测试学习第 13 天:串口助手软件安装、界面认识、参数配置
单片机·嵌入式硬件
黑猫学长呀5 小时前
存储宝典第4篇:存储芯片中常说的E2E是啥?
linux·单片机·嵌入式硬件·e2e·ssd·ufs·存储芯片
xiangw@GZ5 小时前
DDR的硬件拓扑与ODT匹配技术
嵌入式硬件
熙芯XiChip5 小时前
压电片工作原理
嵌入式硬件