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

刘火良 FreeRTOS内核实现与应用之13——内存管理、中断管理

采取和消息队列一样的学习方法,看书<==>记录问题<==>代码<==>看书<==>笔记的步骤。

内容来源于:

1. 野火:12. 内存管理 — [野火]FreeRTOS 内核实现与应用开发实战—基于RT1052 文档;

2. Mastering the FreeRTOS™ Real Time Kernel

内存管理

基本概念

FreeRTOS的V9.0.0版本为我们提供了5种内存管理算法,分别是heap_1.c、heap_2.c、heap_3.c、heap_4.c、heap_5.c,源文件存放于FreeRTOSSourceportableMemMang路径下,在使用的时候选择 其中一个添加到我们的工程中去即可。

内存管理方案

FreeRTOS规定了内存管理的函数接口,具体见,但是不管其内部的内存管理方案是怎么实现的,所以,FreeRTOS可以提供多个内存管理方案,下面,就一起看看各个内存管理方案的区别。

    void *pvPortMalloc( size_t xSize );     //内存申请函数void vPortFree( void *pv );             //内存释放函数void vPortInitialiseBlocks( void );     //初始化内存堆函数size_t xPortGetFreeHeapSize( void );    //获取当前未分配的内存堆大小size_t xPortGetMinimumEverFreeHeapSize( void ); //获取未分配的内存堆历史最小值

1)对于heap_1.c、heap_2.c和heap_4.c这三种内存管理方案,内存堆实际上是一个很大的数组,定义为static uint8_t ucHeap[ configTOTAL_HEAP_SIZE],而宏定义configTOTAL_HEAP_SIZE则表示系统管理内存大小,单位为字,在FreeRTOSConfig.h中由用户设定。

2)对于heap_3.c这种内存管理方案,它封装了C标准库中的malloc()和free()函数,封装后的malloc()和free()函数具备保护,可以安全在嵌入式系统中执行。因此,用户需要通过编译器或者启动文件设置堆空间。

3)heap_5.c方案允许用户使用多个非连续内存堆空间,每个内存堆的起始地址和大小由用户定义。这种应用其实还是很大的,比如做图形显示、GUI等,可能芯片内部的RAM是不够用户使用的,需要外部SDRAM,那这种内存管理方案则比较合适。

中断管理

        区分中断优先级和任务优先级:

  • 任务是软件功能,与硬件无关;任务的优先级由编程人员在软件中分配,软件算法(调度器)决定哪个任务将处于运行状态;
  • 虽然是软件写的,但中断服务程序是硬件功能,硬件控制哪个中断服务程序运行,以及何时运行;
  • 最低优先级的中断会中断最高优先级的任务,任务却没有办法抢占ISR(中断服务程序);

基本概念

推迟中断处理

        ISR尽量短小精悍(记录中断产生的原因,并清除中断),中断所需要的其他处理工作通常可以在任务中执行,从而使中断服务程序尽快退出,这就是所谓的“推迟中断处理”,因为将中断所需要处理的工作从ISR“推迟”到了任务。

        将中断推迟给任务,允许编程人员相对于应用程序中的其他任务优先对待这些中断处理,而且能够使用全部的FreeRTOS API函数。

中断管理方案

重要的参数​:​xHigherPriorityTaskWoken

BaseType_t xHigherPriorityTaskWoken;

它是一个 ​变量类型为 BaseType_t(通常是 int类型的别名)​​ 的参数,​用于在中断服务程序(ISR)或某些特殊 API 中,通知系统是否有更高优先级的任务被唤醒,从而可能需要触发一次上下文切换(即任务切换)。​

一、它主要用在什么地方?
1. ​中断服务程序(ISR,Interrupt Service Routine)中;
2. ​与某些 FreeRTOS ​FromISR**API 配合使用**​
  • 比如:

    • xQueueSendFromISR()

    • xQueueReceiveFromISR()

    • xSemaphoreGiveFromISR()

    • xTaskNotifyFromISR()

    • 等等(很多带 ​FromISR​ 后缀的 API)


二、为什么要用 xHigherPriorityTaskWoken
背景知识:

在 FreeRTOS 中,​任务之间经常通过队列(Queue)、信号量(Semaphore)、事件组(Event Group)等通信机制来同步或传递数据

但有时候,这些操作是在 ​中断(ISR)中发起的,比如:

  • 一个外部中断发生了,传感器数据来了,你想 ​通过队列把数据发给任务去处理

  • 或者一个定时器中断触发,你要 ​释放一个信号量,唤醒等待的任务

问题是:

🤔 ​如果中断里唤醒了一个任务,而这个任务的优先级比当前正在运行的任务还要高,那是不是应该立刻切换过去,让高优先级任务先运行?​

👉 ​答案是:当然应该!但 FreeRTOS 不能自己随便切换任务,它需要你(开发者)告诉它:“嘿,我刚刚可能唤醒了一个高优先级任务,你看着办,可能需要任务切换!”​

这就是 xHigherPriorityTaskWoken的作用!


 三、xHigherPriorityTaskWoken的作用详解
📌 定义:

