1. FreeRTOS 调度机制概述

FreeRTOS 是一个实时操作系统(RTOS),其核心功能是通过 调度器(Scheduler) 管理多个任务的执行。调度机制决定了 何时切换任务 以及 如何选择下一个运行的任务,以满足实时性、优先级和资源共享的需求。以下是 FreeRTOS 调度机制的全面解析:


1.1.调度器的基本工作原理

FreeRTOS 调度器的主要职责是:

  1. 任务切换(Context Switching)保存当前任务的上下文(寄存器、栈等),并恢复下一个任务的上下文。

  2. 任务选择:根据任务状态(就绪、阻塞、挂起)和优先级,决定下一个运行的任务。

  3. 事件响应:处理中断、信号量、队列等事件触发的任务状态变化。

调度触发条件

  • 主动让出 CPU:任务调用 taskYIELD() 或阻塞(如 vTaskDelay()xQueueReceive())。

  • 系统心跳(Tick 中断)SysTick 或定时器中断触发调度检查。

  • 中断服务程序(ISR):某些中断(如 PendSV)强制触发任务切换。


1.2 调度策略

FreeRTOS 支持两种主要调度策略:

(1) 抢占式调度(Preemptive Scheduling)

  • 默认模式,高优先级任务可抢占低优先级任务。

  • 特点

    • 高优先级任务就绪时,立即获得 CPU。

    • 适用于硬实时系统(如电机控制、传感器采集)。


