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

STM32Cube-FreeRTOS任务调度与任务管理-笔记

STM32Cube-FreeRTOS任务调度与任务管理-笔记

    • 一、任务调度机制
      • 1.1 调度算法类型
    • 二、抢占式调度实现与分析
      • 2.1 时间片轮转机制
      • 2.2 调度触发条件
      • 2.3 抢占式调度例子
    • 三、合作式调度实现
      • 3.1 核心逻辑
      • 3.1 合作式调度例子
    • 四、任务管理函数详解
      • 4.1 任务创建
        • 4.1.1 动态创建任务
        • 4.1.2 静态创建任务(手动分配资源)
      • 4.2 任务删除
      • 4.3 挂起与恢复任务
        • 4.3.1 挂起任务
        • 4.3.2 恢复任务
      • 4.4 调度器控制
        • 4.4.1 开启调度器
        • 4.4.2 暂停调度器
        • 4.4.3 恢复调度器
      • 4.5 延时与阻塞
        • 4.5.1 基础延时
        • 4.5.2 周期性精确延时
        • 4.5.3 终止延时
      • 4.6 时间相关函数
        • 4.6.1 获取当前Tick值
        • 4.6.2 终止任务延时
    • 五、配置与优化建议
      • 5.1 调度算法选择
      • 5.2 优先级与时间片配置
      • 5.3 低功耗优化
    • 六、常见问题与注意事项


一、任务调度机制

1.1 调度算法类型

在这里插入图片描述

FreeRTOS支持两种调度算法:

  1. 抢占式调度(Preemptive Scheduling)

    • 特点:高优先级任务可抢占低优先级任务的CPU使用权。
    • 时间片轮转
      • 启用时间片:相同优先级任务间按时间片(默认1ms)轮转。
      • 禁用时间片:仅在更高优先级任务就绪或当前任务主动让出CPU时切换。
    • 配置方式:在STM32CubeMX中设置 USE_PREEMPTIONEnable
      在这里插入图片描述
  2. 合作式调度(Cooperative Scheduling)

    • 特点:任务需主动让出CPU(如调用 taskYIELD()),否则不会被抢占。
    • 配置方式:在STM32CubeMX中设置 USE_PREEMPTIONDisable

二、抢占式调度实现与分析

2.1 时间片轮转机制

  • 基础时钟(Tick)

    • FreeRTOS基础时钟的一个定时周期称为一个时间片(timeslice),默认值为1ms。当使用时间片时,在基础时钟的每次中断里会要求进行一次上下文切换(contextswitching),函数xPortSysTickHandler()就是SysTick定时中断的处理函数。
    • 时间片长度可通过 configTICK_RATE_HZ 宏调整(如设置为100Hz即10ms/次)。 在这里插入图片描述
  • 中断优先级配置
    在这里插入图片描述

    • 优先级分组:默认将全部4位用于抢占优先级(如 configPRIO_BITS=4),系统中断优先级设为最低(15),确保RTOS任务优先级高于中断。

2.2 调度触发条件

  • 使用时间片时
    • 每个时间片(默认1ms)触发一次任务切换。
  • 不使用时间片时
    • 仅在以下情况切换任务:
      1. 更高优先级任务进入就绪态。
      2. 当前任务进入阻塞态或挂起态。
    • 优点:减少上下文切换频率,降低CPU负担。
    • 缺点:同优先级任务可能因时间分配不均导致不公平。

2.3 抢占式调度例子

在这里插入图片描述

这张图可以说明带时间片的抢占式任务优先级的特点。

假设task2具有高优先级,task1具有正常优先级,且这两个任务的优先级都高于空闲任务的优先级。那么首先T1时刻是空闲任务在运行,在这一段时间里面系统里面没有其他任务处于就绪状态。在T2时刻进行了调度,task一抢占CPU开始运行,这是因为task1的优先级高于空闲任务。我们在task 3时刻,task1就进入了阻塞态,就让出了CPU的使用权。空闲任务又进入了运行状态。

