1. FreeRTOS 源码/移植/系统配置

发布时间:2026/6/2 1:08:27

1. FreeRTOS 源码/移植/系统配置 FreeRTOS 简介1. RTOS简介1. FreeRTOS 源码介绍2. FreeRTOS 移植2.1 FreeRTOS 内存管理简介2.2 heap_1 内存管理算法2.3 heap_2 内存管理算法2.4 heap_3 内存管理算法2.5 heap_4 内存管理算法2.6 heap_5 内存管理算法3. FreeRTOS 系统配置1. RTOS简介特性FreeRTOSUCOSLiteOS实时性软实时硬实时软实时资源占用极低~10KB中高~20KB低~2KB典型应用嵌入式/IoT汽车/医疗/工业华为IoT设备内核原理的共性:多任务调度均支持多任务并发基于优先级调度抢占式或协作式。任务管理通过任务控制块TCB管理任务状态运行、就绪、阻塞等。同步与通信提供信号量、互斥锁、队列等机制实现任务间通信。中断处理支持中断嵌套中断服务程序ISR与任务分离。内核原理的差异:特性FreeRTOSUCOSLiteOS调度策略抢占式默认 协作式[需任务主动释放 CPU ]可选严格优先级抢占式调度无时间片轮转µC/OS-IIµC/OS-III 引入时间片轮转但需手动配置抢占式 时间片轮转(部分版本支持)优先级数量无固定限制可配置固定优先级如 256 级精简优先级通常 32 级调度器实现单调度器支持协程已弃用双调度器µC/OS-III 支持时间片轻量化调度器优化低功耗场景任务栈管理静态分配或动态分配静态分配为主静态分配轻量化设计上下文切换速度中等依赖架构优化,用 C 语言实现可移植性强但效率依赖编译器优化高汇编级优化,关键代码如任务切换、中断处理用汇编语言优化确保高实时性。高针对 ARM Cortex-M 优化动态内存分配支持多种堆分配算法heap_1~5,灵活性高但碎片化风险较大。提供固定大小内存分区管理通过内存分区管理动态内存减少碎片化适合长期运行系统。精简动态内存管理可选组件内存保护无依赖硬件 MPU 扩展无µC/OS-IIµC/OS-III 支持 MPU无面向低端 MCU内存占用内核约 6-10KB内核约 10-20KB内核约 2-5KB中断延迟可配置通常 1µs实时性依赖配置。 0.5µs确定性响应 1µs优化中断开关中断嵌套支持支持深度可配置有限支持依赖架构实时性保障软实时优先级反转风险硬实时支持优先级继承协议严格的中断优先级管理支持优先级继承协议PIP避免优先级反转。软实时低功耗优先红色任务调度机制、绿色任务管理与上下文切换、蓝色内存管理、橘色中断处理与实时性1. FreeRTOS 源码介绍 FreeRTOS官网 FreeRTOS 提供两个版本FreeRTOS、FreeRTOS LTS官方长期的支持和维护版本 获取 FreeRTOS 源码地址 2. FreeRTOS 移植2.1 FreeRTOS 内存管理简介静态方法创建任务、队列、信号量等对象的 API 函数一般是以“Static”结尾的例如静态创建任务的 API 函数xTaskCreateStatic()。使用静态方式创建各种对象时需要用户提供各种内存空间例如任务的栈空间、任务控制块所用内存空间等等并且使用静态方式占用的内存空间一般固定下来了即使任务、队列等被删除后这些被占用的内存空间也没有其他用途。在使用动态方式管理内存的时候FreeRTOS 就能够在创建任务、队列、信号量等对象的时候自动地从 FreeRTOS 管理的内存堆中申请所创建对象所需的内存在对象被删除后又可以将这块内存释放会 FreeRTOS 管理的内存堆这样看来动态方式管理内存相比与静态方式显得灵活许多。除了 FreeRTOS 提供的动态内存管理方法标准的 C 库也提供了函数 malloc()和函数 free() 来实现动态地申请和释放内存但是标准 C 库的动态内存管理方法有如下几个缺点并不适用于所有嵌入式系统。占用大量的代码空间。没有线程安全的相关机制。具有不确定性体现在每次执行的时间不同。……FreeRTOS 一共提供了 5 种动态内存管理算法这 5 种动态内存管理算法本别对应了 5 个C 源文件分别为heap_1.c、heap_2.c、heap_3.c、heap_4.c、heap_5.c文件原理特点heap_1.c仅支持单向分配无释放功能。内存一经分配即永久占用适用于确定性要求高且无需动态删除对象的系统如安全关键型嵌入式系统实现简单、无碎片风险但灵活性极低heap_2.c允许申请和释放内存但不能合并相邻的空闲内存块。适用于重复创建和删除相同大小任务的场景长期运行后碎片化严重已逐渐被heap_4.c取代heap_3.c封装标准库的malloc和free通过挂起调度器vTaskSuspendAll()保证线程安全。堆大小由链接器决定不受configTOTAL_HEAP_SIZE限制分配速度较慢且不确定性高适用于需要兼容现有代码的场景heap_4.c允许申请和释放内存并且能够合并相邻的空闲内存块减少内存碎片的产生。支持动态分配不同大小的内存块是通用型应用的首选方案分配效率高于标准库且能通过xPortGetMinimumEverFreeHeapSize()监控内存使用情况heap_5.c能够管理多个非连续内存区域的 heap_4如同时使用内部RAM和外部SRAM。需通过vPortDefineHeapRegions()显式初始化多个内存区域复杂硬件环境下的内存扩展需求2.2 heap_1 内存管理算法/* 此宏用于定义 FreeRTOS 内存堆的定义方式 */#if(configAPPLICATION_ALLOCATED_HEAP1)externuint8_tucHeap[configTOTAL_HEAP_SIZE];/* 用户自定义一个大数组作为 FreeRTOS 管理的内存堆 */#elsestaticuint8_tucHeap[configTOTAL_HEAP_SIZE];/* 定义一个大数组作为 FreeRTOS 管理的内存堆 */#endifvoid*pvPortMalloc(size_txWantedSize){void*pvReturnNULL;staticuint8_t*pucAlignedHeapNULL;/* 确保申请的内存大小按照 portBYTE_ALIGNMENT 字节对齐 * 如果审定的内存大小没有按照 portBYTE_ALIGNMENT 字节对齐 * 则会加大申请的内存大小是指按 portBYTE_ALIGNMENT 字节对齐 */#if(portBYTE_ALIGNMENT!1){if(xWantedSizeportBYTE_ALIGNMENT_MASK){if((xWantedSize(portBYTE_ALIGNMENT-(xWantedSizeportBYTE_ALIGNMENT_MASK)))xWantedSize){xWantedSize(portBYTE_ALIGNMENT-(xWantedSizeportBYTE_ALIGNMENT_MASK));}else{xWantedSize0;}}}#endif/* 挂起任务调度器 */vTaskSuspendAll();{if(pucAlignedHeapNULL){/* 确保内存堆的起始地址按照 portBYTE_ALIGNMENT 字节对齐 */pucAlignedHeap(uint8_t*)(((portPOINTER_SIZE_TYPE)ucHeap[portBYTE_ALIGNMENT-1])(~((portPOINTER_SIZE_TYPE)portBYTE_ALIGNMENT_MASK)));}/* 申请的内存大小需大于 0 * 检查内存堆中是否有足够的空间 */if((xWantedSize0)((xNextFreeBytexWantedSize)configADJUSTED_HEAP_SIZE)((xNextFreeBytexWantedSize)xNextFreeByte)){/* 计算申请到内存的起始地址 * 内存堆的对齐地址内存堆已分配的大小 */pvReturnpucAlignedHeapxNextFreeByte;/* 更新内存堆已分配的大小 */xNextFreeBytexWantedSize;}/* 用于调式不用理会 */traceMALLOC(pvReturn,xWantedSize);}(void)xTaskResumeAll();/* 此宏用于开启动态内存申请失败钩子函数 */#if(configUSE_MALLOC_FAILED_HOOK1){/* 动态内存申请失败 */if(pvReturnNULL){externvoidvApplicationMallocFailedHook(void);/* 调用动态内存申请失败钩子函数 */vApplicationMallocFailedHook();}}#endif/* 返回申请到内存的首地址 */returnpvReturn;}voidvPortFree(void*pv){/* 没有实现释放内存的功能 */(void)pv;configASSERT(pvNULL);}heap_1 内存管理算法管理的内存堆是一个数组在申请内存的时候heap_1 内存管理算法只是简单地从数组中分出合适大小的内存。heap_1 内存管理算法的内存释放函数并没有实现因此使用heap_1 内存管理算法申请的内存是无法释放的。heap_1 内存管理算法的申请内存函数的实现非常简单就是从内存堆的低地址开始往高地址分配内存内存堆利用率是非常高的除了内存堆起始地址的位置可能会因地址对齐产生一小块无用内存外内存堆中其余的内存空间都可以用来分配并且也不会产生内存碎片。内存堆的结构示意图如下heap_1 内存管理算法具有如下特性适用于一旦创建好任务、队列、信号量等就不会删除的应用实际上大多数的 FreeRTOS 应用都是这样的。具有确定性体现在每次执行的时间都是一样的而且不会产生内存碎片。实现的方式非常简单分配的内存都是从一个静态分配的数组中分配的因此也意味着并不适用于那些真正需要动态申请和释放内存的应用。2.3 heap_2 内存管理算法相比于 heap_1 内存管理算法heap_2 内存管理算法使用了最适应算法以支持释放先前申请的内存但是 heap_2 内存管理算法并不能将相邻的空闲内存块合并成一个大的空闲内存块因此 heap_2 内存管理算法不可避免地会产生内存碎片。内存碎片是由于多次申请和释放内存但释放的内存无法与相邻的空闲内存合并而产生的具体的产生过程如下图所示heap_2 内存管理算法的内存堆与 heap_1 内存管理算法的内存堆一样都是一个数组。可以在 FreeRTOSConfig.h 文件中配置 configTOTAL_HEAP_SIZE 配置项以配置内存堆的字节大小同样地也可以用过 configAPPLICATION_ALLOCATED_HEAP 配置项将内存堆定义在指定的内存地址中。用户可以通过函数 xPortGetFreeHeapSize()获取内存堆中未分配的内存总量并根据系统运行时内存堆中剩余内存的大小针对性地对 configTOTAL_HEAP_SIZE 配置项进行优化配置。heap_2 内存管理算法申请内存的过程大致如下因为空闲块链表中的空闲内存块是按照内存块的大小从小到大排序的因此从头开始遍历空闲块链表找到第一个大小适合的空闲内存块。找到大小适合的空闲内存块后由于找到的空闲内存块可能比需要申请的内存大因此需要将整个内存块分为两个小的内存块其中一个内存块的大小就是需要申请内存的大小另一个小内存块作为空闲内存块重新插入空闲块链表。3.heap_2 内存管理算法的释放函数很简单就是将带释放的内存块插入到空闲块链表中。2.4 heap_3 内存管理算法heap_3 内存管理算法本质使用的是调用标准 C 库提供的内存管理函数标准 C 库的内存管理需要链接器设置好一个堆这个堆将作为内存管理的内存堆使用在启动文件中可以配置这个堆的大小如下所示heap_3 内存管理算法具有如下特性需要链接器提供一个堆还需要编译器的库提供用于申请内存的函数 malloc()和用于释放内存的函数 free()。具有不确定性。有可能会大大地增减编译后的代码量。2.5 heap_4 内存管理算法heap_4 内存管理算法使用了首次适应算法与 heap_2 内存管理算法一样heap_4 内存管理算法也支持内存的申请与释放并且 heap_4 内存管理算法还能够将空闲且相邻的内存进行合并从而减少内存碎片的现象。heap_4 内存管理算法的内存堆与 heap_1、heap_2 内存管理算法的内存堆一样都是一个数组heap_4 内存管理算法中定义的内存堆与 heap_1、heap_2 内存管理算法一样可以在FreeRTOSConfig.h 文件中配置 configTOTAL_HEAP_SIZE 配置项以配置内存堆的字节大小同样地也可以用过 configAPPLICATION_ALLOCATED_HEAP 配置项将内存堆定义在指定的内存地址中。用户可以通过函数 xPortGetFreeHeapSize()获取内存堆中未分配的内存总量根据系统运行时内存堆中剩余的内存空间大小就可以针对性地对 configTOTAL_HEAP_SIZE 配置项进行优化配置。与 heap_2 内存管理算法不同的是heap_4内存管理算法中空闲块链表中的内存块并不是按照内存块大小的顺序从小到大排序而是按照空闲块链表中内存块的起始地址大小从小到大排序这也是为了后续往空闲块链表中插入内存块时能够将相邻的内存块合并。heap_4 内存管理算法整体与 heap_2 内存管理算法很相似但是 heap_4 内存管理算法相较于 heap_2 内存管理算法能够将物理内存空间上相邻的两个空闲内存块合并成一个大的空闲内存块而这正是在将空闲内存块插入空闲块链表的时候实现的。与 heap_2 内存管理算法将空闲块链表中的空闲内存块按照内存块的内存大小从小到大排序的方式不同heap_4 内存管理算法是将空闲内存块链表中的空闲内存块按照内存块在物理内存上的起始地址从低到高进行排序的也正是因此才能够更加方便地找出物理内存地址相邻的空闲内存块并将其进行合并。将空闲内存块插入空闲块链表之前会先从头开始遍历空闲块链表按照内存块在物理内存上起始地址从低到高的排序规则找到空闲块要插入的位置。接着判断待插入空闲内存块的起始地址或结束地址是否分别与该位置前面内存块的结束地址或该位置后面内存块的起始地址相同如果相同侧表示待插入的空闲内存块在物理地址上与该位置前面的内存块或该位置后面的内存块相邻那么就将响铃的两个空闲内存块合并成一个大的内存块再将这个大的内存块插入到空闲块链表中这个操作的示意图如下所示以待插入空闲内存块与找到位置的上一个内存块相邻为例heap_4 内存管理算法具有如下特性适用于在程序中多次创建和删除任务、队列、信号量等的应用。与 heap_2 内存管理算法相比即使多次分配和释放随机大小的内存产生内存碎片的几率也要小得多。具有不确定性但是执行的效率比标准 C 库的内存管理高得多。2.6 heap_5 内存管理算法heap_5 内存管理算法是在 heap_4 内存管理算法的基础上实现的因为 heap_5 内存管理算法使用与 heap_4 内存管理算法相同的内存分配、释放和合并算法但是 heap_5 内存管理算法在 heap_4 内存管理算法的基础上实现了管理多个非连续内存区域的能力。heap_5 内 存 管 理 算 法 默 认 并 没 有 定 义 内 存 堆 需 要 用 户 手 动 调 用 函 数vPortDefindHeapRegions()并传入作为内存堆的内存区域的信息对其进行初始化。初始化后的内存堆将被作为空闲内存块链接到空闲块链表中再接下来的内存申请与释放就和 heap_4 内存管理算法一致了。要注意的是因为 heap_5 内存管理算法并不会自动创建好内存堆因此需要用户手动为 heap_5 初始化好作为内存堆的内存区域后才能够动态创建任务、队列、信号量等对象。3. FreeRTOS 系统配置FreeRTOS 使用FreeRTOSConfig.h文件进行配置和裁剪。此文件有几十个配置项这使得用户能够很好地配置和裁剪 FreeRTOS。配置项可分为三大类config 配置项、INCLUDE 配置项、其他配置项01抢占式调度和协程式调度的区别协程式调度是正在运行的任务主动释放 CPU 后才能切换到下一个任务任务切换的时机完全取决于正在运行的任务。协程式的优点在于可以节省开销但是功能比较有限现在的 MCU 性能都比较强大建议使用抢占式调度。02通用方法是完全使用 C 实现的软件算法因此支持所用硬件并且不限制任务优先级的最大值但效率相较于特殊方法低。特殊方法的效率相较于通用方法高但是特殊方法依赖于一个或多个特定架构的汇编指令一般是类似计算前导零[CLZ]的指令因此特殊方法并不支持所有硬件并且对任务优先级的最大值一般也有限制通常为 32。

相关新闻