void vHighPriorityTask(void *pvParams) {while (1) {printf("High Priority Task Running!\n");vTaskDelay(100); // 短暂阻塞,让出 CPU}
}void vLowPriorityTask(void *pvParams) {while (1) {printf("Low Priority Task Running...\n");vTaskDelay(1000); // 长时间阻塞}
}

 结果vHighPriorityTask 会频繁抢占 vLowPriorityTask

(2) 协作式调度(Cooperative Scheduling)

  • 需手动调用 taskYIELD() 让出 CPU,无抢占。

  • 特点

    • 任务必须主动放弃 CPU,否则一直运行。

    • 适用于简单系统,但实时性较差。


1.3 任务状态与调度

FreeRTOS 任务可能处于以下状态:

状态描述
就绪(Ready)任务准备就绪,等待调度器分配 CPU。
运行(Running)当前正在执行的任务(单核 CPU 同一时间只有一个)。
阻塞(Blocked)任务因等待事件(如延时、信号量、队列)而暂停,不占用 CPU。
挂起(Suspended)任务被显式挂起(vTaskSuspend()),不参与调度,需手动恢复(vTaskResume())。

调度器选择任务的规则

  1. 优先选择 最高优先级的就绪任务

  2. 同优先级任务按 时间片轮转(Round-Robin) 分配 CPU(需启用 configUSE_TIME_SLICING)。


1.4 调度器实现细节

(1) 任务切换的底层机制

  • PendSV 中断
    FreeRTOS 使用 PendSV(可挂起的系统调用)进行任务切换,确保切换过程 原子化,避免在中断中直接切换导致嵌套问题。

  • 上下文保存与恢复

    • 硬件自动保存部分寄存器(R0-R3R12LRPCxPSR)。

    • 软件手动保存剩余寄存器(R4-R11)。

任务切换流程

  1. 触发 PendSV 中断。

  2. 保存当前任务的寄存器到其栈中。

  3. 调用 vTaskSwitchContext() 选择新任务。

  4. 从新任务的栈恢复寄存器。

  5. 返回新任务继续执行。

(2) 调度器启动

调用 vTaskStartScheduler() 后:

  1. 创建 空闲任务(Idle Task)(优先级 0,用于清理资源)。

  2. 初始化系统心跳(SysTick 定时器)。

  3. 开始调度第一个任务。

2 FreeRTOS 中的 Tick 中断详解

Tick 中断(系统心跳中断)是 FreeRTOS 调度和任务管理的核心机制,它通过周期性中断为系统提供时间基准,驱动任务调度、延时管理和超时检测。以下是 Tick 中断的全面解析:

2.1 Tick 中断的作用

(1) 核心功能

  • 提供系统时间基准
    维护全局时钟计数器 xTickCount,记录系统启动后的 Tick 次数(1 Tick = configTICK_RATE_HZ 分之一秒)。

  • 任务延时管理
    检查阻塞任务的超时时间(如 vTaskDelay()),唤醒到期任务。

  • 调度触发
    在抢占式调度模式下,每个 Tick 中断会检查是否需要切换任务(如同优先级任务时间片轮转)。

  • 软件定时器
    驱动 FreeRTOS 的软件定时器(xTimer)回调。

(2) 典型应用场景

  • 周期性任务(如每 100ms 采集一次传感器数据)。

  • 超时控制(如等待信号量时设置超时时间)。

  • 低功耗模式(Tickless 模式下动态调整中断间隔)。


2.2 Tick 中断的硬件实现

(1) 硬件依赖

  • 定时器选择:通常使用 MCU 的 SysTick 定时器(Cortex-M 内核内置),也可用通用定时器(如 TIMx)。

  • 中断频率:由 configTICK_RATE_HZ 定义(通常 100Hz~1kHz)。
    示例配置(FreeRTOSConfig.h):

    c

    复制

    下载

    #define configTICK_RATE_HZ 1000  // 1kHz Tick,每1ms中断一次

(2) 初始化流程

FreeRTOS 启动时(vTaskStartScheduler())会初始化 Tick 中断:

  1. 配置定时器周期(如 SysTick_Config(SystemCoreClock / configTICK_RATE_HZ))。

  2. 设置中断优先级(通常为最低优先级,避免影响其他中断)。

  3. 启用定时器和中断。


2.3 Tick 中断的处理流程

(1) 中断服务程序(ISR)

当 Tick 中断发生时:

  1. 递增时钟计数器xTickCount++

  2. 检查任务延时列表

    • 遍历阻塞任务列表,若有任务延时到期,则将其移至就绪列表

  3. 触发调度(可选)

    • 若启用抢占式调度(configUSE_PREEMPTION=1),检查是否需要任务切换。

  4. 处理软件定时器(可选)

    • 若启用 configUSE_TIMERS=1,检查定时器回调。

(2) 关键代码(Cortex-M 示例)

void SysTick_Handler(void) {// 1. 进入中断上下文portENTER_CRITICAL();  // 关中断(防止嵌套)// 2. 调用 FreeRTOS 的 Tick 处理函数if (xTaskIncrementTick() != pdFALSE) {portYIELD();  // 触发任务切换(通过 PendSV)}// 3. 退出中断portEXIT_CRITICAL();
}

2.4 Tick 中断与任务调度

(1) 时间片轮转(Round-Robin)

  • 同优先级任务共享 CPU
    每个 Tick 中断会检查当前任务是否用完时间片(configUSE_TIME_SLICING=1),若用完则切换任务。

  • 示例
    两个同优先级任务 TaskA 和 TaskB 会交替运行(每 1 Tick 切换一次)。

Tick中断函数的作用:
(1)取出下一个Task
(2)切换:
a.保存当前Task
b.恢复新的Task

(2) 延时与阻塞

  • vTaskDelay() 的实现
    任务调用 vTaskDelay(ticks) 后,会被移入阻塞列表,直到 xTickCount 递增到指定值

    void vTaskDelay(TickType_t xTicksToDelay) {vTaskSuspendAll();  // 暂停调度器xTickCount += xTicksToDelay;prvAddCurrentTaskToDelayedList(xTicksToDelay);xTaskResumeAll();   // 恢复调度器
    }

例如存在以下三个任务,其中任务1和任务2优先级相同,任务3优先级最高。

#include <stdio.h>/* Scheduler includes. */
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"/* Library includes. */
#include "stm32f10x_it.h"extern 	void UART_Init(unsigned long ulWantedBaud);/* Demo app includes. */
static void prvSetupHardware( void );static volatile int flagIdleTaskrun = 0;  // 空闲任务运行时flagIdleTaskrun=1
static volatile int flagTask1run = 0;     // 任务1运行时flagTask1run=1
static volatile int flagTask2run = 0;     // 任务2运行时flagTask2run=1
static volatile int flagTask3run = 0;     // 任务3运行时flagTask3run=1/*-----------------------------------------------------------*/void vTask1( void *pvParameters )
{/* 任务函数的主体一般都是无限循环 */for( ;; ){flagIdleTaskrun = 0;flagTask1run = 1;flagTask2run = 0;flagTask3run = 0;/* 打印任务的信息 */printf("T1\r\n");				}
}void vTask2( void *pvParameters )
{	/* 任务函数的主体一般都是无限循环 */for( ;; ){flagIdleTaskrun = 0;flagTask1run = 0;flagTask2run = 1;flagTask3run = 0;/* 打印任务的信息 */printf("T2\r\n");				}
}void vTask3( void *pvParameters )
{	const TickType_t xDelay5ms = pdMS_TO_TICKS( 5UL );		/* 任务函数的主体一般都是无限循环 */for( ;; ){flagIdleTaskrun = 0;flagTask1run = 0;flagTask2run = 0;flagTask3run = 1;/* 打印任务的信息 */printf("T3\r\n");				// 如果不休眠的话, 其他任务无法得到执行vTaskDelay( xDelay5ms );}
}void vApplicationIdleHook(void)
{flagIdleTaskrun = 1;flagTask1run = 0;flagTask2run = 0;flagTask3run = 0;	/* 故意加入打印让flagIdleTaskrun变为1的时间维持长一点 *///printf("Id\r\n");				
}int main( void )
{prvSetupHardware();xTaskCreate(vTask1, "Task 1", 1000, NULL, 0, NULL);xTaskCreate(vTask2, "Task 2", 1000, NULL, 0, NULL);xTaskCreate(vTask3, "Task 3", 1000, NULL, 2, NULL);/* 启动调度器 */vTaskStartScheduler();/* 如果程序运行到了这里就表示出错了, 一般是内存不足 */return 0;
}
/*-----------------------------------------------------------*/static void prvSetupHardware( void )
{/* Start with the clocks in their e

如果在任务3中调用vTaskDelay(ticks),这里设定延迟5ms,运行后可以看到,任务1,2,3是交替运行的,如下图所示。

如果不调用vTaskDelay(ticks),因为任务3的优先级最高,任务1和2将无法运行,如下所示:

注:若任务1,任务2和任务3的优先级相同,则这三个任务轮流执行,且是任务3先执行,然后是任务1,最后是任务2执行。 

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

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

相关文章

Docker安装(精简述版)

1. 安装&#xff1a;Docker 环境&#xff08;Docker desktop&#xff09; #Windows架构版本查看&#xff0c;Win R‌ 输入 ‌cmd‌ 打开命令提示符&#xff1b;输入命令‌&#xff1a; bash echo %PROCESSOR_ARCHITECTURE%#安装Docker desktop&#xff08;安装时勾选 WSL2&am…

Postman-win64-7.3.5-Setup.exe安装教程(附详细步骤+桌面快捷方式设置)​

Postman 是一款超常用的接口调试工具&#xff0c;程序员和测试人员用它来发送网络请求、测试API接口、调试数据交互​ 1. 双击安装包​ 安装包下载地址&#xff1a;https://pan.quark.cn/s/4b2960d60ae9&#xff0c;找到你下的 Postman-win64-7.3.5-Setup.exe 文件&#xff08…

149. Java Lambda 表达式 - Lambda 表达式的序列化

文章目录149. Java Lambda 表达式 - Lambda 表达式的序列化为什么要序列化 Lambda 表达式&#xff1f;Lambda 表达式的序列化规则示例代码&#xff1a;序列化 Lambda 表达式代码解析&#xff1a;Lambda 序列化的限制总结&#xff1a;149. Java Lambda 表达式 - Lambda 表达式的…

颐顿机电携手观远BI数据:以数据驱动决策,领跑先进制造智能化升级

颐顿机电签约观远数据&#xff0c;聚焦财务分析、销售管理等场景&#xff0c;以 BI 数据解决方案推进数据驱动决策&#xff0c;助力先进制造企业提效与竞争力升级。一、合作官宣&#xff1a;颐顿机电 观远数据&#xff0c;开启数据应用新征程浙江颐顿机电有限公司&#xff08;…

【PHP】几种免费的通过IP获取IP所在地理位置的接口(部分免费部分收费)

目录 一、获取客户端IP地址 二、获取IP所在地理位置接口 1、IP域名归属地查询 2、腾讯地图 - IP定位 3、聚合数据 - IP地址&#xff08;推荐&#xff09; 4、高德地图 - IP定位&#xff08;推荐&#xff09; 5、360分享计划 - IP查询 6、天聚ip地址查询 7、百度IP地址…

【Excel】制作双重饼图

一、效果话不多说&#xff0c;直接上数据和效果图&#xff01;&#xff08;示例软件&#xff1a;WPS Office&#xff09;类别现金刷卡小计苹果10.005.0015.00荔枝20.0015.0035.00西瓜30.0025.0055.00总计60.0045.00105.00二、步骤&#xff08;一&#xff09;制作底图插入饼图&a…

gcc-arm-none-eabi安装后,找不到libgcc.a的拉置

位置在&#xff1a;/usr/lib/gcc/arm-none-eabi/6.3.1/libgcc.a查找方法&#xff1a;arm-none-eabi-gcc --print-libgcc-file-name以前没找到&#xff0c;是因为进错目录&#xff1a;/usr/lib/arm-none-eabi/lib

上证50期权2400是什么意思?

本文主要介绍上证50期权2400是什么意思&#xff1f;“上证50期权2400”通常指上证50ETF期权的某个具体合约代码&#xff0c;其中“2400”是合约代码的一部分&#xff0c;需结合完整代码格式理解其含义。上证50期权2400是什么意思&#xff1f;一、上证50期权合约代码的组成上证5…

发那科机器人P点位置号码自动变更功能为禁用状态

通过改变变量的状态&#xff0c;发那科机器人可以实现&#xff0c;当在程序中进行记录、修改、插入、删除、复制/粘贴包含有P点位置号码的行时&#xff0c;P点位置号码会自动从小到大自动排列&#xff0c;可以实现自动排列&#xff0c;或者点击编辑变更编号也可以下图所示女变量…

什么叫湖仓一体

文章目录概念一、理解湖仓一体&#xff1a;先搞懂“数据湖”和“数据仓库”1. 数据仓库&#xff08;Data Warehouse&#xff09;2. 数据湖&#xff08;Data Lake&#xff09;3. 传统架构的痛点&#xff1a;“湖”与“仓”的割裂二、湖仓一体的核心特点&#xff1a;融合“湖”与…

网络安全突发事件应急预案方案

最近有要求需要出一个网络安全突发事件应急预案方案&#xff0c;本文仅就应急预案问题提出一点初步思考&#xff0c;意在抛砖引玉&#xff0c;盼各位读者不吝赐教&#xff0c;共同完善对这一领域的认识。一、总则 &#xff08;一&#xff09;目的 为有效应对规划建筑设计院企业…

【基于3D Gaussian Splatting的三维重建】保姆级教程 | 环境安装 | 制作-训练-测试自己数据集 | torch | colmap | ffmpeg | 全过程图文by.Akaxi

目录 一.【3DGS环境配置】 1.1 克隆3DGS仓库 1.2 安装Visual Studio 2022 1.2.1 下载Visual Studio 2022 1.2.2 更改环境变量 1.3 创建环境 1.3.1 创建python环境 1.3.2 离线安装torch包 1.3.3 安装依赖包 1.3.4安装子模块 &#xff08;1&#xff09;报错解决&…

C#泛型委托讲解

1. 泛型&#xff08;Generics&#xff09; 泛型允许编写类型安全且可重用的代码&#xff0c;避免装箱拆箱操作&#xff0c;提高性能。 泛型类 // 定义泛型类 public class GenericList<T> {private T[] items;private int count;public GenericList(int capacity){items …

【DL学习笔记】DL入门指南

DL入门指南 资料课程 李沐老师 《动手学深度学习》 https://tangshusen.me/Dive-into-DL-PyTorch/李宏毅老师课程 https://speech.ee.ntu.edu.tw/~hylee/ml/2021-spring.php DL入门必掌握知识点 数据处理 &#xff1a; numpy、torch地址处理 &#xff1a; os、pathlib文件处…

在 uni-app 中进行路由跳转前的权限验证(检查用户是否登录)

使用场景&#xff1a; 适用于需要登录才能访问的 uni-app 应用保护需要认证的页面不被未授权用户访问统一处理路由跳转的权限控制 /utils/cookies.js下的部分代码内容&#xff1a; // #ifdef H5 import Cookies from js-cookie // #endif// ums const tokenKey user_center_to…

垃圾收集器ParNewCMS与底层三色标记算法详解

垃圾收集技术详解笔记 1. 分代收集理论 当前虚拟机的垃圾收集采用分代收集算法&#xff0c;根据对象存活周期将内存分为不同代区&#xff0c;以优化回收效率。 核心分区&#xff1a; 新生代&#xff08;Young Generation&#xff09;&#xff1a;对象存活周期短&#xff0c;约9…

全排列(回溯算法)

本文参考代码随想录 给定一个 没有重复 数字的序列&#xff0c;返回其所有可能的全排列。 示例: 输入: [1,2,3] 输出: [ [1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2], [3,2,1] ] 思路 排列是有序的&#xff0c;在排列问题中不需要startIndex&#xff1b;但排列问题需要一个…

在线任意长度大整数计算器

具体请前往&#xff1a;在线大整数计算器--支持超大整数的加减乘除,幂运算/模运算,最大公约数&#xff0c;最小公倍数

AT6668B芯片说明书

这颗北斗专用单芯片解决方案AT6668B&#xff0c;采用射频前端与基带处理一体化设计&#xff0c;集成北斗二号/三号双模B1IB1C信号处理器。通过优化星历解码算法实现秒级卫星锁定&#xff0c;配合硬件加速的干扰监测模块&#xff0c;在电磁环境复杂的应用场景中仍可维持10Hz高频…

谷歌Chrome浏览器安装插件

因为google浏览器的应用市场(https://chrome.google.com/webstore/category/extensions)在国内无法访问,所以无法在线安装插件,这里提供开发者模式离线安装插件的方法。 1、下载crx脚本 谷歌浏览器的插件离线文件的扩展名为:crx(Firefox火狐浏览器的插件扩展名为fpi)。…