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

FreeRTOS【2】任务、优先级知识重点

目录

  • 写在前面
  • 一.什么是任务
  • 二.创建任务
  • 三.使用任务参数
  • 四.任务的删除
  • 五.任务优先级和 Tick
    • 任务优先级
    • Tick
    • 修改优先级
  • 六.任务状态
  • 七.Delay 函数
  • 八.空闲任务及其钩子函数

写在前面

这篇文章只写关键的知识。

一.什么是任务

在 FreeRTOS 中,任务就是一个函数,原型如下

void ATaskFunction( void *pvParameters );

注意点:
这个函数不能返回

  • 同一个函数,可以用来创建多个任务;换句话说,多个任务可以运行同一个函数
  • 函数内部,尽量使用局部变量:
    -每个任务都有自己的栈
  • 每个任务运行这个函数时
    • 任务 A 的局部变量放在任务 A 的栈里、任务 B 的局部变量放在任务 B
      的栈里
    • 不同任务的局部变量,有自己的副本
  • 函数使用全局变量、静态变量的话
    • 只有一个副本:多个任务使用的是同一个副本。
    • 要防止冲突。

二.创建任务

BaseType_t xTaskCreate( TaskFunction_t pxTaskCode, // 函数指针, 任务函数
const char * const pcName, // 任务的名字
const configSTACK_DEPTH_TYPE usStackDepth, // 栈大小,单位为 word,10 表示
40 字节
void * const pvParameters, // 调用任务函数时传入的参数
UBaseType_t uxPriority, // 优先级
TaskHandle_t * const pxCreatedTask ); // 任务句柄, 以后使用它来操作这个任
参数名描述
pvTaskCode函数指针,可以简单地认为任务就是一个 C 函数。它稍微特殊一点:永远不退出,或者退出时要调用"vTaskDelete(NULL)"
pcName任务的名字,FreeRTOS 内部不使用它,仅仅起调试作用。长度为:configMAX_TASK_NAME_LEN
usStackDepth每个任务都有自己的栈,这里指定栈大小。单位是 word,比如传入 100,表示栈大小为 100 word,也就是 400 字节。最大值为 uint16_t 的最大值。怎么确定栈的大小,并不容易,很多时候是估计。精确的办法是看反汇编码。
pvParameters调用 pvTaskCode 函数指针时用到:pvTaskCode(pvParameters)
uxPriority优先级范围:0~(configMAX_PRIORITIES - 1)。数值越小优先级越低,如果传入过大的值,xTaskCreate 会把它调整为(configMAX_PRIORITIES - 1)
pxCreatedTask用来保存 xTaskCreate 的输出结果:task handle。以后如果想操作这个任务,比如修改它的优先级,就需要这个 handle。如果不想使用该 handle,可以传入 NULL。
返回值成功:pdPASS;失败:errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY(失败原因只有内存不足)。注意:文档里都说失败时返回值是 pdFAIL,这不对。pdFAIL 是 0,errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY 是-1。

三.使用任务参数

多个任务可以使用同一个函数,怎么体现它们的差别?

  • 栈不同。
  • 创建任务时可以传入不同的参数。

本质是就是不同的任务,只不过两个任务之间执行的逻辑是一样的。

四.任务的删除

删除任务时使用的函数如下:

void vTaskDelete( TaskHandle_t xTaskToDelete );
参数名描述
pvTaskCode任务句柄,使用xTaskCreate创建任务时可以得到一个句柄。也可传入NULL,这表示删除自己。

怎么删除任务?

  • 自杀: vTaskDelete(NULL)。
  • 被杀:别的任务执行 vTaskDelete(pvTaskCode), pvTaskCode 是自己的句
    柄 。
  • 杀人:执行 vTaskDelete(pvTaskCode), pvTaskCode 是别的任务的句柄。

五.任务优先级和 Tick

任务优先级

