FreeRTOS移植(保姆级教程)

前言

FreeRTOS是一个可以基于ROM运行的、可裁剪的、抢占式、实时多任务内核,具有高度可移植性,特点:公开源代码、可移植性、可固化、可裁剪、多任务、占先式,特别适合于微处理器和控制器,适合很多商业操作系统性能相当的实时操作系统(RTOS)。在使用GD32F103单片机项目移植过FreeRTOS,这里作为一个基础教学版简单记录一下移植过程,欢迎指正!

一、移植前的准备

<1>硬件平台:可运行软件程序的GD32单片机(本项目使用GD32F103VE硬件平台)

<2>软件平台:可直接下载运行的单片机基础工程,本例程是基于使用标准库GigaDevice.GD32F10x_DFP.2.0.3固件库编写

<3>源码获取:FreeRTOS源码(本例程使用源码版本为:FreeRTOS 202406.04 LTS)

下载链接:https://www.freertos.org/a00104.html

<4>ARM编译器版本:**ARM Compiler 6 (AC6后面均使用该缩写)。**AC6 编译速度更快,且是 Arm 未来的主流方向

二、移植

1.GD32 Keil工程

准备一个可以正常运行的GD32工程文件,这里使用的是自己建立的工程项目文件:

2. FreeRTOS源码

接下来我们开始移植。

3.FreeRTOS移植

1、在源码中我们主要关注下面几个FreeRTOS关键文件,后面都会用到:

2、首先在GD32工程文件中,找到合适的地方,新建"FreeRTOS"文件夹,并新建三个子文件夹"inc"、"src"、"port"用于存放上述文件:

3、将源码中".\FreeRTOS\FreeRTOS-Kernel\include"下所有.h文件拷贝到上面"./FreeRTOS/inc"文件夹下。

4、把下面7个.C文件拷贝到src目录下:

5、将配置文件的模板文件拷贝到"/FreeRTOS"目录下:

6、在接口层,我们主要适配内存管理部分和IC硬件上的接口部分,这两个位于".\FreeRTOS-Kernel\portable"下:

注意:"GCC"和"RVDS"是为编译器提供编译环境所需要的相关文件,我们编译器版本是AC6所以我们用"GCC"。如果使用的版本是AC5,选择"RVDS"。如果选错,编译时会上百个报错。删除后重新根据这个步骤添加一下就行(已踩坑),或者更改编译器版本。

"MemMang"中是FreeRTOS提供的5个内存分配方案:

我们需要根据自己的MCU类型去选择,例如我们使用的GD32F103系列是ARM架构、Cortex-M3的核、不带MPU的MCU,就仅保留"ARM_CM3"的文件夹:

最终我们操作后的相关文件如下:

接下来我们就要在编译器中将上述文件加载进来。

4. 工程操作

1、打开工程文件,新建工程资源文件夹"FreeRTOS/Src"和"FreeRTOS/Port",把C文件加载进来:

2、其中内存管理有5种方式,我们选择"heap_4":

3、将头文件包含进来

4、编译器使用V6:

到这里相关源码移植完成了,接下来就是代码操作部分,完成最后的接口适配工作

三. 代码操作

1、首先调整堆栈大小,打开启动文件"startup_gd32f10x_hd.s",我使用的GD32F103VE,堆栈设置为0x3000和0x1000,大家根据自己的项目需要和MCU能力自由分配:

2、打开载入配置文件,可以在main.c文件中导入"FreeRTOS.h",然后右键打开h文件:

3、就可以看到引用的"FreeRTOSConfig.h"文件,还是右键打开:

4、在"FreeRTOSConfig.h"文件中配置freeRTOS,官方模板文件中开启了很多的配置项,对于初学者,大部分我们并不太关心。针对gd32F103我做了如下修改,全部删除FreeRTOSConfig.h文件中的默认配置,直接复制粘贴以下内容替换(后续可根据自身需求更改):

bash 复制代码
/*
 * FreeRTOS Kernel V10.2.1
 * Copyright (C) 2019 Amazon.com, Inc. or its affiliates.  All Rights Reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 * http://www.FreeRTOS.org
 * http://aws.amazon.com/freertos
 *
 * 1 tab == 4 spaces!
 */


#ifndef FREERTOS_CONFIG_H
#define FREERTOS_CONFIG_H

#include "systick.h"
//针对不同的编译器调用不同的stdint.h文件
#if defined(__ICCARM__) || defined(__CC_ARM) || defined(__GNUC__)
  #include <stdint.h>
	#include <stdio.h>
    extern uint32_t SystemCoreClock;
