深入理解_FreeRTOS的内部实现(2)

1.事件组

事件组结构体:

事件组 “不关中断” 的核心逻辑


事件组操作时,优先选择 “关调度器” 而非 “关中断” ,原因和实现如下:

关调度器(而非关中断)
FreeRTOS 提供 taskENTER_CRITICAL()(关调度器 + 按需关中断,取决于 configKERNEL_INTERRUPT_PRIORITY 配置 )或 vTaskSuspendAll()(仅关调度器 ),来保护事件组的任务级操作(如任务调用 xEventGroupSetBits )。
关调度器后,中断仍可正常触发(硬件中断不受影响),但任务无法切换,保证事件组操作的原子性。
中断里的事件组操作
中断函数中不能直接修改事件组(因为中断设置事件组的时候有可能唤醒多个满足条件的任务导致中断时间不确定),而是通过 xEventGroupSetBitsFromISR 触发 “守护任务”(daemon task) 来异步处理:
中断里向守护任务的队列发消息,请求设置事件位
守护任务(任务上下文)真正执行 xEventGroupSetBits,此时用关调度器保证安全。

事件组创建流程:

开始↓
调用 xEventGroupCreate() 函数↓
动态分配事件组内存(若支持静态分配,则使用 xEventGroupCreateStatic())↓
成功? ——→ 是 ——→ 初始化事件组状态(默认所有位为0)↓           ↓否          返回事件组句柄↓           ↓
返回 NULL    结束

设置事件位流程:

开始↓
[上下文判断] → 任务中调用 xEventGroupSetBits() / 中断中调用 xEventGroupSetBitsFromISR()↓
设置指定的事件位(按位或操作更新组值)(操作EventGroup_t 结构体)↓
检查是否有任务因等待这些位被阻塞?↓
是 ——→ 解除阻塞符合条件的任务↓        ↓否      触发上下文切换(若在任务中)或标记延迟调度(若在中断中)↓        ↓
结束 ←←←←←←

等待事件位流程:

开始↓
调用 xEventGroupWaitBits(),传入等待的位掩码、清除标志、阻塞时间等参数↓
检查当前事件位是否满足条件:是否所有指定位被置位?(逻辑AND)或任意指定位被置位?(逻辑OR)↓
满足? ——→ 是 ——→ 根据参数清除事件位↓           ↓否          返回当前事件位值↓           ↓
进入阻塞状态(若阻塞时间 > 0),释放CPU↓           ↓
等待事件被设置或超时 →→→ 超时? ——→ 是 ——→ 返回超时状态↓                               ↓否(事件触发)                    ↓↓                               ↓
返回触发后的事件位值              结束

2.任务通知

FreeRTOS 任务通知是一种轻量级任务间通信机制,用于任务或中断向指定任务发送事件或数据:
一、基本概念
核心原理:每个任务的控制块(TCB)中内置通知相关成员(如 ulNotifiedValue 存储通知值 ),无需额外创建通信结构体,可直接向目标任务发送 “通知”。
启用条件:需在 FreeRTOSConfig.h 中定义 configUSE_TASK_NOTIFICATIONS = 1 开启功能。
二、优势与局限
优势
高效性:无需额外结构体,操作直接,比队列、信号量、事件组更快,节省内存(每个任务仅额外占 8 字节存储通知状态和值 )。
灵活更新:支持多种通知值更新方式(如覆盖、保留原值、置位、递增等 ),适配不同场景。
局限
单播特性:仅能指定一个任务接收通知,无法广播给多任务。
数据缓存限制:任务控制块只有一个通知值,无法缓存多个数据,发送方也不能因发送受阻进入阻塞。
中断交互限制:可从中断发通知给任务,但无法给中断发通知(中断无任务结构体 )。

发通知流程:

开始↓
关闭中断 (taskENTER_CRITICAL)↓
目标任务存在?——否——→ 开启中断 ——→ 返回失败 ——→ 结束↓是
目标任务在等待通知?↓
是 ——→ 设置通知值↓从延迟列表移除目标任务↓将目标任务设为就绪状态↓目标任务优先级更高?——是——→ 开启中断 ——→ 设置需要上下文切换标志↓否                              ↓开启中断 ←————————————————————————— 触发任务调度 (portYIELD)↓                              ↓返回成功 ←————————————————————————— 返回成功↓                              ↓结束                           结束↓否
设置通知值(累加或覆盖)↓
开启中断 (taskEXIT_CRITICAL)↓
返回成功↓
结束

