十八、任务通知

1、前言

(1)所谓“任务通知”,可以反过来读"通知任务"。我们使用队列、信号量、事件组等等方法时,并不知道对方是谁。使用任务通知时,可以明确指定:通知哪个任务。

(2)使用队列、信号量、事件组时,我们都需要实先创建对应的结构体,双方通过中间的结构体通信:

(3)使用任务通知时,任务结构体TCB中就包含了内部对象,可以直接接收别人发过来的“通知”:

(4)本文涉及的内容:

  • 任务通知:通知状态、通知值
  • 任务通知的使用场合
  • 任务通知的优势及限制

2、任务通知的特性

2.1、优势及限制

(1)任务通知的优势:

  • 效率更高:使用任务通知来发送事件、数据给某个任务时,效率更高。比队列、信号量、事件组都有优势。
  • 更节省内存:使用其他方法时都要先创建对应的结构体,使用任务通知时无需额外创建结构体。

(2)任务通知的限制:

  • 不能发送数据给ISR:ISR并没有任务结构体,所以无法使用任务通知的功能给ISR发送数据。但ISR可以使用任务通知的功能,发送数据给任务。
  • 数据只能给该任务独享:使用队列、信号量、事件组时,数据保存在这些结构体中,其他任务、ISR都可以访问这些数据。使用任务通知时,数据存放在目标任务中,只有它可以访问这些数据。在日常工作中,这个限制影响不大。因为很多场合是从多个数据源把数据发给某个任务,而不是把一个数据源发给多个任务。
  • 无法缓冲数据:使用队列时,假设队列深度为N,那么它可以保持N个数据。使用任务通知时,任务结构体中只有一个任务通知值,只能保持一个数据。
  • 无法广播给多个任务:使用事件组可以同时给多个任务发送事件。使用任务通知,只能发给一个任务。
  • 如果发送受阻,发送方无法进入阻塞状态等待:假设队列已经满了,使用 xQueueSendToBack() 给队列发送数据时,任务可以进入阻塞状态等待发送完成。使用任务通知时,即使对方无法接收数据,发送方也无法阻塞等待,只能即刻返回错误。

2.2、通知状态和通知值

(1)每个任务都有一个结构体:TCB(Task Control Block),里面有2个成员:

  • 一个是uint8_t类型,用来表示通知状态
  • 一个是uint32_t类型,用来表示通知值
typedef struct tskTaskControlBlock
{....../* configTASK_NOTIFICATION_ARRAY_ENTRIES = 1 */volatile uint32_t ulNotifiedValue[ configTASK_NOTIFICATION_ARRAY_ENTRIES ];volatile uint8_t ucNotifyState[ configTASK_NOTIFICATION_ARRAY_ENTRIES ];......
} tskTCB;

(2)通知状态有3种取值:

  • taskNOT_WAITING_NOTIFICATION:任务没有在等待通知
  • taskWAITING_NOTIFICATION:任务在等待通知
  • taskNOTIFICATION_RECEIVED:任务接收到了通知,也被称为pending(有数据了,待处理)
##define taskNOT_WAITING_NOTIFICATION ( ( uint8_t ) 0 )  /* 也是初始状态 */
##define taskWAITING_NOTIFICATION     ( ( uint8_t ) 1 )
##define taskNOTIFICATION_RECEIVED    ( ( uint8_t ) 2 )

(3)通知值可以有很多种类型:

  • 计数值
  • 位(类似事件组)
  • 任意数值

3、任务通知的使用

3.1、两类函数

(1)任务通知有2套函数,简化版、专业版,列表如下:

  • 简化版函数的使用比较简单,它实际上也是使用专业版函数实现的
  • 专业版函数支持很多参数,可以实现很多功能
简化板专业板
发出通知

xTaskNotifyGive

vTaskNotifyGiveFromISR

xTaskNotify

xTaskNotifyFromISR

取出通知ulTaskNotifyTakexTaskNotifyWait

3.2、简化版

