GD32H759移植FreeRTOS实战(保姆级教程)
本文详细介绍了将FreeRTOS V202212.00移植到GD32H759 MCU(Cortex-M7内核)的开发过程。主要内容包括:工程准备(硬件环境、软件工具、基础工程搭建)、FreeRTOS内核源码的添加与配置、FreeRTOSConfig.h关键参数设置、中断冲突处理、编译器与FPU的特殊配置等。重点解决了移植过程中的常见问题,如中断优先级配置、FPU启用、编译器匹配等。最终实现了一个基
一、前言
GD32H759 是 GigaDevice 推出的 Cortex‑M7 高性能 MCU,主频最高 600MHz,带 FPU 和丰富片上外设,非常适合跑 RTOS。 FreeRTOS 则是目前应用最广泛的轻量级实时操作系统,内核小巧、移植简单,官方已经提供了 Cortex‑M7 的标准移植层。
这篇文章记录把 FreeRTOS V202212.00 移植到 GD32H759 + Keil MDK(使用 ARM Compiler 5) 的完整过程,包括踩坑和调试经验,最终实现一个简单的 FreeRTOS 点灯任务,并通过串口打印调试信息。
二、工程准备
2.1 硬件与软件环境
-
MCU:GD32H759 系列(本文以 GD32H759I_start开发板为例)。
-
IDE:Keil MDK‑ARM 5.x
-
编译器:ARM Compiler V5.06(armcc)
-
FreeRTOS:FreeRTOS‑Kernel V202212.00
-
官方库:GD32H7xx 标准外设库
保证先有一个 裸机点灯 的 Keil 工程,这是移植的基础。
2.2 新建/拷贝基础工程
通常做法是:
-
使用 GigaDevice 提供的 GD32H759 示例工程,复制一份作为 FreeRTOS 工程模板。
浏览器搜索“兆易创新资料下载中心”,左边点击GD32H7,右边找到应用软件,找到GD32H7xx addon ,如果自己的keil里面没得GD32H7的包,那么就得先安装这个包。

然后下载这官方个例程

自己先跑一下这个例程,能否在板子上正常的跑起来。没问题就可以。
移植第一个点灯例程作为模板。
三、添加 FreeRTOS 内核到 GD32 工程
3.1 拷贝 FreeRTOS 源码
从 FreeRTOS 官方仓库下载内核源码后,下载 FreeRTOS - FreeRTOS™
拷贝以下文件到工程根目录下的 FreeRTOS 文件夹。
自己创建FreeRTOS 文件结构如下:

解压后的freertos文件

复制一下文件到core


头文件:
-
整个
FreeRTOS/Source/include/目录。


使用Keil AC5则需要移植RVDS路径的port.c,AC6则是GCC下的.
-
FreeRTOS/Source/portable/RVDS/ARM_CM7/r0p1/port.c -
FreeRTOS/Source/portable/RVDS/ARM_CM7/r0p1/portmacro.h
内存管理:
-
FreeRTOS/Source/portable/MemMang/heap_4.c(常用,支持内存合并)
3.2 在 Keil 工程中加入文件
建议在 Keil 工程中建立如下的分组,结构更清晰:

3.3 配置头文件路径
在 Keil 中打开 Options for Target → C/C++ → Include Paths,加入freertos的头文件路径。
确保没有把 GCC\ARM_CM7 路径也加进来,否则容易和 AC5 产生语法冲突。
四、编写 FreeRTOSConfig.h(GD32H759 版)
在工程中新建 FreeRTOSConfig.h,核心配置如下,可以根据需要调整参数
#ifndef FREERTOS_CONFIG_H
#define FREERTOS_CONFIG_H
#include "gd32h7xx.h"
#include "bsp_uart.h"
/*-----------------------------------------------------------
* Application specific definitions.
*
* These definitions should be adjusted for your particular hardware and
* application requirements.
*
* THESE PARAMETERS ARE DESCRIBED WITHIN THE 'CONFIGURATION' SECTION OF THE
* FreeRTOS API DOCUMENTATION AVAILABLE ON THE FreeRTOS.org WEB SITE.
*
* See http://www.freertos.org/a00110.html
*----------------------------------------------------------*/
//针对不同的编译器调用不同的stdint.h文件
/* Ensure stdint is only used by the compiler, and not the assembler. */
#if defined(__ICCARM__) || defined(__CC_ARM) || defined(__GNUC__)
#include <stdint.h>
extern uint32_t SystemCoreClock;
#endif
/* 置1:RTOS使用抢占式调度器;置0:RTOS使用协作式调度器(时间片)
*
* 注:在多任务管理机制上,操作系统可以分为抢占式和协作式两种。
* 协作式操作系统是任务主动释放CPU后,切换到下一个任务。
* 任务切换的时机完全取决于正在运行的任务。
*/
#define configUSE_PREEMPTION 1
/*
* 写入实际的CPU内核时钟频率,也就是CPU指令执行频率,通常称为Fclk
* Fclk为供给CPU内核的时钟信号,我们所说的cpu主频为 XX MHz,
* 就是指的这个时钟信号,相应的,1/Fclk即为cpu时钟周期;
*/
#define configCPU_CLOCK_HZ ( SystemCoreClock )
//RTOS系统节拍中断的频率。即一秒中断的次数,每次中断RTOS都会进行任务调度
#define configTICK_RATE_HZ ( ( TickType_t ) 1000 )
//可使用的最大优先级
#define configMAX_PRIORITIES ( 32 )
//空闲任务使用的堆栈大小
#define configMINIMAL_STACK_SIZE ( ( unsigned short ) 128 )
//任务名字字符串长度
#define configMAX_TASK_NAME_LEN ( 10 )
#define configUSE_16_BIT_TICKS 0
//空闲任务放弃CPU使用权给其他同优先级的用户任务
#define configIDLE_SHOULD_YIELD 1
//使用互斥信号量
#define configUSE_MUTEXES 1
/* 设置可以注册的信号量和消息队列个数 */
#define configQUEUE_REGISTRY_SIZE 8
//使用递归互斥信号量
#define configUSE_RECURSIVE_MUTEXES 1
#define configUSE_APPLICATION_TASK_TAG 0
//为1时使用计数信号量
#define configUSE_COUNTING_SEMAPHORES 1
#define configGENERATE_RUN_TIME_STATS 0
/*****************************************************************
FreeRTOS与内存申请有关配置选项
*****************************************************************/
//支持动态内存申请
#define configSUPPORT_DYNAMIC_ALLOCATION 1
//支持静态内存
#define configSUPPORT_STATIC_ALLOCATION 0
//系统所有总的堆大小
#define configTOTAL_HEAP_SIZE ((size_t)(36*1024))
/***************************************************************
FreeRTOS与钩子函数有关的配置选项
**************************************************************/
/* 置1:使用空闲钩子(Idle Hook类似于回调函数);置0:忽略空闲钩子
*
* 空闲任务钩子是一个函数,这个函数由用户来实现,
* FreeRTOS规定了函数的名字和参数:void vApplicationIdleHook(void ),
* 这个函数在每个空闲任务周期都会被调用
* 对于已经删除的RTOS任务,空闲任务可以释放分配给它们的堆栈内存。
* 因此必须保证空闲任务可以被CPU执行
* 使用空闲钩子函数设置CPU进入省电模式是很常见的
* 不可以调用会引起空闲任务阻塞的API函数
*/
#define configUSE_IDLE_HOOK 0
/* 置1:使用时间片钩子(Tick Hook);置0:忽略时间片钩子
*
*
* 时间片钩子是一个函数,这个函数由用户来实现,
* FreeRTOS规定了函数的名字和参数:void vApplicationTickHook(void )
* 时间片中断可以周期性的调用
* 函数必须非常短小,不能大量使用堆栈,
* 不能调用以”FromISR" 或 "FROM_ISR”结尾的API函数
*/
/*xTaskIncrementTick函数是在xPortSysTickHandler中断函数中被调用的。因此,vApplicationTickHook()函数执行的时间必须很短才行*/
#define configUSE_TICK_HOOK 0
#define configUSE_MALLOC_FAILED_HOOK 0
/*
* 大于0时启用堆栈溢出检测功能,如果使用此功能
* 用户必须提供一个栈溢出钩子函数,如果使用的话
* 此值可以为1或者2,因为有两种栈溢出检测方法 */
#define configCHECK_FOR_STACK_OVERFLOW 1
/* 置1:启用堆栈溢出检测功能;置0:忽略堆栈溢出检测功能
* 此值可以为1或者2,因为有两种栈溢出检测方法 */
//#define configCHECK_FOR_STACK_OVERFLOW 2
/* 置1:启用 malloc 失败钩子函数;置0:忽略 malloc 失败钩子函数
* 当动态内存申请失败时,会调用此钩子函数
* 此值可以为1或者2,因为有两种 malloc 失败检测方法 */
//#define configUSE_MALLOC_FAILED_HOOK 1
/***********************************************************************
FreeRTOS与软件定时器有关的配置选项
**********************************************************************/
/* Software timer definitions. */
#define configUSE_TIMERS 1
#define configTIMER_TASK_PRIORITY (configMAX_PRIORITIES-1)
#define configTIMER_QUEUE_LENGTH 10
#define configTIMER_TASK_STACK_DEPTH ( configMINIMAL_STACK_SIZE * 2 )
/* Set the following definitions to 1 to include the API function, or zero
to exclude the API function. */
#define INCLUDE_vTaskPrioritySet 1
#define INCLUDE_uxTaskPriorityGet 1
#define INCLUDE_vTaskDelete 1
#define INCLUDE_vTaskCleanUpResources 1
#define INCLUDE_vTaskSuspend 1
#define INCLUDE_vTaskDelayUntil 1
#define INCLUDE_vTaskDelay 1
//#define INCLUDE_vTaskSuspend 1
#define INCLUDE_xResumeFromISR 1
#define configUSE_TRACE_FACILITY 1
#define configUSE_STATS_FORMATTING_FUNCTIONS 1
// 硬件适配
//#define configSYSTICK_CLOCK_HZ configCPU_CLOCK_HZ // SysTick 时钟源
//#define configSYSTICK_PERIPHERAL SYSTICK_CLKSOURCE_HCLK // 使用内核时钟
// 中断优先级配置(Cortex-M4)
//#define configKERNEL_INTERRUPT_PRIORITY 0xF0 // 最低优先级(FreeRTOS 系统中断)
//#define configMAX_SYSCALL_INTERRUPT_PRIORITY 0x80 // 允许调用 FreeRTOS API 的最高中断优先级
/* 放在 FreeRTOSConfig.h 中 */
#define configENABLE_FPU 1
#define configENABLE_MPU 0
/* Cortex-M specific definitions. */
#ifdef __NVIC_PRIO_BITS
/* __BVIC_PRIO_BITS will be specified when CMSIS is being used. */
#define configPRIO_BITS __NVIC_PRIO_BITS
#else
#define configPRIO_BITS 4 /* 15 priority levels */
#endif
/* 在调用 “设置优先级” 函数时可以使用的最低中断优先级. */
#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 0xf
/* 任何调用中断安全的 FreeRTOS API 函数的中断服务程序所能使用的最高中断优先级。
不要从优先级高于此的任何中断中调用中断安全的 FreeRTOS API 函数!(优先级越高,数值越小) */
//系统可管理的最高中断优先级
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 5
/* 内核端口层自身使用的中断优先级。这些优先级对所有 Cortex-M 端口都是通用的,并且不依赖任何特定的库函数。. */
#define configKERNEL_INTERRUPT_PRIORITY ( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
/* !!!! configMAX_SYSCALL_INTERRUPT_PRIORITY must not be set to zero !!!!
See http://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html. */
#define configMAX_SYSCALL_INTERRUPT_PRIORITY ( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
/* Normal assert() semantics without relying on the provision of an assert.h
header file. */
#define configASSERT( x ) if( ( x ) == 0 ) { taskDISABLE_INTERRUPTS(); for( ;; ); }
/* Definitions that map the FreeRTOS port interrupt handlers to their CMSIS
standard names. */
/****************************************************************
FreeRTOS与中断服务函数有关的配置选项
****************************************************************/
#define vPortSVCHandler SVC_Handler
#define xPortPendSVHandler PendSV_Handler
#define xPortSysTickHandler SysTick_Handler
#endif /* FREERTOS_CONFIG_H */
这里最关键的是:
-
configCPU_CLOCK_HZ使用SystemCoreClock,避免频率修改后忘记同步。 -
中断优先级宏设置必须保证
configMAX_SYSCALL_INTERRUPT_PRIORITY不为 0、且与芯片实际优先级位数匹配,否则会导致 HardFault。
五、处理 SysTick / SVC / PendSV 中断冲突
FreeRTOS 在 Cortex‑M 端口中会接管三个系统中断:SysTick、PendSV、SVC。
GD32 的模板工程一般在 gd32h7xx_it.c 里已经给出了默认的中断实现,需要手动处理:
-
打开
gd32h7xx_it.c,注释或者删掉以下函数。


