准备工作
- GD32开发板。案例是以梁山派为开发板。
- Windows系统的电脑。当前是以Win11的电脑来实现案例的。
- Keil开发工具。并且已经安装好GD32依赖环境。
- FreeRTOS源码包。下载地址为: https://github.com/FreeRTOS/FreeRTOS/releases

当前以FreeRTOSv202212.01版本为例。也是目前的最新版本。
移植流程
项目基础环境准备
参考: https://www.yuque.com/icheima/osa819/uvhcdkzgmrus22iy#HJokw
新建项目名称为:FreeRTOSTemplate
项目结构如下:
确保以下配置信息的正确性:
- Target的配置

- Output的配置

- C/C++(AC6)配置

- Debug配置
FreeRTOS移植
FreeRTOS源码裁剪
- 源码解压

- 进入
FreeRTOS/Source目录。此目录就是我们要移植的源码。

- 在你新建的
FreeRTOSTemplate工程目录下,新建一个文件夹FreeRTOS,将以上源码拷贝到这个文件夹中。

- 删除不必要的文件。来到
portable目录只保留GCC和MemManag目录
源码目录构建
- 新建两个Group:
FreeRTOS_Core和FreeRTOS_Port

FreeRTOS_Core添加源码。源码为FreeRTOS根目录下的c文件


FreeRTOS_Port添加源码。源码为FreeRTOS/portable/MemMang下的heap_4.c。源码为FreeRTOS/portable/GCC/ARM_CM4F下的port.c


- 添加头文件支持。将
FreeRTOS/include和FreeRTOS/portable/GCC/ARM_CM4F添加到头文件依赖中。
FreeRTOSConfig.h文件缺失解决
编译项目,会出现以下错误:
来到FreeRTOS源码目录中,找到FreeRTOSv202212.01\FreeRTOS\Demo\CORTEX_M4F_STM32F407ZG-SK目录中的FreeRTOSConfig.h文件,进行拷贝。
将FreeRTOSConfig.h文件拷贝到项目目录中的FreeRTOS目录下
添加头文件引入。将FreeRTOS目录添加到include path中。
SystemCoreClock问题解决
编译项目,会出现以下错误:
来到FreeRTOSConfig.h文件中,观察以下内容:
- SystemCoreClock其实是有的
- SystemCoreClock在编译预处理前没有被定义出来。原因是
extern uint32_t SystemCoreClock;这一段没有进入编译,因为在编译预处理前不满足#ifdef __ICCARM__这个条件。编译器c语言条件不满足。我们将以上代码进行修改。#ifdef __ICCARM__#include <stdint.h>extern uint32_t SystemCoreClock;#endif
#if defined(__ICCARM__) || defined(__CC_ARM) || defined(__GNUC__)#include <stdint.h>extern uint32_t SystemCoreClock;#endif
xxx_Handler问题解决
编译项目,会出现以下错误:
来到gd32f4xx_it.c文件中。修改三个函数SVC_Handler,PendSV_Handler,SysTick_Handler,将函数通过宏定义包裹: ```cifndef SYS_SUPPORT_OS
endif
修改后如下:```c#ifndef SYS_SUPPORT_OSvoid SVC_Handler(void){}#endif...#ifndef SYS_SUPPORT_OSvoid PendSV_Handler(void){}#endif...#ifndef SYS_SUPPORT_OSvoid SysTick_Handler(void){delay_decrement();}#endif
配置C/C++(AC6)中的define为:USE_STDPERIPH_DRIVER,GD32F470,SYS_SUPPORT_OS
vApplicationXXXHook问题解决
编译项目,会出现以下错误:
修改FreeRTOSConfig.h中的配置:
#define configCHECK_FOR_STACK_OVERFLOW 0
#define configUSE_IDLE_HOOK 0
#define configUSE_TICK_HOOK 0
#define configUSE_MALLOC_FAILED_HOOK 0
Systick硬件delay
修改systick.c源码,修改如下
#ifndef SYS_SUPPORT_OSvoid delay_1ms(uint32_t count){delay = count;while(0U != delay) {}}void delay_decrement(void){if(0U != delay) {delay--;}}#elsevoid delay_1us(uint32_t count) {uint32_t ticks;uint32_t told, tnow, reload, tcnt = 0;reload = SysTick->LOAD;ticks = count * (SystemCoreClock / 1000000);told = SysTick->VAL;while(1) {tnow=SysTick->VAL;if(tnow != told) {if(tnow<told) tcnt+=told-tnow;else tcnt+=reload-tnow+told;told=tnow;if(tcnt>=ticks)break;}}}void delay_1ms(uint32_t count){uint32_t i;for (i=0; i<count; i++){delay_1us(1000);}}#endif
LED点亮测试