(1)在任务中使用xTaskNotifyGive函数,在ISR中使用vTaskNotifyGiveFromISR函数;两个函数都是直接给其他任务发送通知:

  • 使得通知值加1。
  • 并使得通知状态变为“pending”,也就是 taskNOTIFICATION_RECEIVED ,表示有数据了,待处理。

(2)可以使用 ulTaskNotifyTake 函数来取出通知值:

  • 如果通知值等于0,则阻塞(可以指定超时时间)
  • 当通知值大于0时,任务从阻塞状态进入就绪态
  • 在 ulTaskNotifyTask 返回之前,还可以做些清理工作:把通知值减1,或者把通知值清零。

(3)使用ulTaskNotifyTask函数可以实现轻量级的、高效的二进制信号量、计数型信号量。

(4)函数原型如下:

BaseType_t xTaskNotifyGive( TaskHandle_t xTaskToNotify ); 
void vTaskNotifyGiveFromISR( TaskHandle_t xTaskHandle, BaseType_t *pxHigherPriorityTaskWoken ); 
uint32_t ulTaskNotifyTake( BaseType_t xClearCountOnExit, TickType_t xTicksToWait ); 

(5)xTaskNotifyGive函数的参数说明如下:

参数说明
xTaskToNotify任务句柄(创建任务时得到),给哪个任务发通知
返回值必定返回pdPASS

(6)vTaskNotifyGiveFromISR函数的参数说明如下:

参数说明
xTaskHandle任务句柄(创建任务时得到),给哪个任务发通知
xTaskHandle

被通知的任务,可能正处于阻塞状态。
此函数发出通知后,会把它从阻塞状态切换为就绪态。

如果被唤醒的任务的优先级,高于当前任务的优先级,
则" *pxHigherPriorityTaskWoken "被设置为pdTRUE,
这表示在中断返回之前要进行任务切换。

(7)ulTaskNotifyTake函数的参数说明如下:

参数说明
xClearCountOnExit函数返回前是否清零:
pdTRUE:把通知值清零
pdFALSE:如果通知值大于0,则把通知值减1
xTicksToWait

任务进入阻塞态的超时时间,它在等待通知值大于0。

0:不等待,即刻返回;

portMAX_DELAY:一直等待,直到通知值大于0;

其他值:Tick Count,可以用 pdMS_TO_TICKS() 把ms转换为Tick Count

返回值

函数返回之前,在清零或减1之前的通知值。

如果xTicksToWait非0,则返回值有2种情况:

1. 大于0:在超时前,通知值被增加了
2. 等于0:一直没有其他任务增加通知值,最后超时返回0

3.3、专业版

(1)xTaskNotify函数功能更强大,可以使用不同参数实现各种功能,比如:

  • 让接收任务的通知值加1:这时 xTaskNotify() 等同于 xTaskNotifyGive() 。
  • 设置接收任务的通知值得某一位、某些位,这就是一个轻量级的、更高效的事件组。
  • 把一个新值写入接收任务的通知值:上一次的通知值被读走后,写入才成功。这就是轻量级的、长度为1的队列。
  • 用一个新值覆盖接收任务的通知值:无论上一次的通知值是否被读走,覆盖都成功。类似xQueueOverwrite() 函数,这就是轻量级的邮箱。

(2)xTaskNotify() 比 xTaskNotifyGive() 更灵活、强大,使用上也就更复杂。

(3)xTaskNotifyFromISR() 是 xTaskNotify() 对应的ISR版本。

(4)xTaskNotifyFromISR() 和 xTaskNotify() 函数用来发出通知任务,使用 xTaskNotifyWait() 函数来取出任务通知。xTaskNotifyWait() 比 ulTaskNotifyTake() 更复杂:

  • 和 ulTaskNotifyTake() 一样,可以让任务等待(可以加上超时时间),等到任务状态为“pending”(也就是有数据)。
  • 还可以在函数进入、退出时,清除通知值得指定位。

(5)这几个函数的原型如下:

BaseType_t xTaskNotify( TaskHandle_t xTaskToNotify, uint32_t ulValue,eNotifyAction eAction );BaseType_t xTaskNotifyFromISR( TaskHandle_t xTaskToNotify,uint32_t ulValue,eNotifyAction eAction,BaseType_t *pxHigherPriorityTaskWoken );BaseType_t xTaskNotifyWait( uint32_t ulBitsToClearOnEntry,uint32_t ulBitsToClearOnExit,uint32_t *pulNotificationValue,TickType_t xTicksToWait );

(6)xTaskNotify函数的参数说明如下:

参数说明
xTaskToNotify任务句柄(创建任务时得到),给哪个任务发通知
ulValue怎么使用ulValue,由eAction参数决定
eAction见下表
返回值

pdPASS:成功,大部分调用都会成功

pdFAIL:只有一种情况会失败,当eAction为eSetValueWithoutOverwrite,
并且通知状态为"pending"(表示有新数据未读),这时就会失败。

eAction参数说明:

eAction取值说明
eNoAction

仅仅是更新通知状态为“pending”,未使用ulValue。

这个选项相当于轻量级的、更高效的二进制信号量。

eSetBits通知值 = 原来的通知值 | ulValue,按位或。
相当于轻量级的、更高效的事件组。
eIncrement通知值 = 原来的通知值 + 1,未使用ulValue。
相当于轻量级的、更高效的二进制信号量、计数型信号量。
相当于 xTaskNotifyGive() 函数。
eSetValueWithoutOverwrite

不覆盖。
如果通知状态为"pending"(表示有数据未读),

则此次调用xTaskNotify不做任何事,返回pdFAIL。
如果通知状态不是"pending"(表示没有新数据),

则:通知值 = ulValue。

eSetValueWithOverwrite覆盖。
无论如何,不管通知状态是否为"pendng",
通知值 = ulValue。

(7)xTaskNotifyFromISR函数跟xTaskNotify很类似,就多了最后一个参数pxHigherPriorityTaskWoken 。在很多ISR函数中,这个参数的作用都是类似的,使用场景如下:

  • 被通知的任务,可能处于阻塞状态
  • xTaskNotifyFromISR 函数发出通知后,会把接收任务从阻塞状态切换为就绪态
  • 如果被唤醒的任务的优先级,高于当前任务的优先级,则"*pxHigherPriorityTaskWoken"被设置为pdTRUE,这表示在中断返回之前要进行任务切换。

(8)xTaskNotifyWait 函数列表如下:

参数说明
ulBitsToClearOnEntry

在xTaskNotifyWait入口处,要清除通知值的哪些位?

通知状态不是"pending"的情况下,才会清除。

它的本意是:我想等待某些事件发生,所以先把"旧数据"的某些位清零。

能清零的话:通知值 = 通知值 & ~(ulBitsToClearOnEntry)。

比如传入0x01,表示清除通知值的bit0;
传入0xFFFFFFFF即ULONG_MAX,表示清除所有位,即把值设置为0

ulBitsToClearOnExit

在xTaskNotifyWait出口处,如果不是因为超时退出,

而是因为得到了数据而退出时:

通知值 = 通知值 & ~(ulBitsToClearOnExit)。
在清除某些位之前,通知值先被赋给"*pulNotificationValue"。
比如入0x03,表示清除通知值的bit0、bit1;
传入0xFFFFFFFF即ULONG_MAX,表示清除所有位,即把值设置为0

pulNotificationValue

用来取出通知值。
在函数退出时,使用ulBitsToClearOnExit清除之前,

把通知值赋给"*pulNotificationValue"。
如果不需要取出通知值,可以设为NULL。

xTicksToWait任务进入阻塞态的超时时间,它在等待通知状态变为"pending"。
0:不等待,即刻返回;
portMAX_DELAY:一直等待,直到通知状态变为"pending";
其他值:Tick Count,可以用 pdMS_TO_TICKS() 把ms转换为Tick Count
返回值1. pdPASS:成功
这表示xTaskNotifyWait成功获得了通知:
可能是调用函数之前,通知状态就是"pending";
也可能是在阻塞期间,通知状态变为了"pending"。
2. pdFAIL:没有得到通知。

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

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