在T4时刻task1又进入了运行态,在T5时刻更高优先级的task2抢占了CPU的运行。那么这个时候,task1就进入了就绪态,在T6时刻task2进入了阻塞状态,让出了CPU使用权。那么task1就可以从就绪态变为运行态。在T7时刻task一进入了阻塞状态,主动让出了CPU使用权,空闲任务就又进入了运行状态。


三、合作式调度实现

3.1 核心逻辑

  • 特点
    • 使用合作式任务调度方法时,FreeRTOS不主动进行上下文切换,而是运行状态的任务进入阻塞状态,或显式地调用taskYIELD()函数让出CPU使用权时才进行上下文切换。
    • 任务不会发生抢占,所以也不使用时间片,函数taskYIELD()的作用就是主动申请进行一次上下文切换
  • 典型场景
    • 需精确控制任务执行顺序(如调试阶段)。
    • 任务间无优先级差异,需按需轮询。

3.1 合作式调度例子

在这里插入图片描述


四、任务管理函数详解

4.1 任务创建

4.1.1 动态创建任务
BaseType_t xTaskCreate(TaskFunction_t pxTaskCode,      // 任务函数指针const char * const pcName,      // 任务名称(调试用)configSTACK_DEPTH_TYPE usStackDepth, // 栈大小(单位:字)void * const pvParameters,      // 任务参数UBaseType_t uxPriority,         // 优先级(数值越小优先级越低)TaskHandle_t * const pxCreatedTask // 返回任务句柄
);

在这里插入图片描述

  • 特点:RTOS自动分配栈和TCB空间。
4.1.2 静态创建任务(手动分配资源)
TaskHandle_t xTaskCreateStatic(TaskFunction_t pxTaskCode,      // 任务函数指针const char * const pcName,      // 任务名称const uint32_t ulStackDepth,    // 栈大小(单位:字)void * const pvParameters,      // 任务参数UBaseType_t uxPriority,         // 优先级StackType_t * const puxStackBuffer, // 静态分配的栈空间StaticTask_t * const pxTaskBuffer // 静态分配的任务控制块
);

在这里插入图片描述

  • 特点:适用于内存有限的场景,需手动分配栈和TCB空间。

4.2 任务删除

void vTaskDelete(TaskHandle_t xTaskToDelete);
  • 功能
    • 删除指定任务(传入 NULL 表示删除自身)。
    • 自动释放RTOS分配的栈和TCB,但需手动释放任务内动态分配的内存。
    • 需要说明该函数它需要传入的参数是需要删除的任务的句柄。但需要注意,如果要删除的是任务,自己必须在跳出任务死循环之后,在退出任务函数之前执行vTaskDelete删除任务时,自动释放系统自动分配的内存,如动态分配的占空间和任务控制块。但是在任务内,用户自己分配的内存需要在删除任务之前手工释放。

4.3 挂起与恢复任务

4.3.1 挂起任务
void vTaskSuspend(TaskHandle_t xTaskToSuspend);
  • 功能
    • 挂起指定任务(传入 NULL 表示挂起自身)。
    • 挂起的任务不参与调度,需其他任务调用 vTaskResume() 恢复。
4.3.2 恢复任务
void vTaskResume(TaskHandle_t xTaskToResume);
  • 功能
    • 恢复被挂起的任务,使其进入就绪态。
    • 注意:只能在其他任务中调用(不可恢复自身)。

4.4 调度器控制

4.4.1 开启调度器
void vTaskStartScheduler();
  • 功能:启动RTOS调度器,开始任务调度。
4.4.2 暂停调度器
void vTaskSuspendAll();
  • 功能:暂停所有任务调度,进入临界区。
4.4.3 恢复调度器
BaseType_t xTaskResumeAll();
  • 功能:恢复调度器并返回暂停前的就绪任务状态。

4.5 延时与阻塞

4.5.1 基础延时
void vTaskDelay(TickType_t xTicksToDelay);
  • 功能:将任务阻塞指定Tick数(如 vTaskDelay(100) 阻塞100ms)。