开启任务,点亮PE3和PD7
在main.c中编写代码如下:
#include "gd32f4xx.h"#include "systick.h"#include <stdio.h>#include "main.h"#include "FreeRTOS.h"#include "task.h"TaskHandle_t StartTask_Handler;TaskHandle_t Task1_Handler;TaskHandle_t Task2_Handler;void task1(void *pvParameters) {while(1) {vTaskDelay(300);gpio_bit_set(GPIOE, GPIO_PIN_3);vTaskDelay(300);gpio_bit_reset(GPIOE, GPIO_PIN_3);}}void task2(void *pvParameters) {while(1) {vTaskDelay(1000);gpio_bit_set(GPIOD, GPIO_PIN_7);vTaskDelay(1000);gpio_bit_reset(GPIOD, GPIO_PIN_7);}}void start_task(void *pvParameters) {taskENTER_CRITICAL();xTaskCreate((TaskFunction_t)task1,(const char* )"task1",50,NULL,2,(TaskHandle_t* )&Task1_Handler);xTaskCreate((TaskFunction_t)task2,(const char* )"task2",50,NULL,2,(TaskHandle_t* )&Task2_Handler);vTaskDelete(StartTask_Handler);taskEXIT_CRITICAL();}void GPIO_config() {// 1. 时钟初始化rcu_periph_clock_enable(RCU_GPIOE);// 2. 配置GPIO 输入输出模式gpio_mode_set(GPIOE, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_3);// 3. 配置GPIO 模式的操作方式gpio_output_options_set(GPIOE, GPIO_OTYPE_PP, GPIO_OSPEED_MAX, GPIO_PIN_3);// 1. 时钟初始化rcu_periph_clock_enable(RCU_GPIOD);// 2. 配置GPIO 输入输出模式gpio_mode_set(GPIOD, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_7);// 3. 配置GPIO 模式的操作方式gpio_output_options_set(GPIOD, GPIO_OTYPE_PP, GPIO_OSPEED_MAX, GPIO_PIN_7);}int main(void){systick_config();GPIO_config();xTaskCreate((TaskFunction_t)start_task,(const char* )"start_task",128,NULL,1,(TaskHandle_t* )&StartTask_Handler);vTaskStartScheduler();while(1) {}}
HelloWorld
实现多任务串口打印功能。
开发流程:
- 通过模板创建项目
- 添加串口支持库
- 添加所需要c依赖,以及包含所需要h头
- 运行测试,查看效果。
```c
include “gd32f4xx.h”
include “systick.h”
include
include “main.h”
include “FreeRTOS.h”
include “task.h”
include “Usart0.h”
TaskHandle_t StartTask_Handler; TaskHandle_t Task1_Handler; TaskHandle_t Task2_Handler;
void task1(void *pvParameters) { while(1) { vTaskDelay(500); printf(“task 1\n”); } }
void task2(void *pvParameters) { while(1) { vTaskDelay(1000); printf(“task 2\n”); } }
void start_task(void *pvParameters) { taskENTER_CRITICAL();
xTaskCreate((TaskFunction_t)task1,(const char* )"task1",50,NULL,2,(TaskHandle_t* )&Task1_Handler);xTaskCreate((TaskFunction_t)task2,(const char* )"task2",50,NULL,2,(TaskHandle_t* )&Task2_Handler);vTaskDelete(StartTask_Handler);taskEXIT_CRITICAL();
}
void Usart0_recv(uint8_t *data, uint32_t len) { printf(“recv: %s\n”, data); }
int main(void) { nvic_priority_group_set(NVIC_PRIGROUP_PRE2_SUB2); systick_config(); Usart0_init();
xTaskCreate((TaskFunction_t)start_task,(const char* )"start_task",128,NULL,1,(TaskHandle_t* )&StartTask_Handler);vTaskStartScheduler();while(1) {}
}
```c#ifndef __USART0_H__#define __USART0_H__#include "gd32f4xx.h"#include "systick.h"#define USART0_RECV_CALLBACK 1void Usart0_init();void Usart0_send_data(uint8_t data);void Usart0_send_string(char *data);#if USART0_RECV_CALLBACKextern void Usart0_recv(uint8_t *data, uint32_t len);#endif#endif
#include "Usart0.h"#include <stdio.h>#define USART0_RECV_LEN 4096static uint8_t usart0_recv_buff[USART0_RECV_LEN]; // 接收缓冲区static uint32_t usart0_recv_len = 0;void Usart0_init() {uint32_t usartx = USART0;uint32_t usartx_rcu = RCU_USART0;uint32_t usartx_irqn = USART0_IRQn;uint32_t tx_gpio_rcu = RCU_GPIOA;uint32_t tx_gpio_port = GPIOA;uint32_t tx_gpio_pin = GPIO_PIN_9;uint32_t rx_gpio_rcu = RCU_GPIOA;uint32_t rx_gpio_port = GPIOA;uint32_t rx_gpio_pin = GPIO_PIN_10;////////// gpio config ///////////////// 配置时钟rcu_periph_clock_enable(tx_gpio_rcu);rcu_periph_clock_enable(rx_gpio_rcu);// 配置模式gpio_mode_set(tx_gpio_port, GPIO_MODE_AF, GPIO_PUPD_PULLUP, tx_gpio_pin);gpio_mode_set(rx_gpio_port, GPIO_MODE_AF, GPIO_PUPD_PULLUP, rx_gpio_pin);// 配置复用功能gpio_af_set(tx_gpio_port, GPIO_AF_7, tx_gpio_pin);gpio_af_set(rx_gpio_port, GPIO_AF_7, rx_gpio_pin);// 配置输出参数gpio_output_options_set(tx_gpio_port, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, tx_gpio_pin);gpio_output_options_set(rx_gpio_port, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, rx_gpio_pin);////////// usart config /////////////// 串口时钟rcu_periph_clock_enable(usartx_rcu);// USART复位usart_deinit(usartx);usart_baudrate_set(usartx, 115200);// 波特率usart_parity_config(usartx, USART_PM_NONE);// 校验位usart_word_length_set(usartx, USART_WL_8BIT);// 数据位数usart_stop_bit_set(usartx, USART_STB_1BIT);// 停止位usart_data_first_config(usartx, USART_MSBF_LSB);// 先发送高位还是低位// 发送功能配置usart_transmit_config(usartx, USART_TRANSMIT_ENABLE);// 接收功能配置usart_receive_config(usartx, USART_RECEIVE_ENABLE);// 接收中断配置nvic_irq_enable(usartx_irqn, 2, 2);// usart int rbneusart_interrupt_enable(usartx, USART_INT_RBNE);usart_interrupt_enable(usartx, USART_INT_IDLE);// 使能串口usart_enable(usartx);}void Usart0_send_data(uint8_t data) {//通过USART发送usart_data_transmit(USART0, data);//判断缓冲区是否已经空了while(RESET == usart_flag_get(USART0, USART_FLAG_TBE));}//发送字符串void Usart0_send_string(char *data) {while(data && *data) {Usart0_send_data((uint8_t)(*data));data++;}}void USART0_IRQHandler(void) {if ((usart_interrupt_flag_get(USART0, USART_INT_FLAG_RBNE)) == SET) {uint16_t value = usart_data_receive(USART0);usart0_recv_buff[usart0_recv_len] = value;usart0_recv_len++;// 接收到单个byte,可以在此处处理接收到value。TODO:}if (usart_interrupt_flag_get(USART0, USART_INT_FLAG_IDLE) == SET) {//读取缓冲区,清空缓冲区usart_data_receive(USART0);usart0_recv_buff[usart0_recv_len] = '\0';// 接收停止时,缓冲区中的数据,可以在此处处理缓存数据。TODO:#if USART0_RECV_CALLBACKUsart0_recv(usart0_recv_buff, usart0_recv_len);#endifusart0_recv_len = 0;}}int fputc(int ch, FILE *f){Usart0_send_data((uint8_t)ch);return ch;}