#endif

//断言
#define vAssertCalled(char,int) printf("Error:%s,%d\r\n",char,int)
#define configASSERT(x) if((x)==0) vAssertCalled(__FILE__,__LINE__)

/***************************************************************************************************************/
/*                                        FreeRTOS基础配置配置选项                                              */
/***************************************************************************************************************/
#define configUSE_PREEMPTION					         	1                       //1使用抢占式内核,0使用协程
#define configUSE_TIME_SLICING					        	1						//1使能时间片调度(默认式使能的)
#define configUSE_PORT_OPTIMISED_TASK_SELECTION				1                       //1启用特殊方法来选择下一个要运行的任务
                                                                        
#define configUSE_TICKLESS_IDLE					            0                       //1启用低功耗tickless模式
#define configUSE_QUEUE_SETS					            1                       //为1时启用队列
#define configCPU_CLOCK_HZ						           (SystemCoreClock)        //CPU频率
#define configTICK_RATE_HZ						           (1000)                   //时钟节拍频率,这里设置为1000,周期就是1ms
#define configMAX_PRIORITIES					           (32)                     //可使用的最大优先级
#define configMINIMAL_STACK_SIZE				           ((unsigned short)130)    //空闲任务使用的堆栈大小
#define configMAX_TASK_NAME_LEN					           (16)                     //任务名字字符串长度

#define configUSE_16_BIT_TICKS					            0                       //系统节拍计数器变量数据类型,
                                                                        			//1表示为16位无符号整形,0表示为32位无符号整形
#define configIDLE_SHOULD_YIELD					            1                       //为1时空闲任务放弃CPU使用权给其他同优先级的用户任务
#define configUSE_TASK_NOTIFICATIONS                        1                       //为1时开启任务通知功能,默认开启
#define configUSE_MUTEXES						            1                       //为1时使用互斥信号量
#define configQUEUE_REGISTRY_SIZE				            8                       //不为0时表示启用队列记录,具体的值是可以
                                                                       				//记录的队列和信号量最大数目。
#define configCHECK_FOR_STACK_OVERFLOW			            0                       //大于0时启用堆栈溢出检测功能,如果使用此功能
                                                                        			//用户必须提供一个栈溢出钩子函数,如果使用的话
                                                                        			//此值可以为1或者2,因为有两种栈溢出检测方法。
#define configUSE_RECURSIVE_MUTEXES				            1                       //为1时使用递归互斥信号量
#define configUSE_MALLOC_FAILED_HOOK			            0                       //1使用内存申请失败钩子函数
#define configUSE_APPLICATION_TASK_TAG			            0                       
#define configUSE_COUNTING_SEMAPHORES			            1                       //为1时使用计数信号量

/***************************************************************************************************************/
/*                                FreeRTOS与内存申请有关配置选项                                                */
/***************************************************************************************************************/
#define configSUPPORT_DYNAMIC_ALLOCATION                    1                       //支持动态内存申请
#define configTOTAL_HEAP_SIZE					            ((size_t)(10*1024))     //系统所有总的堆大小

/***************************************************************************************************************/
/*                                FreeRTOS与钩子函数有关的配置选项                                              */
/***************************************************************************************************************/
#define configUSE_IDLE_HOOK						            0                       //1,使用空闲钩子;0,不使用
#define configUSE_TICK_HOOK						            0                       //1,使用时间片钩子;0,不使用
 
/***************************************************************************************************************/
/*                                FreeRTOS与运行时间和任务状态收集有关的配置选项                                 */
/***************************************************************************************************************/
#define configGENERATE_RUN_TIME_STATS	                    0                       //为1时启用运行时间统计功能
#define configUSE_TRACE_FACILITY				            1                       //为1启用可视化跟踪调试
#define configUSE_STATS_FORMATTING_FUNCTIONS	            1                       //与宏configUSE_TRACE_FACILITY同时为1时会编译下面3个函数
                                                                       
                                                                        
/***************************************************************************************************************/
/*                                FreeRTOS与协程有关的配置选项                                                  */
/***************************************************************************************************************/
#define configUSE_CO_ROUTINES 			                    0                       //为1时启用协程,启用协程以后必须添加文件croutine.c
#define configMAX_CO_ROUTINE_PRIORITIES                    (2)                      //协程的有效优先级数目

