【超级详细!】手把手教你STM32移植FreeRTOS系统!
本文详细介绍了将FreeRTOS最新版(202406.01LTS)移植到STM32F407ZGT6开发板的完整过程。主要内容包括:1)源码获取途径;2)工程文件精简方法,保留必要内核文件;3)Keil环境配置步骤,包含9个关键.c文件和头文件路径设置;4)FreeRTOSConfig.h配置详解及修改建议;5)中断服务函数处理注意事项;6)应用测试示例,展示队列通信功能实现。文中提供了完整的配置代
目录
一、概述
本章手把手教学最新版FreeRTOS库移植到STM32F407ZGT6最小系统板中,其余型号的STM32单片机区别不大,换几个文件即可。开始前请准备好一个基础的例程,最好是串口和LED作为检验操作系统移植的正确性。
FreeRTOS版本: 202406.01LTS
开发板:STM32F407ZGT6
编译环境:Keil 5
二、源码获取
1、FreeRTOS官网
官网链接:FreeRTOS™ - FreeRTOS™
https://www.freertos.org/zh-cn-cmn-s/

2、RTOS源码以及完整工程
通过网盘分享的文件:FreeRTOS工程
三、移植
1、解压
解压后来到这个目录下
examples ---> FreeRTOSConfig配置例程
include --->头文件,包含使用即可
portable ---> 不同环境下选择的内核不同

2、精简FreeRTOS-Kernel文件
路径:FreeRTOSv202406.01-LTS\FreeRTOS-LTS\FreeRTOS\FreeRTOS-Kernel
删除其余文件,保留需要用到的.c文件

3、精简portable文件
路径:FreeRTOSv202406.01-LTS\FreeRTOS-LTS\FreeRTOS\FreeRTOS-Kernel\portable
这里只保留MemMang文件和RVDS文件,其余文件全部删掉

删除后的效果图

当精简完只有MemMang文件和RVDS文件的时候,我们先进入MemMang文件,这里是内存管理内核部分,常用的是heap4文件,如果需要切换直接保留需要的即可,这里我们只用heap4

只保留heap4的效果图

随后我们来到RVDS文件,这里的文件对应不同的内核,这里我们选用的是STM32F407ZGT6单片机,内核架构是Cortex-M4,带浮点数版本,所以选择ARM_CM4F ,如果是STM32F103,选择M3即可

删除后的效果图

4、打开keil
首选确保你的工程文件能够正常编译,并且把FreeRTOS-Kernel文件放到keil工程目录下

5、移植.C文件
首先移植部分内核文件

接下来移植RVDS文件和MemMang文件,因为之前已经做过裁剪,所以直接选择即可。MemMang文件中的heap_4.c 和 RVDS\ARM_CM4F文件中的port.c

如下图是.c文件移植完成后的结果,一共9个.c文件

6、包含.h文件
这里先包含include文件

随后我们在包含portable\RVDS\ARM_CM4F路径中的文件

最后我们把examples\template_configuration文件中的FreeRTOSConfig.h包含进来

