当前位置: 首页 > news >正文

实时操作系统FreeRTOS移植到STM32VGT6

一、前言

下载平台:STM32F407VGT6
代码使用平台:VSCode
编译器:arm-none-aebi-gcc
程序下载工具:STlink
批处理工具:make
移植的FreeRTOS版本:V11.2.0

其实此方法并不局限在arm-none-aebi-gcc中,此方法对于Keil5也是可以使用的,
只不过复制的一些文件不同而已

欢迎来我的仓库下载我的代码,FreeRTOS项目在freertos分支内:
https://gitee.com/timing_matlab/stm32-f407-vgt6-project.git
这里分享一下在移植过程中需要注意的点!

二、移植FreeRTOS的文件

1、不管三七二十一,先拿源码

来到FreeRTOS的github代码仓库,下载源码或者直接克隆仓库

https://github.com/FreeRTOS/FreeRTOS-Kernel/archive/refs/tags/V11.2.0.tar.gz

或者
克隆的话一定要加–recurse-submodules,不然得到的源码不完整!

git clone https://github.com/FreeRTOS/FreeRTOS.git --recurse-submodules

![[Pasted image 20250823233717.png]]

2、创建一个文件夹保存移植过来的源码

创建一个文件夹ThirdParty,表示这是第三方库,然后再里面创建FreeRTOS文件夹,然后创建inc与src,保存头文件与源文件。
![[Pasted image 20250823233915.png]]

然后我们来到FreeRTOS的源文件开始移植!

第一步来到源码的./FreeRTOS/FreeRTOS/Source

复制当前目录下的全部的c语言源文件,到我们创建的目录下的src里面了
![[Pasted image 20250823234422.png]]

复制完成之后的src文件夹下
![[Pasted image 20250823234620.png]]

第二步来到源码的./FreeRTOS/FreeRTOS/Source/include

复制当前目录下全部的头文件,到我们创建目录的inc里面
![[Pasted image 20250823235452.png]]

复制完成之后的inc文件夹下
![[Pasted image 20250823235537.png]]

第三步来到源码的./FreeRTOS/FreeRTOS/Source/portable

![[Pasted image 20250824000508.png]]

此处需要移植两处地方的源文件与头文件,这里与前两步不太一样。前两步无论在什么平台都是通用的,但是这一步不同平台有不同平台的移植方式。

1、移植平台文件夹–gcc或者RVDS

这里我使用的是STMF40732VGT6,这个芯片使用的是M4架构的,所以要选择CM4结尾或者特征的目录文件,并且如果不使用浮点运算直接使用CM3也是可以的。
我这里因为使用的是arm-none-aebi-gcc,所以我选择gcc。如果是keil,可以去RVDS找芯片对应的文件!
这里很有趣的点是Keil MDK移植居然不是在keil,而是在RVDS中,这里简单提一嘴:

[!提一嘴] 提一嘴
Keil-MDK 的 编译器 就是 ARM 当年 RVDS 套装里的 RVCT(RealView Compiler Toolchain)
只是 ARM 在 2005 年收购 Keil 后,把:
RVCT 编译器 uVision IDE(原来 Keil 的)
重新打包成 “Keil-MDK” 并沿用 “RealView” 品牌。
因此 MDK 的 portable/RVDS/… 目录仍叫 RVDS,实质就是 Keil-MDK 用的 ARMCC/ARMCLANG 编译器

我这里演示stm32F407VGT6移植gcc里面文件的过程:
来到gcc目录下,找到与我芯片符合的ARM-CM4F符合的文件夹
![[Pasted image 20250824002011.png]]

将里面的c源文件与头文件分别复制到src与inc里面
![[Pasted image 20250824002109.png]]

然后就没了。keil平台的话,就是来到RVDS文件夹下,找到对应的芯片型号的文件夹
![[Pasted image 20250824002246.png]]

复制对应的c源文件与头文件到src与inc里面即可
![[Pasted image 20250824002330.png]]

对了还有一个比较重要的头文件,这个也是我们后面交互比较多的文件,用于开启freertos的某些钩子(hook)函数,这个头文件也需要根据各自的芯片架构来选择!

2、来到./FreeRTOS/FreeRTOS/Demo选择对应芯片的FreeRTOSConfig.h

![[Pasted image 20250824092439.png]]

![[Pasted image 20250824092601.png]]