-
注意不要再手工调用
SysTick_Config()或在应用中重新配置 SysTick,避免与 FreeRTOS 配置冲突。
六、Keil 中编译器与 FPU 设置(重点坑点)
6.1 选择 ARM Compiler 版本与端口文件匹配
-
使用 ARM Compiler 5(armcc) 时,必须使用
portable\RVDS\ARM_CM7\r0p1下的 port 文件; -
如果改用 ARM Compiler 6(armclang),则更适合使用
portable\GCC\ARM_CM7\r0p1下的文件。
不能出现 AC5 + GCC 端口 或 AC6 + RVDS 端口 这种混搭,否则容易出现内联汇编语法错误或 A1944E 之类的报错。
6.2 FPU 启用与宏定义
在 Keil:
-
Options for Target → Target → Floating Point Hardware选择Double Precision(GD32H759 支持双精度 FPU)。
某些 FreeRTOS 移植文件在开头会检查:
#ifndef __TARGET_FPU_VFP #error This port can only be used when the project options are configured to enable hardware floating point support. #endif
如果工程设置正确却仍然报错,可以在 C/C++ → Preprocessor Symbols → Define 中手工添加:
__TARGET_FPU_VFP
确保 FPU 检查顺利通过。
七、实现第一个 FreeRTOS 点灯任务(PC9)
7.1 LED GPIO 初始化
以板载 PC9 ---led1为例,main.c文件如下:
/**
* @file main.c
* @author lingxiao (lingxiao@qq.com)
* @brief FreeRTOS在GD32H7xx上的简单应用示例
* @version 1.0
* @date 2026-01-23
*
* @copyright Copyright (c) 2026
*
*/
#include "gd32h7xx.h"
#include "systick.h"
#include "bsp_uart.h"
#include "FreeRTOS.h"
#include "task.h"
#define TASK_STACK_SIZE 512 // 任务栈大小,字为单位
#define TASK_PRIORITY 6 // 任务优先级,最大32
TaskHandle_t start_task_handle ; // 任务句柄
void start_task(void *pvParameters);
/*!
\brief enable the CPU Chache
\param[in] none
\param[out] none
\retval none
*/
static void cache_enable(void)
{
/* Enable I-Cache */
SCB_EnableICache();
//指令缓存(Instruction Cache,ICache):用于存储CPU最近执行的指令,提高程序执行效率。
//数据缓存(Data Cache,DCache):用于存储CPU最近访问的数据,提高数据访问效率。
/* Enable D-Cache */
SCB_EnableDCache();
}
/*!
\brief main function
\param[in] none
\param[out] none
\retval none
*/
int main(void)
{
/* enable the CPU Cache */
cache_enable();
/*串口调试打印初始化 */
bsp_uart1_init();
printf("\n");
printf("FreeRTOS run in gd32h759i demo\r\n");
printf("hello\n");
/* 使能GPIOC时钟 配置PC9为输出模式 */
rcu_periph_clock_enable(RCU_GPIOC);
gpio_mode_set(GPIOC,GPIO_MODE_OUTPUT,GPIO_PUPD_NONE,GPIO_PIN_9);
gpio_output_options_set(GPIOC,GPIO_OTYPE_PP,GPIO_OSPEED_60MHZ,GPIO_PIN_9);
gpio_bit_reset(GPIOC,GPIO_PIN_9);
/* 创建启动任务 */
xTaskCreate( (TaskFunction_t) start_task,
(char *) "start_task", //任务名称
( configSTACK_DEPTH_TYPE )TASK_STACK_SIZE, //任务栈大小
(void *) NULL,
(UBaseType_t) TASK_PRIORITY,//任务优先级
(TaskHandle_t *) &start_task_handle); //任务句柄
vTaskStartScheduler(); //启动任务调度器会自动创建一个空闲任务
while(1)
{
}
}
/**
* @brief 启动任务
*
* @param pvParameters
*/
void start_task(void *pvParameters)
{
while(1)
{
gpio_bit_toggle(GPIOC, GPIO_PIN_9); // 翻转 PC9
vTaskDelay(500); // 延时 500ms
printf("led1 have lignting\n");
}
}
/**
* @brief 堆栈溢出钩子函数
*
* @param xTask 发生堆栈溢出的任务句柄
* @param pcTaskName 发生堆栈溢出的任务名称
*/
void vApplicationStackOverflowHook(TaskHandle_t xTask, char *pcTaskName)
{
// 堆栈溢出时会到这里
printf("Stack Overflow: %s\r\n", pcTaskName);
taskDISABLE_INTERRUPTS();
while(1)
{
}
}
/**
* @brief malloc 失败钩子函数
* @param none
* @retval none
*/
void vApplicationMallocFailedHook(void)
{
printf("Malloc Failed!\r\n");
taskDISABLE_INTERRUPTS();
while(1)
{
}
}
如果下载后 led1闪烁,说明 FreeRTOS 已经成功在 GD32H759 上跑起来了。
八、常见问题与排坑记录
-
A1944E: Literal pool entries cannot be generated in execute-only sections
-
原因:使用 AC6 编译器配 RVDS 端口文件或开启了执行保护(XOM)。
-
解决:保持 AC5 + RVDS 或 AC6 + GCC 的正确组合,并关闭执行‑only 段。
-