等待通知流程:

开始↓
关闭中断 (taskENTER_CRITICAL)↓
检查是否有待处理的通知↓
有通知? ——是——→ 清除/递减通知值 ——→ 开启中断 ——→ 返回通知值 ——→ 结束↓否
等待时间为0? ——是——→ 开启中断 ——→ 返回0 ——→ 结束↓否
将任务添加到延迟列表↓
设置任务状态为阻塞↓
开启中断 (taskEXIT_CRITICAL)↓
触发任务调度 (portYIELD)↓
其他任务运行...↓
收到通知或超时?↓
关闭中断 (taskENTER_CRITICAL)↓
从延迟列表移除任务↓
设置任务为就绪状态↓
开启中断 (taskEXIT_CRITICAL)↓
返回通知值(成功)或0(超时)↓
结束

3.软件定时器

软件定时器的核心机制就是时间到达预设值后自动触发回调函数执行

FreeRTOS 软件定时器的核心是基于系统 tick 的有序链表管理低优先级检测任务,通过回调函数实现定时事件处理。

内部实现:

一、核心数据结构

1. 定时器控制块(Timer_t

typedef struct tmrTimerControl {const char *pcTimerName;           // 定时器名称(调试用)ListItem_t xTimerListItem;         // 用于链表管理的节点TickType_t xTimerPeriodInTicks;    // 定时周期(tick数)UBaseType_t uxAutoReload;          // 是否自动重载(周期/单次模式)void (*pxCallbackFunction)(TimerHandle_t xTimer); // 回调函数指针void *pvTimerID;                   // 定时器ID(用户数据)TickType_t xExpireTime;            // 下次超时的绝对tick时间// ...其他内部字段
} Timer_t;

2. 定时器列表(按超时时间排序)

static List_t xActiveTimerList1;  // 活跃定时器列表1
static List_t xActiveTimerList2;  // 活跃定时器列表2(用于溢出处理)

二、实现机制

1.定时器任务(prvTimerTask

  • 核心职责
    • 优先级通常设为较低值(如configTIMER_TASK_PRIORITY),避免影响关键任务。
    • 周期性检查定时器列表,处理超时定时器。

执行流程:

void prvTimerTask(void *pvParameters) {for (;;) {// 1. 进入临界区,防止任务调度干扰taskENTER_CRITICAL();// 2. 获取当前系统tickTickType_t xTimeNow = xTaskGetTickCount();// 3. 检查活跃列表头部定时器是否超时while (listLIST_IS_EMPTY(&xActiveTimerList1) == pdFALSE) {Timer_t *pxTimer = (Timer_t *)listGET_OWNER_OF_HEAD_ENTRY(&xActiveTimerList1);if (pxTimer->xExpireTime <= xTimeNow) {// 4. 移除超时定时器(void)uxListRemove(&pxTimer->xTimerListItem);// 5. 退出临界区,执行回调(避免长时间持有锁)taskEXIT_CRITICAL();pxTimer->pxCallbackFunction((TimerHandle_t)pxTimer);taskENTER_CRITICAL();// 6. 若为周期模式,重新计算超时时间并加入列表if (pxTimer->uxAutoReload == pdTRUE) {pxTimer->xExpireTime += pxTimer->xTimerPeriodInTicks;vListInsertInOrder(&xActiveTimerList1, &pxTimer->xTimerListItem);}} else {break; // 后续定时器未超时,退出循环}}// 7. 退出临界区,进入阻塞状态直到下一个定时器超时或被唤醒taskEXIT_CRITICAL();vTaskDelayUntil(&xTimeNow, pdMS_TO_TICKS(10)); // 10ms检查一次}
}

2. 定时器添加 / 删除机制


添加定时器(xTimerStart()):
计算超时时间(当前tick + 定时周期)。
将定时器按超时时间插入有序链表(保证头部为最近超时的定时器)。
若新定时器成为链表头部,唤醒定时器任务重新计算阻塞时间。
删除定时器(xTimerDelete()):
从活跃链表中移除定时器节点。
标记定时器为无效状态,防止重复操作。

4.两套API

FreeRTOS 设计两套 API 的核心目标是保证中断处理的实时性和系统稳定性:

任务上下文 API:面向普通任务场景,允许阻塞和直接调度,简化编程。
中断安全 API:面向中断场景,避免长时间关中断,通过参数间接控制调度,确保中断快速响应

对比维度任务上下文 API(常规版本)中断安全 API(FromISR 版本)
命名规则无特殊后缀(如 xQueueSendvTaskDelay后缀为 FromISR(如 xQueueSendFromISRvTaskDelayFromISR
调用场景仅能在任务函数中调用,禁止在中断服务程序(ISR)中使用专门用于 ISR 或异常处理程序,可在中断环境中安全调用
中断状态处理无需显式处理中断状态(任务上下文默认开中断)需保存并恢复中断状态(如 portSET_INTERRUPT_MASK_FROM_ISR),避免破坏 ISR 上下文
上下文切换触发直接调用内核调度器(如 taskYIELD),主动触发切换通过 pxHigherPriorityTaskWoken 参数标记是否需切换,由 ISR 决定是否调用 portYIELD_FROM_ISR
关键参数差异常规参数(如队列句柄、数据指针、超时时间)部分函数增加 BaseType_t* pxHigherPriorityTaskWoken 参数,用于标记高优先级任务是否被唤醒
临界区保护机制使用 taskENTER_CRITICAL() 和 taskEXIT_CRITICAL()使用 portSET_INTERRUPT_MASK_FROM_ISR() 和 portCLEAR_INTERRUPT_MASK_FROM_ISR(),适配中断环境
返回值含义直接返回操作结果(如 pdPASSpdFAILpdTRUE除返回操作结果外,需通过 pxHigherPriorityTaskWoken 间接传递调度需求
阻塞特性支持阻塞(如等待队列或信号量时可指定超时时间)不支持阻塞(中断场景需快速返回,超时参数无效或被忽略)
典型函数示例xQueueSendxSemaphoreTakevTaskDelayvTaskSuspendxQueueSendFromISRxSemaphoreTakeFromISRvTaskDelayFromISRvTaskSuspendFromISR
内核调度介入方式函数内部主动触发调度(如任务切换)需由 ISR 根据函数返回结果决定是否触发调度
对系统实时性的影响可能因阻塞操作降低实时性(任务上下文允许)无阻塞操作,中断处理更高效,保证系统实时响应

FreeRTOS 在中断中检测到高优先级任务就绪时,会立即请求切换,但实际切换发生在中断返回后的安全时机(不是立即切换)。这一设计在保证中断处理完整性的同时,确保高优先级任务以最小延迟获得 CPU 资源

中断API切换高优先级任务流程:

5.FreeRTOS里的两类中断

特性系统中断用户中断
优先级范围高优先级(通常 0~4)低优先级(通常 5~15,取决于配置)
是否可调度否(不可抢占其他系统中断)是(可被系统中断抢占)
能否调用 FreeRTOS API仅能调用带FromISR后缀的 API可调用所有 API(需注意上下文)
对任务调度的影响可能直接触发任务切换(如 PendSV、SysTick)通过pxHigherPriorityTaskWoken标记间接触发切换
典型示例PendSV、SysTick、HardFault、NMI外设中断(如 UART、GPIO、定时器)

显然在 FreeRTOS 中,任务调度器依赖的定时器中断(通常是 SysTick 中断 )和 PendSV 中断,优先级配置是刻意设计为低优先级,当我们关闭中断时,任务也不再调度

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/pingmian/86711.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/86711.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【图论题典】Swift 解 LeetCode 最小高度树:中心剥离法详解

文章目录 摘要描述题解答案题解代码分析思路来源&#xff1a;树的“中心剥离法”构造邻接表和度数组循环剥叶子终止条件 示例测试及结果时间复杂度空间复杂度总结 摘要 树是一种重要的数据结构&#xff0c;在许多应用里&#xff0c;我们希望选一个根&#xff0c;让这棵树的高度…

Docker的介绍与安装

​ Docker 对初学者的简单解释和应用场景 1.什么是 Docker&#xff1f; 简单来说&#xff0c;Docker 就像一个“装箱子”的工具&#xff0c;这个箱子叫做“容器”。 你写的程序和它运行需要的环境&#xff08;比如操作系统、软件、工具&#xff09;都装进一个箱子里。这个箱…

引导相机:工业自动化的智能之眼,赋能制造业高效升级

在工业自动化浪潮中&#xff0c;精准的视觉引导技术正成为生产效率跃升的关键。作为迁移科技——一家成立于2017年、专注于3D工业相机和3D视觉系统的领先供应商&#xff0c;我们深知"引导相机"的核心价值&#xff1a;它不仅是一个硬件设备&#xff0c;更是连接物理世…

智能相机如何重塑工业自动化?迁移科技3D视觉系统的场景革命

从硬件参数到产业价值&#xff0c;解码高精度视觉系统的落地逻辑 一、工业视觉的“智慧之眼” 迁移科技深耕3D工业相机领域&#xff0c;以“稳定、易用、高回报”为核心理念&#xff0c;打造覆盖硬件、算法、软件的全栈式视觉系统。成立6年累计融资数亿元的背后&#xff0c;是…

【数据挖掘】聚类算法学习—K-Means

K-Means K-Means是一种经典的无监督学习算法&#xff0c;用于将数据集划分为K个簇&#xff08;clusters&#xff09;&#xff0c;使得同一簇内的数据点相似度高&#xff0c;不同簇间的相似度低。它在数据挖掘、模式识别和机器学习中广泛应用&#xff0c;如客户细分、图像压缩和…

linux环境内存满php-fpm

检查 PHP-FPM 配置 pm.max_children&#xff1a;该参数控制 PHP-FPM 进程池中最大允许的子进程数。过高的子进程数会导致内存占用过大。你可以根据服务器的内存大小来调整 pm.start_servers&#xff1a;控制 PHP-FPM 启动时创建的进程数。根据实际情况调整此值。 pm.min_spare_…

基于CNN卷积神经网络图像识别小程序9部合集

基于CNN卷积神经网络图像识别小程序合集-视频介绍下自取 ​ 内容包括&#xff1a; 基于python深度学习的水果或其他物体识别小程序 003基于python深度学习的水果或其他物体识别小程序_哔哩哔哩_bilibili 代码使用的是python环境pytorch深度学习框架&#xff0c;代码的环境安…

WebRTC(九):JitterBuffer

JitterBuffer Jitter “Jitter”指的是连续到达的媒体包之间时间间隔的变化。在网络传输中&#xff0c;由于&#xff1a; 网络拥塞路由路径变化队列排队不同链路带宽差异 导致包之间的接收时间不一致&#xff0c;这就是网络“抖动”。 作用 **JitterBuffer&#xff08;抖…

【推荐100个unity插件】在 Unity 中绘制 3D 常春藤,模拟生长——hedera插件的使用

注意&#xff1a;考虑到后续接触的插件会越来越多&#xff0c;我将插件相关的内容单独分开&#xff0c;并全部整合放在【推荐100个unity插件】专栏里&#xff0c;感兴趣的小伙伴可以前往逐一查看学习。 效果演示 文章目录 效果演示前言一、常春藤生成器工具下载二、工具使用1、…

【三维重建】【3DGS系列】【深度学习】3DGS的理论基础知识之高斯椭球的几何变换

【三维重建】【3DGS系列】【深度学习】3DGS的理论基础知识之高斯椭球的几何变换 文章目录 【三维重建】【3DGS系列】【深度学习】3DGS的理论基础知识之高斯椭球的几何变换前言模型变换(Model Transformation)观测变换(Viewing Transformation)视图变换(View Transformation)投影…

EXISTS 和 NOT EXISTS 、IN (和 NOT IN)

在 SQL 中&#xff0c;EXISTS、NOT EXISTS 和 IN 都是用于子查询的条件运算符&#xff0c;用于根据子查询的结果过滤主查询的行。它们之间的区别主要体现在工作方式、效率、对 NULL 值的处理以及适用场景上。 1. EXISTS 和 NOT EXISTS 作用&#xff1a; EXISTS: 检查子查询是…

GitHub 趋势日报 (2025年06月25日)

&#x1f4ca; 由 TrendForge 系统生成 | &#x1f310; https://trendforge.devlive.org/ &#x1f310; 本日报中的项目描述已自动翻译为中文 &#x1f4c8; 今日获星趋势图 今日获星趋势图 880 awesome 788 build-your-own-x 691 free-for-dev 427 best-of-ml-python 404 …

互联网大厂Java求职面试:Java虚拟线程实战

互联网大厂Java求职面试&#xff1a;Java虚拟线程实战 文章内容 开篇&#xff1a;技术总监与程序员郑薪苦的三轮对话 在一场紧张而严肃的Java工程师面试中&#xff0c;技术总监张工正对候选人郑薪苦进行深入提问。郑薪苦虽然性格幽默&#xff0c;但对技术有着扎实的理解。今天…

网络安全的两大威胁:XSS与CSRF攻击实例解析

在网络攻击中,XSS跨站脚本攻击(Cross Site Scripting)与CSRF跨站请求伪造攻击(Cross-Site Request Forgery)是两种常见的攻击方式,它们之间存在显著的区别。以下是对这两种攻击方式的详细比较: 一、攻击原理 XSS跨站脚本攻击 攻击者通过在Web页面中注入恶意脚本来实现攻…

如何一次性将 iPhone 中的联系人转移到 PC

许多重要的联系人都存储在您的 iPhone 上。为了保护关键信息&#xff0c;您可能需要将联系人从 iPhone 转移到 PC&#xff0c;这是一种有效的联系人备份方法。如果您在将 iPhone 联系人转移到电脑上遇到困难&#xff0c;现在可以从本文中学习 5 个有效的解决方案&#xff0c;然…

Spring Boot开启定时任务的三种方式 【@EnableScheduling注解,SchedulingConfigurer接口,Quartz 框架】

Spring Boot 开启定时任务的三种方式​ ​ ​ 在 Spring Boot 应用开发过程中&#xff0c;定时任务是十分常见的需求&#xff0c;比如定时清理日志文件、定期备份数据库数据、定时发送邮件提醒等。Spring Boot 提供了多种开启定时任务的方式&#xff0c;本文将详细介绍三种常见…

LLM 编码器 怎么实现语义相关的 Token 向量更贴近? mask训练:上下文存在 ;; 自回归训练:只有上文,生成模型

LLM 编码器 怎么实现语义相关的 Token 向量更贴近? 目录 LLM 编码器 怎么实现语义相关的 Token 向量更贴近?mask训练:上下文存在自回归训练:只有上文,生成模型一、核心机制:损失函数与反向传播的“语义校准”1. 损失函数的“语义约束”2. 嵌入层参数的“动态调整”二、关…

从OCR瓶颈到结构化理解来有效提升RAG的效果

当人们探讨如何让人工智能系统更好地从文档中查找和使用信息时&#xff0c;通常关注的是令人瞩目的算法和前沿的大型语言模型。但问题是&#xff1a;如果文本提取的质量很差&#xff0c;那么后续的努力都将付诸东流。本文探讨OCR质量如何影响检索增强生成&#xff08;RAG&#…

SpringBoot -- 整合Junit

11.SpringBoot 整合 Junit 11.1 为什么需要单元测试 由于在SpringBoot开发过程中&#xff0c;每开发一个模块&#xff0c;有时需要从 controller、service、mapper 到甚至 xml 文件的编写全部开发完毕才能进行测试&#xff0c;这是十分浪费时间的&#xff0c;比如开发人员想测…

虚拟机远程连接编译部署QT程序

概要 逻辑 我们需要凑齐 QT库、交叉编译工具、sysroot这三大件。 交叉编译的程序是部署到板卡环境运行,需要构建和板卡一样的库环境。 sysroot是我们在虚拟机上自己命名的一个文件夹,包含开发板的运行系统所需的所有文件。 虚拟机是x64版本,开发板是arm64版本。 如果开发板…