没了,就那么简单!
完成之后,文件夹的内容:
![[Pasted image 20250824092636.png]]

![[Pasted image 20250824091422.png]]

3、MemMang文件夹

这里的MemMang是和内存管理有关的文件,这个直接复制对应的头文件与源文件到我们创建文件src即可
![[Pasted image 20250824000712.png]]

通常只需要复制heap_4.c即可,这里提一嘴它们之间的关系,并且它们只能选择一个!

[!补充] 补充
一句话总结
heap_1 ~ heap_5 是 同一个接口(pvPortMalloc / vPortFree)下的五种实现,差异只在 “能否 free、能否合并碎片、能否跨非连续内存” —— 选哪个文件,就决定了 FreeRTOS 堆的形态。

文件能否 free合并相邻碎片支持多块不连续内存典型场景
heap_1.c只分配、不释放的简单应用
heap_2.c早期版本,碎片严重,已不推荐
heap_3.c依赖 C 库直接包装 malloc/free,加线程安全
heap_4.c官方默认,碎片最少,单块 RAM
heap_5.cheap_4 功能 + 可跨多块 RAM/SDRAM

使用规则

  1. 同一工程 只选 1 个 heap_x.c 放进 src中。
  2. heap_5 必须先调用
    vPortDefineHeapRegions(xHeapRegions);   // 传入各段首地址+长度
    
    才能 pvPortMalloc,否则第一次创建任务/队列就挂。
  3. 推荐顺序
    普通 MCU → heap_4
    需要外扩 RAM → heap_5
    极简、永不释放 → heap_1

目前全部文件都移植完成!

三、修改或者增添部分文件的函数

1、修改部分文件的函数

修改FreeRTOSConfig.h部分内容:
这里提供一部分有些重要,有些不重要的内容


🔴 必须(动就炸)

说明
configCPU_CLOCK_HZ必须与 MCU 主频一致(SystemCoreClock)
configTICK_RATE_HZ系统心跳,1 kHz 通用,> 10 kHz 会吃 CPU
configPRIO_BITS与芯片手册匹配(F1/F4 为 4)
configKERNEL_INTERRUPT_PRIORITY & configMAX_SYSCALL_INTERRUPT_PRIORITY中断优先级位偏移,错一位就 HardFault
vPortSVCHandler / xPortPendSVHandler / xPortSysTickHandler向量映射,名字必须对齐
configASSERT调试开关,关闭就失去定位能力

🟡 重要(根据应用调)

推荐值提示
configTOTAL_HEAP_SIZE75 kB调大/调小,看剩余 RAM
configMINIMAL_STACK_SIZE130 为单位,F4 建议 128-256
configMAX_PRIORITIES5-8够用即可,越大 RAM 越多
configCHECK_FOR_STACK_OVERFLOW2开发期开,量产关
configUSE_MALLOC_FAILED_HOOK1开发期开,方便抓内存泄漏
configUSE_TIMERS1用软件定时器就开
configUSE_MUTEXES / configUSE_RECURSIVE_MUTEXES1多任务共享资源必开

🟢 可选(可关可开)

典型场景
configUSE_IDLE_HOOK / configUSE_TICK_HOOK低功耗/统计
configUSE_TRACE_FACILITY开 TRACE 时才用
configGENERATE_RUN_TIME_STATS性能分析
configUSE_CO_ROUTINES几乎没人用

红色不能改,黄色按需改,绿色随意改。

所以我们可以关闭,configUSE_IDLE_HOOK / configUSE_TICK_HOOK功能、configUSE_TRACE_FACILITY、configGENERATE_RUN_TIME_STATS、configUSE_CO_ROUTINES
不过最后在FreeRTOSConfig.h文件中加入头文件
![[Pasted image 20250824095303.png]]

![[Pasted image 20250824093718.png]]

2、stm32f4xx_it.c文件

我们可以在FreeRTOSConfig.h查看
![[Pasted image 20250824094014.png]]
所以我们需要去stm32f4xx_it.c中注释掉对应的函数,因为这部分的函数实现在port.c中已经实现了。
SVC_Handler
PendSV_Handler
SysTick_Handler
![[Pasted image 20250824094352.png]]

3、delay函数