相关文章

2023年“中银杯”四川省职业院校技能大赛“云计算应用”赛项样题卷②

2023年“中银杯”四川省职业院校技能大赛“云计算应用”赛项(高职组) 样题(第2套) 目录 2023年“中银杯”四川省职业院校技能大赛“云计算应用”赛项(高职组) 样题(第2套) 模块…

Topics(动态路由)

Topic类型的Exchange与Direct相比,都是可以根据RoutingKey把消息路由到不同的队列中。只不过Topic类型Exchange可以让队列在绑定路由时可以使用通配符。 *:匹配不多不少刚好一个单词。 #:匹配一个或多个词。 举例: audit.#可以匹配…

解密C++中的forward<int>(a)和forward<int >(a):你真的了解它们之间的区别吗?

一文看尽C中的forward完美转发 一、前言二、深入理解forward和完美转发三、对forward<int>(a)的解析四、对forward<int &&>(a)的解析五、forward<int>(a)和forward<int &&>(a)的区别总结 一、前言 完美转发在C中具有重要性&#xff0…

一文掌握Java注解之@SpringBootApplication知识文集(1)

&#x1f3c6;作者简介&#xff0c;普修罗双战士&#xff0c;一直追求不断学习和成长&#xff0c;在技术的道路上持续探索和实践。 &#x1f3c6;多年互联网行业从业经验&#xff0c;历任核心研发工程师&#xff0c;项目技术负责人。 &#x1f389;欢迎 &#x1f44d;点赞✍评论…

Spring Cloud Gateway + Nacos 实现动态路由

