一、引言
FreeRTOS 作为一款开源、轻量级且功能强大的 RTOS,被广泛应用于各类嵌入式项目。GD32 系列微控制器是兆易创新推出的高性能、低成本的 ARM Cortex 内核微控制器,具有丰富的外设和出色的性能。本文将详细介绍如何在 GCC 环境下将 FreeRTOS 移植到 GD32 微控制器上。
二、准备工作
2.1 硬件准备
GD32 开发板:选择合适的 GD32 开发板,例如 GD32F103 系列开发板,该系列具有较高的性价比和广泛的应用场景。
调试工具:准备好 JTAG 或 SWD 调试器,如 J-Link 或 ST-Link,用于程序的下载和调试。
2.2 软件准备
GCC 工具链:安装适用于 ARM Cortex-M 内核的 GCC 工具链,例如 GNU Arm Embedded Toolchain。可以从官方网站(https://developer.arm.com/tools- ... in/gnu-rm/downloads)下载对应操作系统的版本并进行安装。
OpenOCD:用于与调试器进行通信,实现程序的下载和调试。可以从 OpenOCD 官方网站(http://openocd.org/)下载并安装。
GD32 标准外设库:从兆易创新官方网站下载 GD32 标准外设库,该库包含了 GD32 微控制器的外设驱动代码和示例程序。
FreeRTOS 源码:从 FreeRTOS 官方网站(https://www.freertos.org/)下载最新的 FreeRTOS 源码包。
2.3 开发环境搭建
为了方便开发,推荐使用 Visual Studio Code 作为代码编辑器,并安装相关的扩展,如 Cortex-Debug、C/C++ 等。同时,配置好 GCC 工具链和 OpenOCD 的路径,确保在命令行中可以正常使用。
三、创建 GD32 基础工程
3.1 项目目录结构
首先,创建一个新的项目目录,例如 GD32_FreeRTOS,并在该目录下创建以下子目录:
src:用于存放项目的源文件。
include:用于存放项目的头文件。
FreeRTOS:用于存放 FreeRTOS 的源码。
GD32_Library:用于存放 GD32 标准外设库。
build:用于存放编译生成的中间文件和最终的可执行文件。
3.2 配置 GD32 标准外设库
将下载的 GD32 标准外设库解压到 GD32_Library 目录下。在 src 目录下创建一个 main.c 文件,用于编写主程序。同时,在 include 目录下创建一个 config.h 文件,用于配置项目的一些参数。
3.3 编写基础代码
以下是一个简单的 main.c 文件示例,用于初始化系统时钟和 GPIO 引脚,并让一个 LED 闪烁:
#include "gd32f10x.h"
#include "config.h"
void delay(uint32_t count) {
for (uint32_t i = 0; i < count; i++);
}
int main(void) {
rcu_periph_clock_enable(RCU_GPIOA);
gpio_init(GPIOA, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_8);
while (1) {
gpio_bit_set(GPIOA, GPIO_PIN_8);
delay(0xFFFFF);
gpio_bit_reset(GPIOA, GPIO_PIN_8);
delay(0xFFFFF);
}
}
3.4 GCC工程环境搭建
参考链接
https://blog.csdn.net/pigliuxu/a ... 1001.2014.3001.5502
3.5 编译和下载
在命令行中进入项目根目录,执行 make 命令进行编译。如果编译成功,会在 build 目录下生成 output.elf 和 output.bin 文件。使用 OpenOCD 和 GDB 进行程序的下载和调试,以下是一个简单的下载命令示例:
openocd -f interface/jlink.cfg -f target/gd32f1x0.cfg &
arm-none-eabi-gdb build/output.elf
(gdb) target remote localhost:3333
(gdb) load
(gdb) continue
四、移植 FreeRTOS 到 GD32
4.1 复制 FreeRTOS 源码
将下载的 FreeRTOS 源码包解压,将 FreeRTOS/include、FreeRTOS/src 和 FreeRTOS/portable 文件夹复制到项目的 FreeRTOS 目录下。
4.2 移除不必要的文件
在 FreeRTOS/portable 文件夹中,根据使用的编译器和硬件平台,只保留需要的文件。对于 GCC 和 GD32(Cortex-M3 内核),需要保留 GCC 和 MemMang 文件夹,其他文件夹可以删除。
4.3 添加 FreeRTOS 源码到项目
在 Makefile 中添加 FreeRTOS 源码的编译规则。修改 CFLAGS 和 SRCS 变量,如下所示:
# 编译选项
CFLAGS = -mcpu=cortex-m3 -mthumb -O0 -g -Wall
CFLAGS += -I$(INCLUDE_DIR) -I$(GD32_LIB_DIR)/Include -I$(FREE_RTOS_DIR)/include -I$(FREE_RTOS_DIR)/portable/GCC/ARM_CM3
# 源文件和目标文件
FREE_RTOS_DIR = FreeRTOS
SRCS = $(wildcard $(SRC_DIR)/*.c) $(wildcard $(FREE_RTOS_DIR)/src/*.c) $(FREE_RTOS_DIR)/portable/GCC/ARM_CM3/port.c $(FREE_RTOS_DIR)/portable/MemMang/heap_4.c
OBJS = $(patsubst %.c, $(BUILD_DIR)/%.o, $(SRCS))
4.4 创建 FreeRTOS 配置文件
在 include 目录下创建一个 FreeRTOSConfig.h 文件,用于配置 FreeRTOS 的各种参数。以下是一个基本的 FreeRTOSConfig.h 文件示例:
#ifndef FREERTOS_CONFIG_H
#define FREERTOS_CONFIG_H
#include "gd32f10x.h"
// 定义系统时钟频率
#define configCPU_CLOCK_HZ ((unsigned long)72000000)
// 定义空闲任务栈大小
#define configMINIMAL_STACK_SIZE ((unsigned short)128)
// 定义堆大小
#define configTOTAL_HEAP_SIZE ((size_t)(10 * 1024))
// 定义任务优先级数量
#define configMAX_PRIORITIES ((unsigned char)5)
// 定义 tick 中断频率
#define configTICK_RATE_HZ ((TickType_t)1000)
// 使能抢占式调度器
#define configUSE_PREEMPTION 1
// 使能时间片调度
#define configUSE_TIME_SLICING 1
// 使能空闲钩子函数
#define configUSE_IDLE_HOOK 0
// 使能 tick 钩子函数
#define configUSE_TICK_HOOK 0
// 定义中断优先级分组
#define configKERNEL_INTERRUPT_PRIORITY ( 7 << 4 )
#define configMAX_SYSCALL_INTERRUPT_PRIORITY ( 5 << 4 )
// 使能任务统计信息功能
#define configGENERATE_RUN_TIME_STATS 0
// 定义队列和信号量相关宏
#define configUSE_QUEUE_SETS 1
#define configUSE_MUTEXES 1
#define configUSE_RECURSIVE_MUTEXES 1
#define configUSE_COUNTING_SEMAPHORES 1
// 其他配置选项
#define INCLUDE_vTaskPrioritySet 1
#define INCLUDE_uxTaskPriorityGet 1
#define INCLUDE_vTaskDelete 1
#define INCLUDE_vTaskSuspend 1
#define INCLUDE_xResumeFromISR 1
#endif /* FREERTOS_CONFIG_H */
4.5 修改中断处理函数
在 FreeRTOS 中,需要对一些中断处理函数进行修改,以确保 FreeRTOS 能够正常工作。主要需要修改的中断处理函数包括 SysTick_Handler 和 PendSV_Handler。在 src 目录下创建一个 port.c 文件,添加以下代码:
#include "FreeRTOS.h"
#include "task.h"
extern void xPortPendSVHandler(void);
extern void vPortSVCHandler(void);
extern void xPortSysTickHandler(void);
void SysTick_Handler(void) {
if (xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED) {
xPortSysTickHandler();
}
}
void PendSV_Handler(void) {
xPortPendSVHandler();
}
void SVC_Handler(void) {
vPortSVCHandler();
}
4.6 编写 FreeRTOS 任务代码
修改 main.c 文件,编写 FreeRTOS 任务代码。以下是一个简单的示例,创建两个任务,一个任务控制 LED 闪烁,另一个任务打印信息:
#include "gd32f10x.h"
#include "config.h"
#include "FreeRTOS.h"
#include "task.h"
void vTaskLED(void *pvParameters) {
rcu_periph_clock_enable(RCU_GPIOA);
gpio_init(GPIOA, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_8);
while (1) {
gpio_bit_set(GPIOA, GPIO_PIN_8);
vTaskDelay(pdMS_TO_TICKS(500));
gpio_bit_reset(GPIOA, GPIO_PIN_8);
vTaskDelay(pdMS_TO_TICKS(500));
}
}
void vTaskPrint(void *pvParameters) {
while (1) {
printf("Hello, FreeRTOS!\n");
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
int main(void) {
xTaskCreate(vTaskLED, "LED Task", configMINIMAL_STACK_SIZE, NULL, 1, NULL);
xTaskCreate(vTaskPrint, "Print Task", configMINIMAL_STACK_SIZE, NULL, 1, NULL);
vTaskStartScheduler();
while (1) {
// 不会执行到这里
}
}
4.7 编译和下载
再次执行 make 命令进行编译,然后使用 OpenOCD 和 GDB 进行程序的下载和调试。如果一切正常,LED 会开始闪烁,并且在串口终端上会输出 Hello, FreeRTOS! 信息。
五、关键代码解析
5.1 FreeRTOSConfig.h
FreeRTOSConfig.h 文件是 FreeRTOS 的配置文件,用于配置 FreeRTOS 的各种参数。以下是一些重要参数的解析:
configCPU_CLOCK_HZ:定义系统时钟频率,用于计算任务的时间片和定时器的时间间隔。
configMINIMAL_STACK_SIZE:定义空闲任务的栈大小,每个任务都需要有自己的栈空间。
configTOTAL_HEAP_SIZE:定义 FreeRTOS 堆的大小,用于动态内存分配。
configMAX_PRIORITIES:定义任务的最大优先级数量,优先级范围从 0 到 configMAX_PRIORITIES - 1。
configTICK_RATE_HZ:定义 tick 中断的频率,即系统时钟节拍的频率。
5.2 中断处理函数
SysTick_Handler、PendSV_Handler 和 SVC_Handler 是 FreeRTOS 中重要的中断处理函数。
SysTick_Handler:用于处理 SysTick 定时器中断,在定时器中断中调用 xPortSysTickHandler() 函数,更新系统时钟节拍。
PendSV_Handler:用于处理 PendSV 中断,PendSV 中断用于任务切换,调用 xPortPendSVHandler() 函数进行任务上下文切换。
SVC_Handler:用于处理 SVC 中断,SVC 中断用于系统调用,调用 vPortSVCHandler() 函数处理系统调用。
5.3 任务创建和调度
在 main.c 文件中,使用 xTaskCreate() 函数创建任务,该函数的原型如下:
BaseType_t xTaskCreate( TaskFunction_t pxTaskCode,
const char * const pcName,
configSTACK_DEPTH_TYPE usStackDepth,
void *pvParameters,
UBaseType_t uxPriority,
TaskHandle_t *pxCreatedTask );
1
2
3
4
5
6
pxTaskCode:任务的入口函数指针。
pcName:任务的名称,用于调试和日志记录。
usStackDepth:任务的栈大小。
pvParameters:传递给任务的参数。
uxPriority:任务的优先级。
pxCreatedTask:任务句柄,用于后续对任务的操作。
调用 vTaskStartScheduler() 函数启动 FreeRTOS 调度器,开始任务调度。
六、常见问题及解决方法
6.1 编译错误
找不到头文件:检查 CFLAGS 中的头文件包含路径是否正确,确保 FreeRTOS 和 GD32 标准外设库的头文件路径都已添加。
函数未定义:检查是否正确添加了 FreeRTOS 的源文件,确保 port.c 和 heap_4.c 等文件已包含在编译列表中。
6.2 运行错误
任务无法切换:检查 SysTick_Handler 和 PendSV_Handler 中断处理函数是否正确实现,确保在中断处理函数中调用了 FreeRTOS 提供的相应函数。
内存不足:检查 configTOTAL_HEAP_SIZE 参数是否设置过小,适当增大堆的大小。
————————————————
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/pigliuxu/article/details/145637123
|