优先级的取值范围是: 0~(configMAX_PRIORITIES – 1),数值越大优先级越高。
FreeRTOS 的调度器可以使用 2 种方法来快速找出优先级最高的、可以运行的任务。
使用不同的方法时, configMAX_PRIORITIES 的取值有所不同。

  • 通用方法
    使用 C 函数实现,对所有的架构都是同样的代码。对 configMAX_PRIORITIES 的取值
    没有限制。但是 configMAX_PRIORITIES 的取值还是尽量小,因为取值越大越浪费内存,
    也浪费时间。
    configUSE_PORT_OPTIMISED_TASK_SELECTION 被定义为 0、或者未定义时,使用此方法。
  • 架构相关的优化的方法
    架构相关的汇编指令,可以从一个 32 位的数里快速地找出为 1 的最高位。使用这些
    指令,可以快速找出优先级最高的、可以运行的任务。使用这种方法时,
    configMAX_PRIORITIES 的取值不能超过 32。
    configUSE_PORT_OPTIMISED_TASK_SELECTION 被定义为 1 时,使用此方法。

FreeRTOS 会确保最高优先级的、可运行的任务,马上就能执行
 对于相同优先级的、可运行的任务,轮流执行

Tick

人有心跳,心跳间隔基本恒定。
FreeRTOS 中也有心跳,它使用定时器产生固定间隔的中断。这叫 Tick、滴答,比如
每 10ms 发生一次时钟中断。

注意,基于 Tick 实现的延时并不精确,比如 vTaskDelay(2)的本意是延迟 2 个 Tick 周
期,有可能经过 1 个 Tick 多一点就返回了。
在这里插入图片描述
使用 vTaskDelay 函数时,建议以 ms 为单位,使用 pdMS_TO_TICKS 把时间转换为 Tick。
这样的代码就与 configTICK_RATE_HZ 无关,即使配置项 configTICK_RATE_HZ 改变了,
我们也不用去修改代码。

修改优先级

- 获取优先级

UBaseType_t uxTaskPriorityGet( const TaskHandle_t xTask );

使用参数 xTask 来指定任务,设置为 NULL 表示获取自己的优先级。

- 修改优先级

void vTaskPrioritySet( TaskHandle_t xTask,
UBaseType_t uxNewPriority );

使 用 参 数 xTask 来 指 定 任 务 , 设 置 为 NULL 表 示 设 置 自 己 的 优 先 级 ;
参数 uxNewPriority 表示新的优先级,取值范围是 0~(configMAX_PRIORITIES – 1)。

六.任务状态

- 阻塞状态(Blocked)
任务处于想执行但是执行不了,需要契机才能运行,以依托与外部条件的。

  • 契机是什么?:
    • 时间
      • 等待一段时间,比如2min
      • 等到固定的时间
    • 同步事件:这事件由别的任务,或者是中断程序产生
      • 例子 1:任务 A 等待任务 B 给它发送数据
      • 例子 2:任务 A 等待用户按下按键
    • 同步事件的来源:
      • 队列(queue)
      • 二进制信号量(binary semaphores)
      • 计数信号量(counting semaphores)
      • 互斥量(mutexes)
      • 递归互斥量、递归锁(recursive mutexes) 事件组(event groups)
      • 任务通知(task notifications)

- 暂停状态(Suspended)
FreeRTOS 中的任务也可以进入暂停状态,暂停就是冻结了这个任务,或者隐藏了这个任务。
唯一的方法是通过 vTaskSuspend 函数。函
数原型如下:

void vTaskSuspend( TaskHandle_t xTaskToSuspend );

参数 xTaskToSuspend 表示要暂停的任务,如果为 NULL,表示暂停自己。
要退出暂停状态,只能由别人来操作:

  • 别的任务调用: vTaskResume
  • 中断程序调用: xTaskResumeFromISR
    实际开发中,暂停状态用得不多。

- 就绪状态(Ready)
随时响应的状态。

状态轮转图:
在这里插入图片描述

七.Delay 函数

很多人看Delay函数,望文生义,感觉就是简单的一个延时函数,其实不然,操作系统作为一个整体,各个任务交替执行井然有序的前提是需要一个delay来调节任务的节拍。
Delay类似操作系统世界的交通警察,该停的时候停,该动的时候动。
有两个 Delay 函数:

  • vTaskDelay:至少等待指定个数的 Tick Interrupt 才能变为就绪状态
  • vTaskDelayUntil:等待到指定的绝对时刻,才能变为就绪态。
void vTaskDelay( const TickType_t xTicksToDelay ); /* xTicksToDelay: 等待多少Tick
*/
/* pxPreviousWakeTime: 上一次被唤醒的时间
* xTimeIncrement: 要阻塞到时间(pxPreviousWakeTime + xTimeIncrement)
* 单位都是 Tick Count
*/
BaseType_t xTaskDelayUntil( TickType_t * const pxPreviousWakeTime,
const TickType_t xTimeIncrement );