xHigherPriorityTaskWoken是一个标志变量,用于告诉 FreeRTOS:在当前 ISR 中,是否有一个比当前运行任务优先级更高的任务被唤醒了。如果是,FreeRTOS 可能需要在 ISR 结束时进行一次上下文切换(即任务切换)。​


📌 通常的使用模式如下:
步骤 1:​定义一个变量
BaseType_t xHigherPriorityTaskWoken = pdFALSE;

初始化为 pdFALSE,表示默认没有高优先级任务被唤醒。

步骤 2:​在中断中调用带有 _FromISR 后缀的 API,并传入该变量

例如,通过队列向某个任务发送数据:

xQueueSendFromISR(xQueue, &data, &xHigherPriorityTaskWoken);

这个函数会在 ISR 中尝试向队列发送数据,如果此时有任务在等待这个队列的数据,那么该任务会被唤醒。

  • 如果 ​被唤醒的任务的优先级 > 当前正在运行的任务优先级,那么:

    xHigherPriorityTaskWoken会被 API 内部设置为 pdTRUE

步骤 3:​判断是否需要触发上下文切换

在 ISR 的最后,你通常会调用:

portYIELD_FROM_ISR(xHigherPriorityTaskWoken);

或者:

portYIELD_FROM_ISR(xHigherPriorityTaskWoken == pdTRUE);

这个宏会根据 xHigherPriorityTaskWoken的值决定是否立即进行一次 ​任务切换(上下文切换)​,让更高优先级的任务得以运行。


四、通俗理解(类比)

你可以把整个过程想象成:

🏢 你是一家公司的老板(当前运行的任务),突然来了一个电话(中断),电话那头说:“有紧急文件需要马上交给经理(更高优先级任务)处理!”

你(ISR)就把文件交给了经理,但经理的优先级比你高,他拿到文件后应该立刻去处理,而不是等你这个老板忙完。

所以你(ISR)就要告诉公司的调度员(FreeRTOS 内核):“嘿,我刚刚叫经理来干活了,他比我还急,你最好现在就让他上场!”

👉 这个“提醒调度员”的动作,就是通过 ​xHigherPriorityTaskWoken+ portYIELD_FROM_ISR()​ 实现的。

五 、关键点总结

项目

说明

变量名

xHigherPriorityTaskWoken(类型一般为 BaseType_t

作用

用于在中断(ISR)中,标识是否有更高优先级的任务被唤醒,从而可能需要任务切换

常用场景

在调用 ​_FromISR​ 类 API(如 xQueueSendFromISR, xSemaphoreGiveFromISR等)时使用

初始值

通常初始化为 pdFALSE

API 是否会修改它?​

✅ 是的,如果本次操作导致高优先级任务被唤醒,相关 FromISR API 会将其设为 pdTRUE

如何使用它?​

最后通过 portYIELD_FROM_ISR(xHigherPriorityTaskWoken)通知内核是否需要任务切换

为什么重要?​

保证高优先级任务能够及时响应,提升系统的实时性与响应能力

如果不使用会怎样?​

可能导致高优先级任务无法及时运行,降低系统实时性,甚至出现逻辑错误


 六、补充:portYIELD_FROM_ISR()是什么?

它是一个 ​,定义在 FreeRTOS 的移植层(portmacro.h)中,其作用是:

  • 根据传入的值决定是否触发一次上下文切换(即任务切换)​

  • 它最终可能会调用类似 portYIELD()的底层函数,具体行为依赖于硬件平台(如 Cortex-M 的 PendSV 机制)

你一般不需要关心它的内部实现,只需要:

portYIELD_FROM_ISR(xHigherPriorityTaskWoken);

就可以了。


七、一句话记忆 🧠

xHigherPriorityTaskWoken就是中断里用来告诉 FreeRTOS:“我可能叫了个大佬(高优先级任务)来干活,你看着办要不要赶紧切换过去!” 的小旗子。​


重要方法:用于同步的二进制信号量

        可以把二进制信号量认为长度为1的队列一样,队列在任何时候只能最多包含一个数据项,要么空,要么满;

        任务通过xSemaphoreTake()函数,接收推迟中断处理的任务,尝试从队列中读取数据项,该读取操作有阻塞时间,如果队列为空,则导致任务进入阻塞状态;

        中断发生时,ISR调用xSemaphoreGiveFromISR() API函数将令牌(信号量)放入队列中,填满队列,这将导致任务退出阻塞状态并移除令牌,再次清空队列。

        当任务完成处理后,再次尝试从队列中读取,结果发现队列为空,便重新进入阻塞状态,等待下一次操作:

重要方法:用于同步的计数信号量

        可以把计数信号量认为是长度大于1的队列,任务对存储在队列中的数据不感兴趣——只对队列数据项的数量感兴趣;

        每次“释放”计数信号量时,都会使用其队列的另一个空间;队列数据项的数量就是信号量的“计数”值;

        1)计数事件

        事件处理程序将在每次事件发生时“释放”信号量——导致信号量的计数值在每次“释放”时递增;任务将在每次处理事件时“获取”信号量——导致信号量的计数值在每次“获取”时递减;

        2)资源管理

        计数值可以表示可用资源的数量;为了获得对资源的控制权,任务必须先“获取”信号量——减少信号量的计数值,当计数值到0时,就没有可用资源了;

        当使用完资源后,就会“释放”信号量——递增信号量的计数值;

