准备工作
- GD32开发板。案例是以梁山派为开发板。
- Windows系统的电脑。当前是以Win11的电脑来实现案例的。
- Keil开发工具。并且已经安装好GD32依赖环境。
- FreeRTOS源码包。下载地址为: Releases · FreeRTOS/FreeRTOS · GitHub
当前以FreeRTOSv202212.01
版本为例。也是目前的最新版本。
移植流程
项目基础环境准备
参考: 工程模板创建 · 语雀
新建项目名称为: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语言条件不满足。我们将以上代码进行修改。
FreeRTOSConfig.h修改前的内容
#ifdef __ICCARM__
#include <stdint.h>
extern uint32_t SystemCoreClock;
#endif
FreeRTOSConfig.h修改后的内容
#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
,将函数通过宏定义包裹:
SYS_SUPPORT_OS宏定义
#ifndef SYS_SUPPORT_OS
#endif
修改后如下:
gd32f4xx_it.c文件修改
#ifndef SYS_SUPPORT_OS
void SVC_Handler(void)
{
}
#endif
...
#ifndef SYS_SUPPORT_OS
void PendSV_Handler(void)
{
}
#endif
...
#ifndef SYS_SUPPORT_OS
void SysTick_Handler(void)
{
delay_decrement();
}
#endif
配置C/C++(AC6)
中的define
为:USE_STDPERIPH_DRIVER,GD32F470,SYS_SUPPORT_OS
vApplicationXXXHook问题解决
编译项目,会出现以下错误:
修改FreeRTOSConfig.h
中的配置:
修改configCHECK_FOR_STACK_OVERFLOW
#define configCHECK_FOR_STACK_OVERFLOW 0
修改configUSE_IDLE_HOOK
#define configUSE_IDLE_HOOK 0
修改configUSE_TICK_HOOK
#define configUSE_TICK_HOOK 0
修改configUSE_MALLOC_FAILED_HOOK
#define configUSE_MALLOC_FAILED_HOOK 0
Systick硬件delay
修改systick.c
源码,修改如下
systick.c硬件延时
#ifndef SYS_SUPPORT_OS
void delay_1ms(uint32_t count)
{
delay = count;
while(0U != delay) {
}
}
void delay_decrement(void)
{
if(0U != delay) {
delay--;
}
}
#else
void 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中编写代码如下:
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头
- 运行测试,查看效果。
main.c
#include "gd32f4xx.h"
#include "systick.h"
#include <stdio.h>
#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) {}
}