7、FreeRTOSConfig.h文件替换
打开FreeRTOSConfig.h文件,直接把如下的代码对该文件进行替换即可,博主已经对文件进行了修改。
#ifndef FREERTOS_CONFIG_H
#define FREERTOS_CONFIG_H
/* 头文件 */
#include "stm32f4xx.h"
#include <stdint.h>
#include <stdio.h>
#include <stdbool.h>
extern uint32_t SystemCoreClock;
/* 基础配置项 */
#define configUSE_PREEMPTION 1 /* 1: 抢占式调度器, 0: 协程式调度器, 无默认需定义 */
#define configUSE_PORT_OPTIMISED_TASK_SELECTION 1 /* 1: 使用硬件计算下一个要运行的任务, 0: 使用软件算法计算下一个要运行的任务, 默认: 0 */
#define configUSE_TICKLESS_IDLE 0 /* 1: 使能tickless低功耗模式, 默认: 0 */
#define configCPU_CLOCK_HZ SystemCoreClock /* 定义CPU主频, 单位: Hz, 无默认需定义 */
//#define configSYSTICK_CLOCK_HZ (configCPU_CLOCK_HZ / 8)/* 定义SysTick时钟频率,当SysTick时钟频率与内核时钟频率不同时才可以定义, 单位: Hz, 默认: 不定义 */
#define configTICK_RATE_HZ 1000 /* 定义系统时钟节拍频率, 单位: Hz, 无默认需定义 */
#define configMAX_PRIORITIES 10 /* 定义最大优先级数, 最大优先级=configMAX_PRIORITIES-1, 无默认需定义 */
#define configMINIMAL_STACK_SIZE 128 /* 定义空闲任务的栈空间大小, 单位: Word, 无默认需定义 */
#define configMAX_TASK_NAME_LEN 16 /* 定义任务名最大字符数, 默认: 16 */
#define configUSE_16_BIT_TICKS 0 /* 1: 定义系统时钟节拍计数器的数据类型为16位无符号数, 无默认需定义 */
#define configIDLE_SHOULD_YIELD 1 /* 1: 使能在抢占式调度下,同优先级的任务能抢占空闲任务, 默认: 1 */
#define configUSE_TASK_NOTIFICATIONS 1 /* 1: 使能任务间直接的消息传递,包括信号量、事件标志组和消息邮箱, 默认: 1 */
#define configTASK_NOTIFICATION_ARRAY_ENTRIES 1 /* 定义任务通知数组的大小, 默认: 1 */
#define configUSE_MUTEXES 1 /* 1: 使能互斥信号量, 默认: 0 */
#define configUSE_RECURSIVE_MUTEXES 1 /* 1: 使能递归互斥信号量, 默认: 0 */
#define configUSE_COUNTING_SEMAPHORES 1 /* 1: 使能计数信号量, 默认: 0 */
#define configUSE_ALTERNATIVE_API 0 /* 已弃用!!! */
#define configQUEUE_REGISTRY_SIZE 8 /* 定义可以注册的信号量和消息队列的个数, 默认: 0 */
#define configUSE_QUEUE_SETS 1 /* 1: 使能队列集, 默认: 0 */
#define configUSE_TIME_SLICING 1 /* 1: 使能时间片调度, 默认: 1 */
#define configUSE_NEWLIB_REENTRANT 0 /* 1: 任务创建时分配Newlib的重入结构体, 默认: 0 */
#define configENABLE_BACKWARD_COMPATIBILITY 0 /* 1: 使能兼容老版本, 默认: 1 */
#define configNUM_THREAD_LOCAL_STORAGE_POINTERS 0 /* 定义线程本地存储指针的个数, 默认: 0 */
#define configSTACK_DEPTH_TYPE uint16_t /* 定义任务堆栈深度的数据类型, 默认: uint16_t */
#define configMESSAGE_BUFFER_LENGTH_TYPE size_t /* 定义消息缓冲区中消息长度的数据类型, 默认: size_t */
/* 内存分配相关定义 */
#define configSUPPORT_STATIC_ALLOCATION 0 /* 1: 支持静态申请内存, 默认: 0 */
#define configSUPPORT_DYNAMIC_ALLOCATION 1 /* 1: 支持动态申请内存, 默认: 1 */
#define configTOTAL_HEAP_SIZE ((size_t)(30 * 1024)) /* FreeRTOS堆中可用的RAM总量, 单位: Byte, 无默认需定义 */
#define configAPPLICATION_ALLOCATED_HEAP 0 /* 1: 用户手动分配FreeRTOS内存堆(ucHeap), 默认: 0 */
#define configSTACK_ALLOCATION_FROM_SEPARATE_HEAP 0 /* 1: 用户自行实现任务创建时使用的内存申请与释放函数, 默认: 0 */
/* 钩子函数相关定义 */
#define configUSE_IDLE_HOOK 0 /* 1: 使能空闲任务钩子函数, 无默认需定义 */
#define configUSE_TICK_HOOK 0 /* 1: 使能系统时钟节拍中断钩子函数, 无默认需定义 */
#define configCHECK_FOR_STACK_OVERFLOW 0 /* 1: 使能栈溢出检测方法1, 2: 使能栈溢出检测方法2, 默认: 0 */
#define configUSE_MALLOC_FAILED_HOOK 0 /* 1: 使能动态内存申请失败钩子函数, 默认: 0 */
#define configUSE_DAEMON_TASK_STARTUP_HOOK 0 /* 1: 使能定时器服务任务首次执行前的钩子函数, 默认: 0 */
/* 运行时间和任务状态统计相关定义 */
#define configGENERATE_RUN_TIME_STATS 0 /* 1: 使能任务运行时间统计功能, 默认: 0 */
#if configGENERATE_RUN_TIME_STATS
#include "./BSP/TIMER/btim.h"
#define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() ConfigureTimeForRunTimeStats()
extern uint32_t FreeRTOSRunTimeTicks;
#define portGET_RUN_TIME_COUNTER_VALUE() FreeRTOSRunTimeTicks
#endif
#define configUSE_TRACE_FACILITY 1 /* 1: 使能可视化跟踪调试, 默认: 0 */
#define configUSE_STATS_FORMATTING_FUNCTIONS 1 /* 1: configUSE_TRACE_FACILITY为1时,会编译vTaskList()和vTaskGetRunTimeStats()函数, 默认: 0 */
/* 协程相关定义 */
#define configUSE_CO_ROUTINES 0 /* 1: 启用协程, 默认: 0 */
#define configMAX_CO_ROUTINE_PRIORITIES 2 /* 定义协程的最大优先级, 最大优先级=configMAX_CO_ROUTINE_PRIORITIES-1, 无默认configUSE_CO_ROUTINES为1时需定义 */
/* 软件定时器相关定义 */
#define configUSE_TIMERS 1 /* 1: 使能软件定时器, 默认: 0 */
#define configTIMER_TASK_PRIORITY ( configMAX_PRIORITIES - 1 ) /* 定义软件定时器任务的优先级, 无默认configUSE_TIMERS为1时需定义 */
#define configTIMER_QUEUE_LENGTH 5 /* 定义软件定时器命令队列的长度, 无默认configUSE_TIMERS为1时需定义 */
#define configTIMER_TASK_STACK_DEPTH ( configMINIMAL_STACK_SIZE * 2) /* 定义软件定时器任务的栈空间大小, 无默认configUSE_TIMERS为1时需定义 */
/* 可选函数, 1: 使能 */
#define INCLUDE_vTaskPrioritySet 1 /* 设置任务优先级 */
#define INCLUDE_uxTaskPriorityGet 1 /* 获取任务优先级 */
#define INCLUDE_vTaskDelete 1 /* 删除任务 */
#define INCLUDE_vTaskSuspend 1 /* 挂起任务 */
#define INCLUDE_xResumeFromISR 1 /* 恢复在中断中挂起的任务 */
#define INCLUDE_vTaskDelayUntil 1 /* 任务绝对延时 */
#define INCLUDE_vTaskDelay 1 /* 任务延时 */
#define INCLUDE_xTaskGetSchedulerState 1 /* 获取任务调度器状态 */
#define INCLUDE_xTaskGetCurrentTaskHandle 1 /* 获取当前任务的任务句柄 */
#define INCLUDE_uxTaskGetStackHighWaterMark 1 /* 获取任务堆栈历史剩余最小值 */
#define INCLUDE_xTaskGetIdleTaskHandle 1 /* 获取空闲任务的任务句柄 */
#define INCLUDE_eTaskGetState 1 /* 获取任务状态 */
#define INCLUDE_xEventGroupSetBitFromISR 1 /* 在中断中设置事件标志位 */
#define INCLUDE_xTimerPendFunctionCall 1 /* 将函数的执行挂到定时器服务任务 */
#define INCLUDE_xTaskAbortDelay 1 /* 中断任务延时 */
#define INCLUDE_xTaskGetHandle 1 /* 通过任务名获取任务句柄 */
#define INCLUDE_xTaskResumeFromISR 1 /* 恢复在中断中挂起的任务 */
/* 中断嵌套行为配置 */
#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 /* FreeRTOS可管理的最高中断优先级 */
#define configKERNEL_INTERRUPT_PRIORITY ( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
#define configMAX_SYSCALL_INTERRUPT_PRIORITY ( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
#define configMAX_API_CALL_INTERRUPT_PRIORITY configMAX_SYSCALL_INTERRUPT_PRIORITY
/* FreeRTOS中断服务函数相关定义 */
#define xPortPendSVHandler PendSV_Handler
#define vPortSVCHandler SVC_Handler
#define xPortSysTickHandler SysTick_Handler
/* 断言 */
#define vAssertCalled(char, int) printf("Error: %s, %d\r\n", char, int)
#define configASSERT( x ) if( ( x ) == 0 ) vAssertCalled( __FILE__, __LINE__ )
#endif /* FREERTOS_CONFIG_H */
替换后编译效果,会提示有三个报错,我们需要进入stm32f4xx_it.c文件,将SVC_Handler 、 PendSV_Handler 、 SysTick_Handler中断服务函数注释掉(删除也可以),这是因为RTOS切换过程中,需要用到这三个中断服务函数,在port文件中对三个函数进行了改写,如想了解更多,请查看博主主页