/***************************************************************************************************************/
/*                                FreeRTOS与软件定时器有关的配置选项                                            */
/***************************************************************************************************************/
#define configUSE_TIMERS				                    1                               //为1时启用软件定时器
#define configTIMER_TASK_PRIORITY		                    (configMAX_PRIORITIES-1)        //软件定时器优先级
#define configTIMER_QUEUE_LENGTH		                    5                               //软件定时器队列长度
#define configTIMER_TASK_STACK_DEPTH	                    (configMINIMAL_STACK_SIZE*2)    //软件定时器任务堆栈大小
    
/***************************************************************************************************************/
/*                                FreeRTOS可选函数配置选项                                                      */
/***************************************************************************************************************/
#define INCLUDE_xTaskGetSchedulerState                      1                       
#define INCLUDE_vTaskPrioritySet		                    1
#define INCLUDE_uxTaskPriorityGet		                    1
#define INCLUDE_vTaskDelete				                    1
#define INCLUDE_vTaskCleanUpResources	                    1
#define INCLUDE_vTaskSuspend			                    1
#define INCLUDE_vTaskDelayUntil			                    1
#define INCLUDE_vTaskDelay				                    1
#define INCLUDE_eTaskGetState			                    1
#define INCLUDE_xTimerPendFunctionCall	                    1
    
/***************************************************************************************************************/
/*                                FreeRTOS与中断有关的配置选项                                                  */
/***************************************************************************************************************/
#ifdef __NVIC_PRIO_BITS
	#define configPRIO_BITS       		                    __NVIC_PRIO_BITS
#else
	#define configPRIO_BITS       		                    4                  
#endif

#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY			    15	                    //中断最低优先级
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY	    5                       //系统可管理的最高中断优先级
#define configKERNEL_INTERRUPT_PRIORITY 		            (configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
#define configMAX_SYSCALL_INTERRUPT_PRIORITY 	            (configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )

/***************************************************************************************************************/
/*                                FreeRTOS与中断服务函数有关的配置选项                                          */
/***************************************************************************************************************/

#define xPortPendSVHandler 	  PendSV_Handler
//#define xPortSysTickHandler 	SysTick_Handler
#define vPortSVCHandler 	    SVC_Handler

#endif /* FREERTOS_CONFIG_H */

上述相关内容解释:

1.调整系统时钟

2.配置"configCPU_CLOCK_HZ"为"SystemCoreClock"

3."port.c"文件中适配接口函数

5、上面内容替换后,在工程中FreeRTOSConfig.h如下所示:

6、xPortSysTickHandler函数用于更新系统时基,我们把它放在系统滴答里,打开"gd32f10x_it.c"文件,首先添加头文件与函数声明:

7、滑到最后, port.c 和 gd32f10x_it.c 这两个文件中有重复定义的函数:PendSV_Handler()、SVC_Handler()和 Systick_Handler(),这里屏蔽掉 gd32f10x_it.c 中的 PendSV_Handler()、SVC_Handler()两个函数,然后在"SysTick_Handler"中添加新代码:

上述新代码:

bash 复制代码
#if (INCLUDE_xTaskGetSchedulerState  == 1 )
    if (xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED)
	{
#endif   
		xPortSysTickHandler();
#if (INCLUDE_xTaskGetSchedulerState  == 1 )
	}
#endif  

8、注释掉bsp_timer.c中SysTick_Handler实现,这个函数原本用于裸机的时间计数和任务调度,现在将由 FreeRTOS 接管,确保只 gd32f10x_it.c 中有 SysTick_Handler

(此处我的bsp_timer.c中有实现这个函数,如果你的代码没有相关内容,可以忽略该步骤)

9、配置并启动滴答定时器,打开systick.c文件,对void systick_config(void)做以下修改,如果提示未定义则添加#include "FreeRTOS.h"和#include "task.h"包含头文件

上述代码:

bash 复制代码
/*!
    \brief      configure systick
    \param[in]  none
    \param[out] none
    \retval     none
*/
void systick_config(void)
{
    /* setup systick timer for 1000Hz interrupts*/
    if (SysTick_Config(rcu_clock_freq_get(CK_SYS) / configTICK_RATE_HZ)){
        /* capture error */
        while (1){
        }
    }
    /* configure the systick handler priority */
    NVIC_SetPriority(SysTick_IRQn, 0x00U);
}

至此移植相关代码修改完成。

编译运行0警告0错误:

接下来就是修改任务调度器,让FreeRTOS全权托管了

四、Hello FreeRTOS,创建任务启动任务

1、任务优先级,堆栈和句柄

bash 复制代码
//test
#define tesTask_PRIO				(tskIDLE_PRIORITY + 1)
#define tesTask_STK_SIZE 		256 
TaskHandle_t tesTask_Handler;

