GD32 GCC 环境 移植FreeRTOS

一、引言

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-and-software/open-source-software/developer-tools/gnu-toolchain/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.youkuaiyun.com/pigliuxu/article/details/145289861?spm=1001.2014.3001.5502

3.5 编译和下载

在命令行中进入项目根目录,执行 make 命令进行编译。如果编译成功,会在 build 目录下生成 output.elfoutput.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/includeFreeRTOS/srcFreeRTOS/portable 文件夹复制到项目的 FreeRTOS 目录下。

4.2 移除不必要的文件

FreeRTOS/portable 文件夹中,根据使用的编译器和硬件平台,只保留需要的文件。对于 GCC 和 GD32(Cortex-M3 内核),需要保留 GCCMemMang 文件夹,其他文件夹可以删除。

4.3 添加 FreeRTOS 源码到项目

Makefile 中添加 FreeRTOS 源码的编译规则。修改 CFLAGSSRCS 变量,如下所示:

# 编译选项
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_HandlerPendSV_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_HandlerPendSV_HandlerSVC_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 );
  • pxTaskCode:任务的入口函数指针。
  • pcName:任务的名称,用于调试和日志记录。
  • usStackDepth:任务的栈大小。
  • pvParameters:传递给任务的参数。
  • uxPriority:任务的优先级。
  • pxCreatedTask:任务句柄,用于后续对任务的操作。

调用 vTaskStartScheduler() 函数启动 FreeRTOS 调度器,开始任务调度。

六、常见问题及解决方法

6.1 编译错误

  • 找不到头文件:检查 CFLAGS 中的头文件包含路径是否正确,确保 FreeRTOS 和 GD32 标准外设库的头文件路径都已添加。
  • 函数未定义:检查是否正确添加了 FreeRTOS 的源文件,确保 port.cheap_4.c 等文件已包含在编译列表中。

6.2 运行错误

  • 任务无法切换:检查 SysTick_HandlerPendSV_Handler 中断处理函数是否正确实现,确保在中断处理函数中调用了 FreeRTOS 提供的相应函数。
  • 内存不足:检查 configTOTAL_HEAP_SIZE 参数是否设置过小,适当增大堆的大小。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值