重要方法:用于同步的队列

        二进制和计数信号量用于事件通信,队列用于事件通信和数据传输;

推迟工作到RTOS守护任务

方法有两种:

1)可以为每个使用推迟处理技术的中断创建一个任务;

2)也可以采用“集中式推迟中断处理”方法,使用xTimerPendFunctionCallFromISR() API函数将中断处理推迟到RTOS守护任务——消除了为每一个中断创建单独任务的需求。

 中断不同对时间限制的要求也不同,所以在同一个应用程序中,通常使用两种推迟中断处理方法。

xTimerPendFunctionCallFromISR()

xTimerPendFunctionCallFromISR()允许你在中断(ISR)中,​​“提交一个函数调用请求”​,这个函数调用不会立即执行,而是被 FreeRTOS ​挂起(pend)​,稍后由 ​一个后台的 Timer Service Task(定时器服务任务,也叫 Timer Daemon Task)​​ 在 ​任务上下文中安全地执行它。​

关键点总结

项目

说明

函数名

xTimerPendFunctionCallFromISR()

作用

允许你 ​在中断(ISR)中安全地提交一个函数调用请求,该调用会被 FreeRTOS 后台的 ​Timer Service Task(定时器守护任务)在任务上下文中执行

为什么有用?​

为了 ​不在中断中执行复杂逻辑,而是把任务“推迟”到后台任务中安全地执行,同时还能传递参数

被调用的函数格式

必须是:void 函数名(void *param1, uint32_t param2)

参数传递

支持传递 ​1 个指针参数 + 1 个 uint32_t 参数

与哪个任务相关?​

由 FreeRTOS 的 ​Timer Service Task(Timer Daemon Task)​​ 执行,该任务在后台运行,负责调用你提交的函数

是否需要配置 Timer Task?​

✅ ​是的!你必须开启 configUSE_TIMERS=1,否则这个功能不可用,因为该函数依赖 Timer Service Task

xTimerPendFunctionCall()vs xTimerPendFunctionCallFromISR()

函数

调用场景

说明

xTimerPendFunctionCall()

在任务中调用​(非中断)

功能一样,但是用于任务上下文

xTimerPendFunctionCallFromISR()

在中断中调用​(ISR)

中断安全版本,用于从中断中延迟调用函数


什么时候使用它?

推荐在以下场景使用:

场景

是否推荐使用

在中断中接收到数据,想让任务去处理

✅ 推荐 ✅

在中断中想调用某个任务函数,但不想直接调用

✅ 推荐 ✅

想在中断里延迟执行某个逻辑,但不想阻塞或耗时

✅ 推荐 ✅

想通过函数指针机制,实现中断与任务间的解耦通信

✅ 推荐 ✅

你不想用队列,但又想传递函数+参数到任务

✅ 推荐(一种轻量级替代方案)

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

相关文章:

  • 从“拉取请求“到“合并请求“:一个有趣的术语翻译现象
  • 第七章 面向对象编程(基础部分)
  • 联想M7400黑白激光打印机显示纸盒无纸反复卡纸在硒鼓下面维修一例
  • Redis 提高缓存命中率指南
  • 建设部网站工程设计收费标准怎么知道公司网站是哪个公司做的
  • 一、通用的FPGA开发流程介绍
  • Java Spring “核心基础”面试清单(含超通俗生活案例与深度理解)
  • 公司网站设计需要多少钱wordpress vip服务积分
  • 【吕口】知呼依茶-服务平台系统方案
  • YOLO入门教程(番外):目标检测的一阶段学习方法
  • 妇产科网站建设沧州微网网络信息有限公司
  • 北京房产网站建设濮阳网站建设专家团队
  • OpenCV 库函数
  • 信息发布网站怎么做如何做网站上抓视频
  • 【深度学习计算机视觉】10:转置卷积实战进阶——破解棋盘效应与工业级应用
  • ai调用excel整理板厚,零件,预计板耗信息保存为json
  • PyCharm 2025:最新图文教程!
  • Docker 实战教程(7) | 镜像管理和仓库操作
  • 百度快照抓取的是网站哪里的内容建站公司怎么接单
  • 江苏省建设工程竣工备案网站网站结构优化包括哪些
  • open manus实战:生成一个贪吃蛇游戏
  • 制作销售网站清浦网站建设
  • 建站为应用技术长沙有实力的关键词优化价格
  • 内网隧道突破:红队实战指南
  • 宿州北京网站建设保亭网站建设
  • Java-142 深入浅出 MySQL Spring事务失效的常见场景与解决方案详解(4)
  • 网站优化培训好学吗公司网站变更域名
  • 【开题答辩全过程】以 安全电子选举系统的设计与实现为例,包含答辩的问题和答案
  • ESP32项目(三、控制继电器,伺服电机,舵机)
  • Python 3 内置函数详解