//
#define LedTask_PRIO		(tskIDLE_PRIORITY + 1)
#define LedTask_STK_SIZE 		256  
TaskHandle_t LedTask_Handler;

2、任务创建函数

bash 复制代码
/**********************************************************************************************************
* 函数名: RTOS_Init
* 功能说明: RTOS初始化,用于创建任务
* 形参:无
* 返回值: 无
**********************************************************************************************************/
void RTOS_Init(void)
{
		taskENTER_CRITICAL();           //进入临界区
	
		//测试任务
		xTaskCreate((TaskFunction_t )tesTask,     	
								(const char*    )"tesTask",   	
								(uint16_t       )tesTask_STK_SIZE, 
								(void*          )NULL,				
								(UBaseType_t    )tesTask_PRIO,	
								(TaskHandle_t*  )NULL); 
	
								
		//LED任务
		xTaskCreate((TaskFunction_t )LedTask,     	
								(const char*    )"LedTask",   	
								(uint16_t       )LedTask_STK_SIZE, 
								(void*          )NULL,				
								(UBaseType_t    )LedTask_PRIO,	
								(TaskHandle_t*  )NULL); 
								
		//扩展任务模块
        //。。。。。。
								
								
		taskEXIT_CRITICAL();            //退出临界区
		vTaskStartScheduler();          //开启任务调度
}

3、两个任务实现部分

bash 复制代码
#include "FreeRTOS.h"
#include "task.h"


/*
* Description : Test
* Paramter    : None
* Return      : None
*/
void tesTask()
{
    
	while(1)
	{
        printf("Hello FreeRTOS\r\n");

		vTaskDelay(1000);
	}
}


/*
* Description : LED
* Paramter    : None
* Return      : None
*/
void LedTask(void)
{
    static U08 count1 = 0;
    static U08 count2 = 0;
	
	while(1)
	{
			if (SYS_FLT == 1)
			{
					count1 = (count1 + 1) % 10;
					count2 = 0;

					// 系统故障,红灯闪烁
					if (count1 <= 3)
					{
							LED_GREEN_OFF();
							LED_RED_ON();
					}
					else
					{
							LED_GREEN_OFF();
							LED_RED_OFF();
					}
			}
			else if (SYS_RUN == 1)
			{
					count1 = 0;
					count2 = (count2 + 1) % 10;

					// 运行状态,绿灯闪烁
					if (count2 <= 3)
					{
							LED_GREEN_ON();
							LED_RED_OFF();
					}
					else
					{
							LED_GREEN_OFF();
							LED_RED_OFF();
					}
			}
			else
			{
					count1 = 0;
					count2 = 0;

					// 空闲状态
					LED_GREEN_ON();
					LED_RED_OFF();
			}
		
     vTaskDelay(100);
	}
}

4、在主函数中完成调用

bash 复制代码
/*
* Description : Main
* Parameter   : None
* Return      : None
*/
int main(void)
{
	System_Init();       //自定义函数,内部包含其他初始化模块
	RTOS_Init();         //RTOS任务创建
}

最后编译通过,0警告0错误。

接下来大家就可以嗨皮的学习FreeRTOS的各类组件了。

相关推荐
LCG元3 小时前
STM32实战:基于STM32F103的智能消防报警联动系统
stm32·单片机·嵌入式硬件
爱写代码的倒霉蛋3 小时前
天梯赛备赛经验分享(基础版)
经验分享·算法
ivy159868377153 小时前
CH32V203G6U6 ‌沁恒32位 RISC-V 微控制器(MCU)‌
单片机·嵌入式硬件·risc-v
LaughingZhu4 小时前
Product Hunt 每日热榜 | 2026-04-21
人工智能·经验分享·深度学习·神经网络·产品运营
blevoice4 小时前
杰理蓝牙音箱开发板AC696N上演示降本设计:AC696N“省晶振”方案配置
单片机·嵌入式硬件·jl杰理蓝牙音频芯片·杰理ac696n开发板·ac6966b蓝牙音响芯片·蓝牙芯片ble透传·杰理蓝牙音箱方案开发
蛋白界小百灵4 小时前
非洲猪瘟I177L蛋白原核表达与单抗制备技术解析-卡梅德
经验分享·科技
Ww.xh4 小时前
STM32+ESP8266智能农业系统开发指南
stm32·单片机·嵌入式硬件
yong99904 小时前
在 STC15W201S 上实现 MODBUS RTU 协议
stm32·单片机·嵌入式硬件
xzl045 小时前
瑞萨 FSP 和 STM32 HAL 库的启动流程核心差异
stm32·单片机·嵌入式硬件·rt-thread