编译后效果图,此时可以正常的编写函数应用

四、应用测试
如果准备好串口1调试的例程,直接复制如下代码即可完成基本的FreeRTOS应用。
#include "main.h"
#include "stm32f4xx.h"
#include "FreeRTOS.h"
#include "task.h"
#include "timers.h"
#include "queue.h"
#include "stream_buffer.h"
#include "semphr.h"
//创建队列句柄
QueueHandle_t Queue_Data_Handle;
#define queue_data_length 10
//宏定义 send_task
BaseType_t retA;
TaskHandle_t Pt_send_Task_TaskHandle;
void queue_sned_Task(void *p);
#define Sned_Task_Name "queue_send"
#define Sned_Task_StackD 128
#define Sned_Task_Priority 1
//宏定义 rece_task
BaseType_t retB;
TaskHandle_t Pt_rece_Task_TaskHandle;
void queue_receive_Task(void *p);
#define Rece_Task_Name "queue_rece"
#define Rece_Task_StackD 128
#define Rece_Task_Priority 1
void queue_send_Task(void *p)
{
uint32_t send_value = 0;
while(1)
{
xQueueSend(Queue_Data_Handle, &send_value, portMAX_DELAY);
{ //如果BUFF满,则不会执行如下程序
send_value++;
if(send_value > 100)
{
send_value = 0;
}
vTaskDelay(100);
}
}
}
void queue_receive_Task(void *p)
{
uint32_t rece_value = 0;
while(1)
{
xQueueReceive(Queue_Data_Handle, &rece_value, portMAX_DELAY);
{
//如果BUFF空,则不会执行如下程序
printf("rece_value = %d\r\n", rece_value);
}
}
}
int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
Debug_Init(115200);
/*创建队列 注意队列长度 和 数据字节大小*/
Queue_Data_Handle = xQueueCreate(queue_data_length, //队列长度
sizeof(uint32_t)); //单个数据大小四字节
/*创建发送任务*/
retA = xTaskCreate( (TaskFunction_t) queue_send_Task,
(const char *)Sned_Task_Name,
(uint16_t)Sned_Task_StackD,
(void *)NULL,
(UBaseType_t)Sned_Task_Priority,
(TaskHandle_t *)&Pt_send_Task_TaskHandle);
/*创建接收任务*/
retA = xTaskCreate( (TaskFunction_t) queue_receive_Task,
(const char *)Rece_Task_Name,
(uint16_t)Rece_Task_StackD,
(void *)NULL,
(UBaseType_t)Rece_Task_Priority,
(TaskHandle_t *)&Pt_rece_Task_TaskHandle);
/*开始调度*/
vTaskStartScheduler();
/*不会执行到这里*/
while (1) {
;
}
}
运行效果图如下

五、总结
如果有兴趣的话可以对照FreeRTOSConfig文件自行配置宏定义,同时了解更多FreeRTOS的应用以及源码请查看博主主页。
更多推荐



所有评论(0)