使用 vTaskDelay(n)时,进入、退出 vTaskDelay 的时间间隔至少是 n 个
Tick 中断
使用 xTaskDelayUntil(&Pre, n)时,前后两次退出 xTaskDelayUntil 的时间
至少是 n 个 Tick 中断
退出 xTaskDelayUntil 时任务就进入的就绪状态,一般都能得到执行机会
所以可以使用 xTaskDelayUntil 来让任务周期性地运行。
在这里插入图片描述

八.空闲任务及其钩子函数

==一个良好的程序,它的任务都是事件
驱动的:平时大部分时间处于阻塞状态。==有可能我们自己创建的所有任务都无法执行,但
是调度器必须能找到一个可以运行的任务:所以,我们要提供空闲任务。在使用
vTaskStartScheduler()函数来创建、启动调度器时,这个函数内部会创建空闲任务:

  • 空闲任务优先级为 0:它不能阻碍用户任务运行.
  • 空闲任务要么处于就绪态, 要么处于运行态,永远不会阻塞.
    空闲任务的优先级为 0,这意味着一旦某个用户的任务变为就绪态,那么空闲任务马
    上被切换出去,让这个用户任务运行。在这种情况下,我们说用户任务"抢占"(pre-empt)
    了空闲任务,这是由调度器实现的。
    要注意的是:如果使用 vTaskDelete()来删除任务,那么你就要确保空闲任务有机会执
    行,否则就无法释放被删除任务的内存。

钩子函数:
我们可以添加一个空闲任务的钩子函数(Idle Task Hook Functions),空闲任务的循环
每执行一次,就会调用一次钩子函数。钩子函数的作用有这些:

  • 执行一些低优先级的、后台的、需要连续执行的函数
  • 测量系统的空闲时间:空闲任务能被执行就意味着所有的高优先级任务都
    停止了,所以测量空闲任务占据的时间,就可以算出处理器占用率。
  • 让系统进入省电模式:空闲任务能被执行就意味着没有重要的事情要做,
    当然可以进入省电模式了。

空闲任务的钩子函数的限制:

  • 不能导致空闲任务进入阻塞状态、暂停状态
  • 如果你会使用 vTaskDelete()来删除任务,那么钩子函数要非常高效地执行。
    如果空闲任务移植卡在钩子函数里的话,它就无法释放内存。

使用钩子函数的前提:

  • 宏定义为 1: configUSE_IDLE_HOOK
  • 实现 vApplicationIdleHook 函数
    在这里插入图片描述
http://www.xdnf.cn/news/1872.html

相关文章:

  • 【C语言】C语言结构体:从基础到高级特性
  • 深入解析 doas:有望替代 sudo 的极简权限管理工具
  • Dify快速入门之发布应用
  • Trae 编程工具 Cline 插件安装与 Claude 3.7 API Key 自定义配置详解
  • 修改RK3568 UBUNTU开机画面
  • C++ Lambda 表达式
  • 黑马点评商户查询缓存--缓存更新策略
  • shell练习(2)
  • github 简单访问方法(无魔法)
  • 数据库-数据类型、约束 和 DQL语言
  • QComboBox自适应下拉展开区域宽度但控件本身限制宽度
  • leetcode刷题日记——有效的括号
  • IOMUXC_SetPinMux的0,1参数解释
  • Java集合框架解析
  • 【TS入门笔记1---初识TS】
  • A*迷宫寻路
  • 【频谱分析仪与信号分析仪】异同比较
  • 【力扣刷题|第五天作业】二分查找-寻找旋转排序数组中的最小值 II
  • Redis Bitmaps
  • 网络编程!
  • Android 16强制横竖屏设置
  • SQL进阶知识:七、数据库设计
  • 每日英语:每周背10句
  • PyQt6实例_pyqtgraph散点图显示工具_代码分享
  • AI大模型从0到1记录学习 数据结构和算法 day20
  • 分片算法详解:原理、类型与实现方案
  • 链表-两两交换链表中的结点
  • 接地电阻柜的主要材料有哪些?
  • 树莓派的系统烧录
  • 差分探头关键性能参数解析