STM32CubeMX6.16版本 Keil(MDK)5.43(AC6编译器)版本 源码移植FreeRTOS 202406.04 LTS
前文
由于FreeRTOS 的兼容性(CMSIS支持)很好,目前已知的 FreeRTOS 移植的方式有三种:
-
CubeMX SoftwarePacks 直接添加

-
Keil 中直接添加

-
在keil 裸机工程(例如点亮LED)手动添加FreeRTOS源文件,并修改部分文件(本文的操作)
在折腾的过程中发现,由于keil AC6编译器和版本的问题,CubeMX 直接添加和Keil 直接添加都有或多或少的问题,并不是点一下几个选项,keil就能直接编译通过运行,还是需要修改和删减部分文件,那还不如手动添加源码能更加学习到东西,为今后进一步学习FreeRTOS 做好最起码keil 编译能通过,最简单的task 能运行这些最基础的东西。
本文也是截至2026年2月目前各种最新版本软件,能跑通的唯一文档记录了
再继续下文的工作之前,建议按照 CubeMX 生成 keil 设计软件并编译 点亮LED 这个最基本的流程先跑起来
前提准备
STM32CubeMX 安装
建议先跑一个LED 项目试一下
keil 安装

不在赘述
下载Free RTOS 源码
官网下载解压即可

点亮一个LED
CubeMX NEW Project MCU Selector STM32F407ZGT6(你可以选择自己的板卡)

配置老三样:
外部时钟源

时钟树
一般都是死的,或者你看好自己的板卡相关晶振频率,我的就是低速32.768 高速8M

然后主频 输入168 (F103记不得了自己查资料),让软件自己update 一下

这里点错了,要用HSE时钟源,不过HSI HSE 用下来也没什么区别。。。 反正推荐用HSE
完整如下

下载器 和系统定时器sysTick

配置点亮LED的 GPIO
看你板卡,我是PE3 PE4 PG9 ,尽量点两个以上的IO口,方便后续调试

其他不在赘述 都是点GPIO_Output 模式,其他不用动,默认就可以了

配置生成文件
起个名字,找个地址,Structure选择 Basic IDE选择 MDK-ARM

点击左侧的 Code Generator ,检查一下和我一样就可以了,不用动
点击 GENERATE CODE

Open Project

Keil 编译 (第一个坑)


编译不通过,打开魔术棒,选择V6 编译器


顺便配置一下 下载器,选择你自己用的,我用的是DAPlink
在点Settings 确认SW 模式,

点一下FLASH Download 勾选项 Reset and Run 再点一下面的1M Flash (或者你板卡不一样的Flash , 总之要点一下)点击OK 退出

点击OK 退出

再次编译,通过。

点亮LED
可以把左侧的文件夹全部展开,看一下用到了哪些文件

各个文件的作用,大家应该是知道的。。。不了解的可以在看看其他人的贴子或者百度或者问AI
点开main.c 和 stm32f4xx_hal_gpio.c