4.5.2 周期性精确延时

在这里插入图片描述

BaseType_t xTaskDelayUntil(TickType_t * const pxPreviousWakeTime, // 上次唤醒时间const TickType_t xTimeIncrement        // 周期间隔(单位:Tick)
);
  • 功能:用于周期性任务,确保任务以固定间隔执行。
4.5.3 终止延时
void vTaskDelayUntil( /* ... */ );
  • 功能:可配合 xTaskDelayUntil() 实现精确周期任务。

4.6 时间相关函数

4.6.1 获取当前Tick值
TickType_t xTaskGetTickCount();
  • 功能:返回自系统启动以来的Tick总数。
4.6.2 终止任务延时
BaseType_t xTaskCheckForTimeOut( /* ... */ );
  • 功能:检查任务是否超时,用于手动处理延时逻辑。

五、配置与优化建议

5.1 调度算法选择

  • 抢占式调度
    • 适用场景:实时性要求高的系统(如工业控制)。
    • 配置:STM32CubeMX中启用 USE_PREEMPTION
  • 合作式调度
    • 适用场景:资源受限或需严格控制任务切换的场景。

5.2 优先级与时间片配置

  • 优先级分组
    • 通过 configPRIO_BITS 宏配置抢占优先级与子优先级分配(如 configPRIO_BITS=4 表示4位抢占优先级)。
  • 时间片调整
    • 通过 configTICK_RATE_HZ 修改Tick频率(如设置为100Hz降低中断频率)。

5.3 低功耗优化

  • Tickless模式
    • 配置 configUSE_TICKLESS_IDLE=1,在空闲时关闭SysTick,降低功耗。

六、常见问题与注意事项

  1. 任务栈大小
    • 动态任务需合理设置栈大小,避免栈溢出。
    • 静态任务需手动分配足够内存。
  2. 内存管理
    • 删除任务前需手动释放动态分配的内存。
  3. 中断优先级
    • 确保RTOS任务优先级低于系统中断(如 configMAX_SYSCALL_INTERRUPT_PRIORITY)。

相关文章:

  • Swift可以像Python一样在定义变量时省略var或者let?定义常量和变量的不同形式?const常量的不同形式?变量或常量修改?
  • 《解锁SCSS算术运算:构建灵动样式的奥秘》
  • 理解计算机系统_并发编程(1)_并发基础和基于进程的并发
  • Python 数据智能实战 (11):LLM如何解决模型可解释性
  • 最小单调子序列的长度+联通最小乘积
  • iview 分页改变每页条数时请求两次问题
  • 相交链表的解答
  • SONiC-OTN代码详解(具体内容待续)
  • leetcode:最小覆盖字符串
  • LeetCode 1007. 行相等的最少多米诺旋转 题解
  • php study 网站出现404 - Page Not Found 未找到
  • 深度学习中的数据增强:提升食物图像分类模型性能的关键策略
  • VTK入门指南
  • [三分钟学算法]分治-快速排序-最小的K个数:设计一个算法,找出数组中最小的k个数。以任意顺序返回这k个数均可。
  • 【数据结构】稀疏矩阵的快速转置
  • 架构思维:异构数据的同步一致性方案
  • P1802 5 倍经验日
  • 递归算法详解(Java 实现):从原理到高阶应用
  • 时序分解 | Matlab基于WOA-MVMD鲸鱼算法优化多元变分模态分解
  • 机器学习实操 第二部分 神经网路和深度学习 第11章 训练深度神经网络
  • 当AI开始谋财害命:从骗钱到卖假药,人类该如何防范?
  • 三百余英国王室藏品,一览爱德华时代的优雅
  • 江西望仙谷回应“游客凌晨等不到接驳车”:已限流,接驳车运行时间延长
  • 纽约大都会博物馆展“萨金特与巴黎”:从艺术生到明星画家
  • 国际著名学者Charles M. Lieber全职受聘清华深圳国际研究生院
  • 专访 | 杜普兰蒂斯:爱上中国文化,下一步努力提升速度