增加一个比较通用的delay文件。
在调度器启动前,使用堵塞式的delay函数;
在调度器启动后,使用FreeRTOS的delay函数。
可以确保一些初始化函数可以正确运行。
初始化调用:

	delay_init(168); //168是STM32VGT6支持的时钟频率,这里请修改相应的时钟频率

函数使用:

	delay_ms(nms);      /* 延时nms */delay_us(nus);      /* 延时nus */

delay.h

#ifndef __DELAY_H
#define __DELAY_H
#include "stm32f4xx.h" //若用 F1/F7/H7 改成对应头文件
#include <misc.h>
void delay_init(uint16_t sysclk); /* 初始化延迟函数 */
void delay_ms(uint16_t nms);      /* 延时nms */
void delay_us(uint32_t nus);      /* 延时nus */
#endif

delay.c

#include "delay.h"
#include "stm32f4xx.h" // 若用 F1/F7/H7 改成对应头文件
#include "FreeRTOS.h"
#include "task.h"
static uint32_t fac_us = 0;
/* 初始化时只算系数,不碰 SysTick 寄存器 */
void delay_init(uint16_t sysclk)
{SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);fac_us = sysclk / 8; // 时钟 = HCLK/8
}
/*------------------------------------------* 微秒延时:调度器启动前 → 纯空循环*            调度器启动后 → SysTick 计数*------------------------------------------*/
void delay_us(uint32_t nus)
{if (xTaskGetSchedulerState() == taskSCHEDULER_RUNNING){/* FreeRTOS 已运行:用 SysTick 忙等 */volatile uint32_t ticks, told, tnow, tcnt = 0, reload;UBaseType_t original_priority = uxTaskPriorityGet(NULL);vTaskPrioritySet(NULL, configMAX_PRIORITIES - 1);reload = SysTick->LOAD;ticks = nus * fac_us;told = SysTick->VAL;while (1){tnow = SysTick->VAL;if (tnow != told){if (tnow < told)tcnt += told - tnow;elsetcnt += reload - tnow + told;told = tnow;if (tcnt >= ticks)break;}}vTaskPrioritySet(NULL, original_priority);}else{/* 调度器未启动:纯空循环,不依赖 SysTick */uint32_t ticks = nus * fac_us;while (ticks--)__NOP();}
}
/*------------------------------------------* 毫秒延时:调度器启动前 → 空循环*            调度器启动后 → vTaskDelay*------------------------------------------*/
void delay_ms(uint16_t nms)
{if (xTaskGetSchedulerState() == taskSCHEDULER_RUNNING){vTaskDelay(pdMS_TO_TICKS(nms));}else{/* 调度器未启动:空循环 1 ms × nms */for (uint32_t i = 0; i < nms; i++)delay_us(1000);}
}

4、定义两个个钩子函数

此钩子函数可以作为一个调试接口。这里可以点灯 / 打印 / 断点,方便调试
vApplicationStackOverflowHook
vApplicationMallocFailedHook

void vApplicationStackOverflowHook(TaskHandle_t xTask, char *pcTaskName)
{/* 这里可以点灯 / 打印 / 断点,方便调试 */(void)xTask;(void)pcTaskName;/* 死循环,方便调试器停住 */taskDISABLE_INTERRUPTS();for (;;);
}void vApplicationMallocFailedHook(void)
{/* 可以点灯、打印或断点 */taskDISABLE_INTERRUPTS();for (;;);
}

随便在一个包含
#include “FreeRTOS.h”
#include “task.h”
文件下定义即可。

随后只要在Makefile或者Keil里面添加对应的头文件与源文件路径即可
![[Pasted image 20250824095659.png]]

这里就不提供keil的添加方式了,这个流程在创建工程的时候就应该知道了。

四、后记

我们简单使用一下,这个迁移过来的FreeRTOS工程。

1、包含头文件

#include "FreeRTOS.h"
#include "task.h"

2、初始化延时函数(此步可选)

delay_init(168);

3、创建任务

500ms – LED翻转
1s – LCD的显示变化

//任务实现
void LED_Task(void *arg)
{for(;;){GPIO_ToggleBits(GPIOA, GPIO_Pin_0);vTaskDelay(500);}
}void LCD_Task(void *arg)
{ST_7735S_Clear(0xffff);int num = 0;char buff[20] = {0};for (;;){bzero(buff,sizeof(buff));sprintf(buff,"%d",num++);ST_7735S_ShowString(10, 20, "Hello!World!", RGB888to565(0xff, 0x00, 0xff), 0xffff);ST_7735S_ShowString(10, 40, buff, RGB888to565(0xff, 0x00, 0xff), 0xffff);vTaskDelay(500);}
}//任务创建
xTaskCreate(LED_Task,"LED",128,NULL,1,NULL);
xTaskCreate(LCD_Task, "LCD", 128, NULL, 2, NULL);