复制函数到 main函数的 while循环里 HAL_Delay() 函数就是基于systick 实现的函数,也是自带的,键入就可以了,可以识别
/* USER CODE BEGIN WHILE */
while (1)
{
HAL_GPIO_WritePin(GPIOE,GPIO_PIN_3, GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOE,GPIO_PIN_4, GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOG,GPIO_PIN_9, GPIO_PIN_RESET);
HAL_Delay(1000);
HAL_GPIO_WritePin(GPIOE,GPIO_PIN_3, GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOE,GPIO_PIN_4, GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOG,GPIO_PIN_9, GPIO_PIN_SET);
HAL_Delay(1000);
/* USER CODE END WHILE */
要在 绿色的 /* USER CODE BEGIN WHILE */
/* USER CODE END WHILE */
中间添加代码,在下一次更新CubeMX 设置的时候,代码就不会被删除

测试,成功点亮
20260208-235624
至此我们完成了前期的所有准备,开始源码移植FreeRTOS
FreeRTOS 源码导入和移植
解压缩,进入FreeRTOS文件夹

进入路径 FreeRTOS-LTS\FreeRTOS\FreeRTOS-Kernel 内

这就是源码了,全选复制。
到mdk项目文件目录下,新建一个文件夹FreeRTOS,全部复制进去

删减文件(第二个坑)
删除 当前目录下文件夹其余没有用的文件 留下三个文件夹和.C文件(可以先点击一下internet快捷方式 到freertos的文档中心,浏览器收藏一下),如下

进入portable 文件夹,删除除了GCC 和MemMang 之外的所有文件夹,(在之前mdk使用AC5版本编译器时候,用保留的是RVDS 和MemMang 文件夹,也算是一个坑)

只留下:

其中MemMang是 内存管理的.c文件,GCC就是编译支持文件
MemMang文件夹内部,不需要再做删减。
进入GCC 文件夹 删除除了你板卡芯片文件夹之外所有的文件夹,我的是F407 是ARM_M4F的芯片,我就保留了ARM_M4F,同理你可以保留你对应芯片的文件夹


至此源码文件的删减,基本结束。
在MDK中导入源码文件
MDK左边栏新建文件夹,命名为FreeRTOS ,把window MDK项目下FreeRTOS主目录下所有的.c文件
- croutine.c
- event_groups.c
- list.c
- queue.c
- stream_buffer.c
- tasks.c
- timers.c
portable\GCC\ARM_CM4F目录下的 port.c
portable\MemMang 目录下的heap_4.c (内存管理文件1 2 3 4 5,当前只用这个4.c是最常用的一种模式)
总共9个.c文件导入进去,如图

打开魔术棒,到C/C++ 选项卡

点击include Paths 右边的三个点点

添加头文件目录
FreeRTOS\include
..\FreeRTOS\portable\GCC\ARM_CM4F (ARM_CM4F对应上面你选择的芯片文件夹)

点击OK OK 退出
.c .h 导入基本完成
Keil再次编译
提示缺失 64 | #include "FreeRTOSConfig.h"

这个文件可以在mdk文件夹的FreeRTOS 找到
FreeRTOS\examples\template_configuration

FreeRTOSConfig.h 是FreeRTOS的配置文件
在readme中这样描述,FreeRTOSConfig.h 并不是依赖于板卡和芯片的配置文件,而是基于你的项目调整的。所以我们后期的学习,也要学习这文件的修改,我们暂时不用这个官方 FreeRTOSConfig.h
这里我发一个可以直接使用的FreeRTOSConfig.h
/* USER CODE BEGIN Header */
/*
* FreeRTOS Kernel V10.0.1
* Copyright (C) 2017 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!
*/
/* USER CODE END Header */
#ifndef FREERTOS_CONFIG_H
#define FREERTOS_CONFIG_H
/*-----------------------------------------------------------
* Application specific definitions.
*
* These definitions should be adjusted for your particular hardware and
* application requirements.
*
* These parameters and more are described within the 'configuration' section of the
* FreeRTOS API documentation available on the FreeRTOS.org web site.
*
* See http://www.freertos.org/a00110.html
*----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
/* Section where include file can be added */
/* USER CODE END Includes */
/* Ensure definitions are only used by the compiler, and not by the assembler. */
#if defined(__ICCARM__) || defined(__CC_ARM) || defined(__GNUC__)
#include <stdint.h>
extern uint32_t SystemCoreClock;
#endif
#define configUSE_PREEMPTION 1
#define configSUPPORT_STATIC_ALLOCATION 0 //add
#define configSUPPORT_DYNAMIC_ALLOCATION 1
#define configUSE_IDLE_HOOK 0
#define configUSE_TICK_HOOK 0
#define configCPU_CLOCK_HZ ( SystemCoreClock )
#define configTICK_RATE_HZ (100) //10ms
#define configMAX_PRIORITIES ( 56 )
#define configMINIMAL_STACK_SIZE ((uint16_t)512)
#define configTOTAL_HEAP_SIZE ((size_t)(1024*10))
#define configMAX_TASK_NAME_LEN ( 16 )
#define configUSE_TRACE_FACILITY 1
#define configUSE_16_BIT_TICKS 0
#define configUSE_MUTEXES 1
#define configQUEUE_REGISTRY_SIZE 8
#define configUSE_RECURSIVE_MUTEXES 1
#define configUSE_COUNTING_SEMAPHORES 1
#define configUSE_PORT_OPTIMISED_TASK_SELECTION 0
/* Co-routine definitions. */
#define configUSE_CO_ROUTINES 0
#define configMAX_CO_ROUTINE_PRIORITIES ( 2 )
/* Software timer definitions. */
#define configUSE_TIMERS 1
#define configTIMER_TASK_PRIORITY ( 2 )
#define configTIMER_QUEUE_LENGTH 10
#define configTIMER_TASK_STACK_DEPTH 256
/* Set the following definitions to 1 to include the API function, or zero
to exclude the API function. */
#define INCLUDE_vTaskPrioritySet 1
#define INCLUDE_uxTaskPriorityGet 1
#define INCLUDE_vTaskDelete 1
#define INCLUDE_vTaskCleanUpResources 0
#define INCLUDE_vTaskSuspend 1
#define INCLUDE_vTaskDelayUntil 1
#define INCLUDE_vTaskDelay 1
#define INCLUDE_xTaskGetSchedulerState 1
#define INCLUDE_xTimerPendFunctionCall 1
#define INCLUDE_xQueueGetMutexHolder 1
#define INCLUDE_uxTaskGetStackHighWaterMark 1
#define INCLUDE_eTaskGetState 1
/*
* The CMSIS-RTOS V2 FreeRTOS wrapper is dependent on the heap implementation used
* by the application thus the correct define need to be enabled below
*/
#define USE_FreeRTOS_HEAP_4
/* Cortex-M specific definitions. */
#ifdef __NVIC_PRIO_BITS
/* __BVIC_PRIO_BITS will be specified when CMSIS is being used. */
#define configPRIO_BITS __NVIC_PRIO_BITS
#else
#define configPRIO_BITS 4
#endif
/* The lowest interrupt priority that can be used in a call to a "set priority"
function. */
#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 15
/* The highest interrupt priority that can be used by any interrupt service
routine that makes calls to interrupt safe FreeRTOS API functions. DO NOT CALL
INTERRUPT SAFE FREERTOS API FUNCTIONS FROM ANY INTERRUPT THAT HAS A HIGHER
PRIORITY THAN THIS! (higher priorities are lower numeric values. */
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 5
/* Interrupt priorities used by the kernel port layer itself. These are generic
to all Cortex-M ports, and do not rely on any particular library functions. */
#define configKERNEL_INTERRUPT_PRIORITY ( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
/* !!!! configMAX_SYSCALL_INTERRUPT_PRIORITY must not be set to zero !!!!
See http://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html. */
#define configMAX_SYSCALL_INTERRUPT_PRIORITY ( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
/* Normal assert() semantics without relying on the provision of an assert.h
header file. */
/* USER CODE BEGIN 1 */
#define configASSERT( x ) if ((x) == 0) {taskDISABLE_INTERRUPTS(); for( ;; );}
/* USER CODE END 1 */
/* Definitions that map the FreeRTOS port interrupt handlers to their CMSIS
standard names. */
//
/* IMPORTANT: This define is commented when used with STM32Cube firmware, when the timebase source is SysTick,
to prevent overwriting SysTick_Handler defined within STM32Cube HAL */
//
/* USER CODE BEGIN Defines */
/* Section where parameter definitions can be added (for instance, to override default ones in FreeRTOS.h) */
/* USER CODE END Defines */
#endif /* FREERTOS_CONFIG_H */
可以复制下来,保存,重命名为FreeRTOSConfig.h放到 MDK项目的Inc文件夹下
在MDK中手动添加把它放在左边栏Application/User下,方便我们以后修改,这个.h不需要在魔术棒C/C++选项卡下添加文件夹,因为本来Inc文件夹就已经导入过了

编译,没有错误(虽然此时没有错误,但是不代表移植成功)

修改三个中断函数 (第三个坑)
在此基础上,首先在FreeRTOSConfig.h 的末尾之前添加三个宏定义
#define xPortPendSVHandler PendSV_Handler //
#define vPortSVCHandler SVC_Handler //
#define xPortSysTickHandler SysTick_Handler //

MDK左边栏Application/User 点击进入stm32f4xx_it.c(不同芯片对应进入不同的it.c 中断服务函数)
把对应的
- PendSV_Handle
- SVC_Handler
- SysTick_Handler
函数 // 掉 ,注释掉




FreeRTOS 就是借由这几个中断为切入点实现功能
main 函数引用添加(第四个坑)
由于新的AC6编译器,对代码检查愈发严格,如果不写include,会报错,提示必须引用.h
所以在main.c的include 后面添加引用
#include "FreeRTOSConfig.h"
#include "FreeRTOS.h"
#include "task.h"
注意在绿色的CODE BEGIN Includes
CODE END Includes
之间插入,防止CubeMX代码更新时,把代码删除了

源码移植完毕,进行测试
FreeRTOS 创建线程 使用不同的LED 测试
创建任务函数
注意在main函数之前声明,然后在main函数后面写完整函数 还是注意在绿色中间
void led1 (void *param);
void led2 (void *param);
void led3 (void *param);

void led1 (void *param)
{
while(1)
{
HAL_GPIO_WritePin(GPIOE,GPIO_PIN_4,GPIO_PIN_RESET);
vTaskDelay(100);
HAL_GPIO_WritePin(GPIOE,GPIO_PIN_4,GPIO_PIN_SET);
vTaskDelay(100);
}
}
void led2 (void *param)
{
while(1)
{
HAL_GPIO_WritePin(GPIOE,GPIO_PIN_3,GPIO_PIN_RESET);
// HAL_Delay(1000);
vTaskDelay(100);
HAL_GPIO_WritePin(GPIOE,GPIO_PIN_3,GPIO_PIN_SET);
vTaskDelay(100);
}
}
void led3 (void *param)
{
while(1)
{
HAL_GPIO_WritePin(GPIOG,GPIO_PIN_9,GPIO_PIN_RESET);
vTaskDelay(100);
HAL_GPIO_WritePin(GPIOG,GPIO_PIN_9,GPIO_PIN_SET);
vTaskDelay(100);
}
}

在main中 创建线程任务 开始调度
xTaskCreate(led1,"Task1",150,NULL,1,NULL);
xTaskCreate(led2,"Task2",150,NULL,1,NULL);
xTaskCreate(led3,"Task3",150,NULL,1,NULL);
vTaskStartScheduler();
编译下载,测试成功
20260209-线程LED测试
再此基础上再修改一下
也是为后面我们修改项目做测试 ,在CubeMX中添加一个UART,修改一下systick的时钟源,由于systick被FreeRTOS占用,如同我注释掉的HAL_Dealy()不能用了
修改systick 的时钟源
我们在CubeMX中 将Timebase Source 源改为定时器TIM6
添加一个UART1

Mode 修改为Asynchronous,其他不动

修改NVIC(第五个坑)
以防万一,我们在NVIC中提高一下System tick(FreeRTOS 占用的定时器) 的优先级,也为我们以后学习修改中断优先级做准备,改成14

并且在Code generation 中 关闭我们之前注释掉的中断服务函数,不然就会重新生成
如图所示 把
- System service call via SWI instruction
- Pendable request for system service
- System tick timer
的右边Generate IRQ ... 取消括号选择 (Call HAL 能取消也取消掉,点不动就算了)

如图修改
GENERATE CODE 重新生成项目MDK
MDK 修改 编译 运行
添加UART的任务函数 和 创建线程 就是在之前的那些位置上添加,展示代码,但不再截图演示了
void myuart (void *param);
...
xTaskCreate(myuart,"Task4",150,NULL,1,NULL);
...
void myuart (void *param)
{
while(1)
{
HAL_UART_Transmit(&huart1,"hello FreeRTOS this 4 task\n",sizeof("hello FreeRTOS this 4 task\n"), 0xfffffff);
vTaskDelay(100);
}
}
编译会报一个警告,那个字符串写的不规范,无所谓,后面慢慢优化就行。


编译下载
LED 必定是正常的 不再演示
看一下串口
20260209-串口线程测试
大家可以试一试HAL_Delay(); 能否正常使用 (🙂)
后记
这也是我学习FreeRTOS的一个记录,这一个星期踩了很多坑,把这些都一一描述了,也就过去了
好了,大家可以在此基础上 进一步学习 STM32 FreeRTOS 的内核 内存管理 和更多复杂的功能的
openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。
更多推荐





所有评论(0)