详细GD32F103的FreeRTOS移植含常见错误
目录
1、FreeRTO官方源码获取
2、新建相关文件夹
3、复制相关文件
4、文件加入到工程
5、DEBUG工程适配
1、FreeRTO官方源码获取
FreeRTOS源码的官方下载地址: FreeRTOS官方下载链接。
选择你需要的版本:我这选择的是最新版本即202411。
这里有三个文件,其中我们可以下载ZIP也可以下载EXE的。
下载一个即可,运行EXE或者解压ZIP均会获得源码
在官方源码文件夹中,主要包含两个目录:FreeRTOS
和 FreeRTOS-Plus
。其中,FreeRTOS
文件夹是移植过程中需要重点关注的部分,它包含了 FreeRTOS 内核的源码以及各种 Demo 例程。而 FreeRTOS-Plus
文件夹则包含了一些第三方扩展组件,通常在移植过程中不会使用。
打开 FreeRTOS
文件夹后,可以看到其中主要包含以下三个子文件夹。以下是主要文件夹和文件的文件树结构总结:
2、新建相关文件夹
打开GD32F103的例程,并在其新建FreeRTOS文件夹。
打开FreeRTOSv202411.00\FreeRTOS\Source文件夹,
3、复制相关文件
(1)将include文件夹复制到GD32F103freertos\FreeRTOS
(2)在GD32F103freertos\FreeRTOS下新建portable文件夹,并将FreeRTOSv202411.00\FreeRTOS\Source\portable下的Keil、MemMang、RVDS复制到GD32F103freertos\FreeRTOS\portable
(3)将FreeRTOSv202411.00\FreeRTOS\Source下的使用.c文件复制到GD32F103freertos\FreeRTOS。注意FreeRTOS版本不同所对应的.c文件数量不同。
将FreeRTOSv202411.00\FreeRTOS\Demo\CORTEX_STM32F103_Keil下的FreeRTOSConfig.h复制到GD32F103freertos\User下
4、文件加入到工程
打开工程,点击文件管理
新建FreeROTS_CORE,并将GD32F103freertos\FreeRTOS下的使用.c添加到其中
再新建FreeROTS_PORTABLE,选择GD32F103freertos\FreeRTOS\portable\MemMang所需的内存管理方案
所对应的芯片型号GD32F103freertos\FreeRTOS\portable\RVDS\ARM_CM3添加到其中。
添加头文件地址
5、DEBUG工程适配
编写测试代码main.c文件
#include "gd32f10x.h"
#include "gd32f10x_libopt.h"
#include "systick.h"
#include <stdio.h>
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "semphr.h"
#include "croutine.h"
#include "portmacro.h"
//任务优先级
#define START_TASK_PRIO 1
//任务堆栈大小
#define START_STK_SIZE 128
//任务句柄
TaskHandle_t StartTask_Handler;
//任务函数
void start_task(void *pvParameters);
//任务优先级
#define LED0_TASK_PRIO 2
//任务堆栈大小
#define LED0_STK_SIZE 128
//任务句柄
TaskHandle_t LED0Task_Handler;
//任务函数
void led0_task(void *pvParameters);
//任务优先级
#define LED1_TASK_PRIO 3
//任务堆栈大小
#define LED1_STK_SIZE 128
//任务句柄
TaskHandle_t LED1Task_Handler;
//任务函数
void led1_task(void *pvParameters);
int main(void)
{
rcu_apb2_clock_config(RCU_APB2_CKAHB_DIV1);//设置主频108M(#define __SYSTEM_CLOCK_108M_PLL_HXTAL (uint32_t)(108000000)),8M外部晶振 (#define HXTAL_VALUE ((uint32_t)8000000))
systick_config();//配置1ms SysTick
rcu_periph_clock_enable(RCU_AF);//AF时钟使能
gpio_pin_remap_config(GPIO_SWJ_NONJTRST_REMAP, ENABLE);//下载口NJTRST引脚当做普通I/O口
rcu_periph_clock_enable(RCU_GPIOB);//GPIOB时钟打开
gpio_init(GPIOB, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_4);//PB4配置成输出
rcu_periph_clock_enable(RCU_GPIOB);//GPIOB时钟打开
gpio_init(GPIOB, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_5);//PB4配置成输出
//创建开始任务
xTaskCreate((TaskFunction_t )start_task, //任务函数
(const char* )"start_task", //任务名称
(uint16_t )START_STK_SIZE, //任务堆栈大小
(void* )NULL, //传递给任务函数的参数
(UBaseType_t )START_TASK_PRIO, //任务优先级
(TaskHandle_t* )&StartTask_Handler); //任务句柄
vTaskStartScheduler(); //开启任务调度
}
//开始任务任务函数
void start_task(void *pvParameters)
{
taskENTER_CRITICAL(); //进入临界区
//创建LED0任务
xTaskCreate((TaskFunction_t )led0_task,
(const char* )"led0_task",
(uint16_t )LED0_STK_SIZE,
(void* )NULL,
(UBaseType_t )LED0_TASK_PRIO,
(TaskHandle_t* )&LED0Task_Handler);
//创建LED1任务
xTaskCreate((TaskFunction_t )led1_task,
(const char* )"led1_task",
(uint16_t )LED1_STK_SIZE,
(void* )NULL,
(UBaseType_t )LED1_TASK_PRIO,
(TaskHandle_t* )&LED1Task_Handler);
vTaskDelete(StartTask_Handler); //删除开始任务
taskEXIT_CRITICAL(); //退出临界区
}
//LED0任务函数
void led0_task(void *pvParameters)
{
while(1)
{
gpio_bit_set(GPIOB, GPIO_PIN_4);
vTaskDelay(500);
gpio_bit_reset(GPIOB, GPIO_PIN_4);
vTaskDelay(500);
}
}
//LED1任务函数
void led1_task(void *pvParameters)
{
while(1)
{
gpio_bit_set(GPIOB, GPIO_PIN_5);
vTaskDelay(800);
gpio_bit_reset(GPIOB, GPIO_PIN_5);
vTaskDelay(800);
}
}
打开"FreeRTOSConfig.h"进行如下更改:
#define configCPU_CLOCK_HZ SystemCoreClock
#define vPortSVCHandler SVC_Handler
#define xPortPendSVHandler PendSV_Handler
进行编译后::
报错: No space in execution regions with ...
原因:空间不够
解决:打开"FreeRTOSConfig.h"进行如下更改,将栈空间减小
//#define configTOTAL_HEAP_SIZE ( ( size_t ) ( 17 * 1024 ) )
#define configTOTAL_HEAP_SIZE ( ( size_t ) ( 2 * 1024 ) )
报错:Symbol SVC_Handler multiply defined (by port.o and gd32f10x_it.o).
Symbol PendSV_Handler multiply defined (by port.o and gd32f10x_it.o).
原因:函数被多处定义(需要调用FreeRTOS的函数舍去源码的相关函数)
解决:打开gd32f10x_it.c,注释如下函数
//void SVC_Handler(void)
//{
//}
//void PendSV_Handler(void)
//{
//}
报错:identifier "SystemCoreClock" is undefined
原因:未找到SystemCoreClock定义(这是系统时钟,RTOS需要调用,要指明它在何处)
解决:打开"FreeRTOSConfig.h"添加如下代码
#if defined(__ICCARM__) || defined(__CC_ARM) || defined(__GNUC__)
#include <stdint.h>
extern uint32_t SystemCoreClock;
#endif
报错:Undefined symbol vApplicationIdleHook (referred from tasks.o).
Undefined symbol vApplicationTickHook (referred from tasks.o).
Undefined symbol vApplicationMallocFailedHook (referred from heap_4.o).
Undefined symbol vApplicationStackOverflowHook (referred from tasks.o).
原因:相关钩子函数为找到
解决:打开"FreeRTOSConfig.h"进行如下更改
#define configUSE_IDLE_HOOK 0 //1
#define configUSE_TICK_HOOK 0 //1
#define configUSE_MALLOC_FAILED_HOOK 0 //1
#define configCHECK_FOR_STACK_OVERFLOW 0 //2
当解决上面错误是,出现了无报错无警告,但是调度器开启失败的情况,这是可能是因为滴答定时器相关配置出现了问题。
让我们打开gd32f10x_it.c对SysTick_Handler函数注释,在main.c文件中添加如下代码
//void SysTick_Handler(void)
//{
// delay_decrement();
//}
#include "FreeRTOS.h"
#include "task.h"
extern void xPortSysTickHandler(void);
void SysTick_Handler(void)
{
if (xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED) {
xPortSysTickHandler();
}
}
注意此函数是滴答定时器的中断服务函数
FreeRTOS的心跳就是由滴答定时器产生的,根据FreeRTOS的系统时钟节拍设置好滴答定时器的周期,这样就会周期触发滴答定时器中断了。在滴答定时器中断服务函数中调用FreeRTOS的API函数。
报错:.Undefined symbol xTaskGetSchedulerState
原因:未显式启用 xTaskGetSchedulerState()
函数
解决:打开"FreeRTOSConfig.h"添加如下代码
#define INCLUDE_xTaskGetSchedulerState 1
//或者加入如下代码:
/*
#define configUSE_TIMERS 1
#define configTIMER_TASK_PRIORITY ( 2 )
#define configTIMER_QUEUE_LENGTH 10
#define configTIMER_TASK_STACK_DEPTH ( configMINIMAL_STACK_SIZE * 2 )
*/
最后,编译、烧入、运行,成功移植!