-
-
端口文件语法错误(portmacro.h 内联汇编 expected ')')
-
原因:用 AC5 编译 GCC 版本的 portmacro.h。
-
解决:确认
port.c/portmacro.h路径来自RVDS\ARM_CM7\r0p1。
-
-
程序运行但 FreeRTOS 任务不执行,LED 不闪
-
检查 SysTick 中断是否被自己写的
SysTick_Handler抢走; -
确认没有重复配置 SysTick;
-
检查
configCPU_CLOCK_HZ与SystemCoreClock是否一致。
-
-
在任务里加 printf 后串口卡死
-
先增大任务栈并打开栈溢出钩子;
-
检查 UART 发送函数是否在临界区或禁止中断状态下阻塞太久。
-
九、总结与后续扩展
通过以上步骤,可以在 GD32H759 上稳定跑起 FreeRTOS 内核,实现基本任务调度和 点灯示例。移植完成后,接下来可以逐步引入:
-
任务间消息队列、二值/计数信号量。
-
事件组、软件定时器。
-
中断服务函数里使用 “FromISR” 版本的 FreeRTOS API,实现外设事件驱动。
-
更复杂的多任务框架(如串口命令行、以太网协议栈等)。
在实际项目中,建议从“一个主任务 + 若干简单任务”起步,逐步引入复杂模块,同时配合串口日志和栈/堆监控,减少调试难度。
openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。
更多推荐
所有评论(0)