值得一提的是任务创建不局限xTaskCreate函数,这是动态创建的任务,还有创建静态任务xTaskCreateStatic的方式。这里我们分析分析这个动态创建任务的函数:

    BaseType_t xTaskCreate( TaskFunction_t pxTaskCode,const char * const pcName,const configSTACK_DEPTH_TYPE uxStackDepth,void * const pvParameters,UBaseType_t uxPriority,TaskHandle_t * const pxCreatedTask ) 
参数名类型含义
pxTaskCodeTaskFunction_t任务入口函数指针(必须是一个 void func(void *pv) 形式的函数)
pcNameconst char *任务名字(调试/Trace 用,长度受 configMAX_TASK_NAME_LEN 限制)
uxStackDepthconfigSTACK_DEPTH_TYPE任务栈大小,单位是 “字”(不是字节)。
例如 128 → 512 B(32 位 MCU)
pvParametersvoid *传给任务的参数,可为 NULL
uxPriorityUBaseType_t任务优先级数值越大越优先;0 为空闲任务
pxCreatedTaskTaskHandle_t *返回的任务句柄,之后可用它 vTaskDelete/ vTaskPrioritySet 等;
不需要句柄就填 NULL

返回值

  • pdPASS:创建成功
  • errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY:内存不足

4、开始任务

vTaskStartScheduler();

5、任务现象

正确的实现!
![[IMG_20250824_102021.jpg]]

![[IMG_20250824_102026.jpg]]

这里总结一下移植的流程
1.获取FreeRTOS源码
2.移植文件
3.修改部分FreeRTOSConfig.h文件
4.修改stm32f407xx_it.c注释掉SVC_Handler、PendSV_Handler、SysTick_Handler函数实现
5.增加一个比较通用的delay函数
6.实现调试的钩子函数
vApplicationStackOverflowHook、vApplicationMallocFailedHook
7.创建任务与开启任务

以上为本文的内容。享受FreeRTOS吧!

http://www.dtcms.com/a/348246.html

相关文章:

  • Axure RP 9的安装
  • 2025年渗透测试面试题总结-31(题目+回答)
  • leetcode 1504. 统计全 1 子矩形 中等
  • `malloc` 内存分配函数
  • fastdds:topic instance
  • 【嵌入式】【搜集】状态机、状态迁移图及状态模式材料
  • 【线性代数】常见矩阵类型
  • 【Nginx系列】查看 Nginx 的日志
  • Building Systems with the ChatGPT API 使用 ChatGPT API 搭建系统(第八章学习笔记及总结)
  • Hibernate详解
  • GaussDB 数据库架构师修炼(十八) SQL引擎-分布式计划
  • 保姆级Maven安装与配置教程(Windows版)
  • SpringCloud Alibaba核心知识点
  • MIT 6.5840 (Spring, 2024) 通关指南——入门篇
  • 项目学习总结(4)
  • Java内存泄漏详解:检测、分析与预防策略
  • 大语言模型的自动驾驶 LMDrive/DriveVLM-Dual
  • 电动车运行原理与最新人工智能驾驶技术在电动车上的应用展望:从基础动力系统到L5级完全自动驾驶的技术深度解析
  • EndNote 2025 Mac 文献管理工具
  • Multitouch for mac 触控板手势增强软件
  • Multi-output Classification and Multi-label Classification|多输出分类和多标签分类
  • 跨语言文化的统一语义真理:存在性、形式化及其对自然语言处理(NLP)深层语义分析的影响
  • 什么是大模型的指令跟随
  • Preprocessing Model in MPC 3 - 基于同态加密的协议 - Over Fields 有限域
  • Python 列表:定义、操作、推导式与嵌套
  • 西门子 SCL 简单案例
  • 计算机视觉学习路线:从入门到进阶的完整指南
  • 最近遇到的几个JVM问题
  • java学习 + 一个向前端传流顺序不一致的一个解决思路
  • c++ 常用接口设计