1、maven 依赖 主要依赖 <!-- 网关 --><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-gateway</artifactId></dependency>案件差不多完整主要依赖 <!--Spring boot 依赖(微服务基…

fanout(扇出模型)

在广播的流程下&#xff0c;消息发送的流程如下&#xff1a; 可以有多个消费者。 每个消费者有自己的queue(队列)。 每个队列都要绑定到Exchange(交换机)。 生产者发送的消息&#xff0c;只能发送到交换机&#xff0c;交换机来决定要发给哪个队列&#xff0c;生产者也无法决…

oracle物化视图

物化视图定义 视图是一个虚拟表&#xff08;也可以认为是一条语句&#xff09;&#xff0c;基于它创建时指定的查询语句返回的结果集&#xff0c;每次访问它都会导致这个查询语句被执行一次&#xff0c;为了避免每次访问都执行这个查询&#xff0c;可以将这个查询结果集存储到…

【算法题】矩阵顺时针旋转90° (js)

力扣链接&#xff1a;https://leetcode.cn/problems/rotate-matrix-lcci/description/ 本人题解&#xff1a; /*** param {number[][]} matrix* return {void} Do not return anything, modify matrix in-place instead.*/ var rotate function (matrix) {const x matrix.le…

Spring高手之路-Spring事务的传播机制(行为、特性)

目录 含义 七种事务传播机制 1.REQUIRED&#xff08;默认&#xff09; 2.REQUIRES_NEW 3.SUPPORTS 4.NOT_SUPPORTED 5.MANDATORY 6.NEVER 7.NESTED 含义 Spring事务的传播机制是指在多个事务方法相互调用时&#xff0c;如何处理这些事务的传播行为。对应七种事务传播行为…

Halcon闭运算closing

Halcon闭运算 文章目录 Halcon闭运算 闭运算的计算步骤&#xff0c;为先膨胀&#xff0c;后腐蚀。这两步操作能将看起来很接近的元素&#xff0c;如区域内部的空洞或外部孤立的点连接成一体&#xff0c;区域的外观和面积也不会有明显的改变。通俗地说&#xff0c;就是类似于“填…

Linux 权限掌控术:深入探索和用户管理

文章目录 前言1.外壳程序是什么&#xff1f;外壳程为什么存在&#xff1f;工作原理外壳程序怎么个事&#xff1f; 2. Linux权限的概念2.1 什么是权限2.2权限的本质2.3 Linux中的用户 3. 普通用户变成rootlinux中有三种人 4.Linux中文件的权限4.1文件的属性权限4.2 掌握修改权限…

2024.1.1每日一题

LeetCode每日一题 新的一年开始了&#xff0c;祝大家新年快乐&#xff0c;坚持做每日一题。 1599.经营摩天轮的最大利润 1599. 经营摩天轮的最大利润 - 力扣&#xff08;LeetCode&#xff09; 题目描述 你正在经营一座摩天轮&#xff0c;该摩天轮共有 4 个座舱 &#xff0…

DevOps系列之 JNI实现Java调用C的实现案例

JNI&#xff08;Java Native Interface&#xff09;允许Java代码与其他语言编写的代码进行交互。以下是一个简单的JNI示例&#xff0c;演示如何使用JNI在Java中调用C/C函数。 最终的目录结构如下&#xff1a; JNI&#xff08;Java Native Interface&#xff09;允许Java代码与其…

服务器硬件及RAID配置实战

目录 1、RAID的概念 2、RAID的实现方式 3、标准的RAID 3.1 RAID 0 3.2 RAID 1 3.3 RAID 5 3.4 RAID 10 4、建立硬件 RAID的过程步骤 1、进入RAID 1.1 重启服务器 1.2 进入RAID界面 1.3 在RAID界面切换目录 2、创建RAID 2.1 移动到RAID卡 2.2 按F2&#xff0c;选择…

lenovo联想拯救者8.8英寸掌上游戏机Legion Go 8APU1(83E1)原装出厂Windows11预装系统

链接&#xff1a;https://pan.baidu.com/s/1d586XWXcAWVxlLyV2Oku7Q?pwdd74t 提取码&#xff1a;d74t 系统自带所有驱动、出厂主题壁纸、Office办公软件、联想电脑管家等预装程序 所需要工具&#xff1a;16G或以上的U盘 文件格式&#xff1a;ISO 文件大小&#xff1a;…

适用于各种危险区域的火焰识别摄像机,实时监测、火灾预防、安全监控,为安全保驾护航

火灾是一种极具破坏力的灾难&#xff0c;对人们的生命和财产造成了严重的威胁。为了更好地预防和防范火灾&#xff0c;火焰识别摄像机作为一种先进的监控设备&#xff0c;正逐渐受到人们的重视和应用。本文将介绍火焰识别摄像机在安全监控和火灾预防方面的全面应用方案。 一、火…

操作系统(Operator System)

这里写目录标题 1. 什么是操作系统2. 主要功能3. 计算机的层状结构4. 什么叫做管理5. 总结6. 为什么要有操作系统7. 最后 1. 什么是操作系统 操作系统&#xff08;英语&#xff1a;Operating System&#xff0c;缩写&#xff1a;OS&#xff09;是一组主管并控制计算机操作、运…

CRM客户关系管理系统

系统开发环境以及版本 操作系统&#xff1a; Windows_7集成开发工具&#xff1a; Eclipse EE_4.7编译环境&#xff1a;JDK_1.8Web服务器&#xff1a;Tomcat_9.0数据库&#xff1a;MySQL_5.7.23 系统框架 spring框架springmvc框架mybatis框架Logback日志框架安全验证框架maven框…

你逛过凌晨四点的校园吗?--大四毕业生的年终总结

前言&#xff1a; Hello大家好&#xff0c;我是Dream。 又是一年的年终总结&#xff0c;我也迎来了自己的毕业季&#xff0c;没错&#xff0c;我马上要毕业啦&#xff01;不知道大家是什么时候认识我的呢&#xff0c;又或者是第一次发现我~这一年&#xff0c;迎接过朝阳、拍下过…

Filezilla使用

服务端 点击安装包 点击我接受 点击下一步 点击下一步 点击下一步 点击安装即可 配置用户组&#xff0c;点击编辑&#xff0c;出现组点击 点击添加&#xff0c;点击确定即可 配置用户&#xff0c;点击编辑点击用户 点击添加&#xff0c;设置用户名&#xff…