6.FreeRTOS之任务通知

什么是任务通知?
FreeRTOS 从版本 V8.2.0 开始提供任务通知这个功能,每个任务都有一个 32 位的通知值。按照 FreeRTOS 官方的说法,使用消息通知比通过二进制信号量方式解除阻塞任务快 45% , 并且更加 省内存(无需创建队列)。
在大多数情况下,任务通知可以替代二值信号量、计数信号量、事件标志组,可以替代长度为 1 的队列(可以保存一个 32 位整数或指针值),并且任务通知速度更快、使用的 RAM 更少!

任务通知值的更新方式

FreeRTOS 提供以下几种方式发送通知给任务 :
  1. 发送消息给任务,如果有通知未读, 不覆盖通知值
  2. 发送消息给任务,直接覆盖通知值
  3. 发送消息给任务,设置通知值的一个或者多个位
  4. 发送消息给任务,递增通知值
通过对以上方式的合理使用,可以在一定场合下替代原本的队列、信号量、事件标志组等。

任务通知的优势和劣势 

任务通知的优势
1. 使用任务通知向任务发送事件或数据,比使用队列、事件标志组或信号量快得多。
2. 使用其他方法时都要先创建对应的结构体,使用任务通知时无需额外创建结构体。
任务通知的劣势
1. 只有任务可以等待通知,中断服务函数中不可以,因为中断没有 TCB
2. 通知只能一对一,因为通知必须指定任务。
3. 等待通知的任务可以被阻塞, 但是发送消息的任务,任何情况下都不会被阻塞等待。
4. 任务通知是通过更新任务通知值来发送数据的,任务结构体中只有一个任务通知值,只能保持一个数据

任务通知相关 API 函数 

1. 发送通知
参数:
xTaskToNotify :需要接收通知的任务句柄;
ulValue :用于更新接收任务通知值, 具体如何更新由形参 eAction 决定;
eAction :一个枚举,代表如何使用任务通知的值;
返回值:
如果被通知任务还没取走上一个通知,又接收了一个通知,则这次通知值未能更新并返回
pdFALSE , 而其他情况均返回 pdPASS
BaseType_t xTaskNotifyAndQuery( TaskHandle_t xTaskToNotify,uint32_t ulValue,eNotifyAction eAction,uint32_t *pulPreviousNotifyValue );
参数:
xTaskToNotify :需要接收通知的任务句柄; ulValue :用于更新接收任务通知值, 具体如何更新 由形参 eAction 决定; eAction :一个枚举,代表如何使用任务通知的值;
pulPreviousNotifyValue :对象任务的上一个任务通知值,如果为 NULL , 则不需要回传, 这个时 候就等价于函数 xTaskNotify()
返回值:
如果被通知任务还没取走上一个通知,又接收了一个通知,则这次通知值未能更新并返回
pdFALSE , 而其他情况均返回 pdPASS
BaseType_t xTaskNotifyGive( TaskHandle_t xTaskToNotify );
参数:
xTaskToNotify :接收通知的任务句柄, 并让其自身的任务通知值加 1
返回值:
总是返回 pdPASS

2. 等待通知 

等待通知API函数只能用在任务,不可应用于中断中! 

uint32_t ulTaskNotifyTake( BaseType_t xClearCountOnExit,TickType_t xTicksToWait );
参数:
xClearCountOnExit :指定在成功接收通知后,将通知值清零或减 1 pdTRUE :把通知值清零(二值信号量);pdFALSE :把通知值减一(计数型信号量); xTicksToWait :阻塞等待任务通知值的最大时间;
返回值:
0 :接收失败 非 0 :接收成功,返回任务通知的通知值
BaseType_t xTaskNotifyWait( uint32_t ulBitsToClearOnEntry,uint32_t ulBitsToClearOnExit,uint32_t *pulNotificationValue,TickType_t xTicksToWait );
ulBitsToClearOnEntry :函数执行前清零任务通知值那些位 。 ulBitsToClearOnExit :表示在函数退出前,清零任务通知值那些位,在清 0 前,接收到的任务通知值会先被保存到形参
*pulNotificationValue 中。 pulNotificationValue :用于保存接收到的任务通知值。 如果 不需要使用,则设置为 NULL 即可 。 xTicksToWait :等待消息通知的最大等待时间。

实验Demo 

Demo1:模拟二值信号量

void StartTaskSend(void const * argument)
{/* USER CODE BEGIN StartTaskSend *//* Infinite loop */for(;;){if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET){osDelay(20);if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET){xTaskNotifyGive(TaskReceiveHandle);printf("任务通知:模拟二值信号量发送成功!\r\n");}while(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET);}osDelay(1);}/* USER CODE END StartTaskSend */
}void StartTaskReceive(void const * argument)
{/* USER CODE BEGIN StartTaskReceive */uint32_t rece = 0;/* Infinite loop */for(;;){if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_RESET){osDelay(20);if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_RESET){rece = ulTaskNotifyTake(pdTRUE, portMAX_DELAY);if(rece != 0)printf("任务通知:模拟二值信号量接收成功!\r\n");}while(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_RESET);}osDelay(1);}/* USER CODE END StartTaskReceive */
}

Demo2:模拟计数型信号量

void StartTaskSend(void const * argument)
{/* USER CODE BEGIN StartTaskSend *//* Infinite loop */for(;;){if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET){osDelay(20);if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET){xTaskNotifyGive(TaskReceiveHandle);printf("任务通知:模拟计数型信号量发送成功!\r\n");}while(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET);}osDelay(1);}/* USER CODE END StartTaskSend */
}void StartTaskReceive(void const * argument)
{/* USER CODE BEGIN StartTaskReceive */uint32_t rece = 0;/* Infinite loop */for(;;){if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_RESET){osDelay(20);if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_RESET){rece = ulTaskNotifyTake(pdFALSE, portMAX_DELAY);if(rece != 0)printf("任务通知:模拟计数型信号量接收成功!rece=%d\r\n",rece);}while(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_RESET);}osDelay(1);}/* USER CODE END StartTaskReceive */
}

Demo3:模拟事件标志组

void StartTaskSend(void const * argument)
{/* USER CODE BEGIN StartTaskSend *//* Infinite loop */for(;;){if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET){osDelay(20);if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET){printf("将bit0位置1\r\n");xTaskNotify(TaskReceiveHandle, 0x01, eSetBits);}while(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET);}if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_RESET){osDelay(20);if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_RESET){printf("将bit1位置1\r\n");xTaskNotify(TaskReceiveHandle, 0x02, eSetBits);}while(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_RESET);}osDelay(1);}/* USER CODE END StartTaskSend */
}void StartTaskReceive(void const * argument)
{/* USER CODE BEGIN StartTaskReceive */uint32_t notify_val,event_bit = 0;/* Infinite loop */for(;;){xTaskNotifyWait(0, 0xFFFFFFFF, &notify_val, portMAX_DELAY);if(notify_val & 0x01)event_bit |= 0x01;	//bit0置1if(notify_val & 0x02)event_bit |= 0x02;	//bit1置1if(event_bit == (0x01 | 0x02))	//0x01|0x02=0x03{printf("任务通知:模拟时间标志组接收成功!event_bit=%d\r\n",event_bit);event_bit = 0;}osDelay(1);}/* USER CODE END StartTaskReceive */
}

Demo4:模拟邮箱

 

void StartTaskSend(void const * argument)
{/* USER CODE BEGIN StartTaskSend *//* Infinite loop */for(;;){if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET){osDelay(20);if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET){printf("KEY1按下\r\n");xTaskNotify(TaskReceiveHandle, 0x01, eSetValueWithoutOverwrite);//eSetValueWithOverwrite不带覆写,eSetValueWithoutOverwrite带覆写}while(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET);}if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_RESET){osDelay(20);if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_RESET){printf("KEY2按下\r\n");xTaskNotify(TaskReceiveHandle, 0x02, eSetValueWithoutOverwrite);}while(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_RESET);}osDelay(1);}/* USER CODE END StartTaskSend */
}/* USER CODE END Header_StartTaskReceive */
void StartTaskReceive(void const * argument)
{/* USER CODE BEGIN StartTaskReceive */uint32_t notify_val;/* Infinite loop */for(;;){xTaskNotifyWait(0, 0xFFFFFFFF, &notify_val, portMAX_DELAY);printf("notify_val=%d\r\n",notify_val);osDelay(1);}/* USER CODE END StartTaskReceive */
}

记得点赞+收藏

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

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

相关文章

前端之html(一)

HTML定义: HTML 超文本标记语言 (1)骨架: HTML:整个网页 head:网页头部 boby:网页主体 title:网页标题 (2)标签关系: 1.嵌套 2.并列 (3)注释 语法:<!-- ... --> 基础: (4) 标签:双标签:<> ... </> 单标签:<> <br> …

书生第四期实训营基础岛——L1G3000浦语提示词工程实践

基础任务 任务要求 背景问题&#xff1a;近期相关研究指出&#xff0c;在处理特定文本分析任务时&#xff0c;语言模型的表现有时会遇到挑战&#xff0c;例如在分析单词内部的具体字母数量时可能会出现错误。任务要求&#xff1a;利用对提示词的精确设计&#xff0c;引导语言…

Android启动流程_SystemServer阶段

前言 上一篇文档我们描述了在 Android 启动流程中 Zygote 部分的内容&#xff0c;从 Zygote 的配置、启动、初始化等内容展开&#xff0c;描述了 Zygote 在 Android 启动中的功能逻辑。本篇文档将会继续 Android 启动流程的描述&#xff0c;从 SystemServer 进程的内容展开&am…

Flutter CustomScrollView 效果-顶栏透明与标签栏吸顶

CustomScrollView 效果 1. 关键组件 CustomScrollView, SliverOverlapAbsorber, SliverPersistentHeader 2. 关键内容 TLDR SliverOverlapAbsorber 包住 pinned为 true 的组件 可以被CustomScrollView 忽略高度。 以下的全部内容的都为了阐述上面这句话。初阶 Flutter 开发知…

Litctf-web

Litctf-web exx xxe&#xff0c; <?xml version"1.0" encoding"utf-8"?> <!DOCTYPE xxe [<!ELEMENT name ANY ><!ENTITY xxe SYSTEM "file:///flag" >]><user><username>&xxe;</username> …

线程模型介绍

线程模型的介绍 线程有三种模型&#xff1a;N:1用户线程模型&#xff0c;1:1核心线程模式&#xff0c;N:M混合线程模型 POSIX: Portable Operating System Interface(可移值操作系统接口) N&#xff1a;1用户线程模型 线程的实现建立在进程控制的机制之上&#xff0c;有用户…

2024 Rust现代实用教程:1.3获取rust的库国内源以及windows下的操作

文章目录 一、使用Cargo第三方库1.直接修改Cargo.toml2.使用cargo-edit插件3.设置国内源4.与windows下面的rust不同点 参考 一、使用Cargo第三方库 1.直接修改Cargo.toml rust语言的库&#xff1a;crate 黏贴至Cargo.toml 保存完毕之后&#xff0c;自动下载依赖 拷贝crat…

ML 系列:第 18 部 - 高级概率论:条件概率、随机变量和概率分布

文章目录 一、说明二、关于条件概率2.1 为什么我们说条件概率&#xff1f;2.2 为什么条件概率在统计学中很重要 三、 随机变量的定义3.1 定义3.2 条件概率中的随机变量 四、概率分布的定义五、结论 一、说明 条件概率是极其重要的概率概念&#xff0c;它是因果关系的数学表述&…

基于springboot的社区团购系统设计与实现

一、项目背景 网络交易&#xff08;Electronic Commerce&#xff09;&#xff1a;是指实现整个贸易过程中各阶段的贸易活动的电子化。网络交易是一种多技术的集合体。其业务可包括&#xff1a;信息交换、售后服务、销售、电子支付、运输、组建虚拟企业、公司和贸易伙伴可以共同…

一文读懂系列:SSL加密流量检测技术详解

SSL加密流量检测功能的主要目的是为了对加密流量做解密处理&#xff0c;并对解密后的流量做内容安全检查&#xff08;比如反病毒、入侵防御、URL远程查询、内容过滤、文件过滤和邮件过滤等&#xff09;和审计&#xff08;防止信息泄露&#xff09;。接下来我们详细介绍SSL加密流…

【C++】关联式容器

1.Set和Map 1.1 set的介绍 set是一个常用的关联式容器&#xff0c;它存储唯一的元素&#xff0c;这些元素默认情况下按照升序排序。其底层是一种自平衡的二叉搜索树(红黑树)。 set元素的键值就是实值&#xff0c;实值就是键值。set的元素允许插入删除但是不允许修改(具有const…

荣誉证书PSD素材(59套免费)

本作品提供荣誉证书PSD素材下载&#xff0c;格式为PSD&#xff1b; 请使用软件Photoshop进行编辑&#xff0c;作品中文字及图均可以通过软件修改和编辑&#xff1b; 点击下载: 荣誉证书PSD素材

第T6周:Tensorflow实现好莱坞明星识别

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 目标 具体实现 &#xff08;一&#xff09;环境 语言环境&#xff1a;Python 3.10 编 译 器: PyCharm 框 架: &#xff08;二&#xff09;具体步骤 1.查询…

Spring6框架搭建(自用)

一、什么是Spring 众所不周知&#xff0c;Spring就是爪哇人的春天&#xff0c;但是在框架程序设计之前都绕不开javaWeb 1.javaWeb框架发展史 1、ServletJSPJavaBean(跳转页面、业务逻辑判断、数据库查询) 2、MVC三层架构(M Model pojo(User)V-view(USP)C-(controller-servl…

linux-UART

参考博客 https://blog.csdn.net/m0_38106923/article/details/126024970?sharetypeblog&shareId126024970&sharereferAPP&sharesourceweixin_40933496&sharefromlink 1.串口 UART的全称是Universal Asynchronous Receiver and Transmitter&#xff0c;即异步…

大数据治理:策略、技术与挑战

随着信息技术的飞速发展&#xff0c;大数据已经成为现代企业运营和决策的重要基础。然而&#xff0c;大数据的复杂性、多样性和规模性给数据管理带来了前所未有的挑战。因此&#xff0c;大数据治理应运而生&#xff0c;成为确保数据质量、合规性、安全性和可用性的关键手段。本…

vue插件清除 所有console.log()

一、作用 1、提升性能console.log() 语句会消耗一定的性能&#xff0c;尤其是在频繁调用的情况下。在生产环境中移除这些语句可以提高应用的运行效率。 2、减少信息泄露console.log() 可以输出敏感信息&#xff08;如用户数据、API 响应等&#xff09;。在生产环境中&#xf…

DAY15|二叉树Part03|LeetCode: 513.找树左下角的值、112. 路径总和、106. 从中序与后序遍历序列构造二叉树

LeetCode: 513.找树左下角的值 力扣代码链接 文字讲解&#xff1a;LeetCode: 513.找树左下角的值 视频讲解&#xff1a;怎么找二叉树的左下角&#xff1f; 递归中又带回溯了&#xff0c;怎么办&#xff1f; 基本思路 对题目进行一下分析&#xff0c;要找二叉树最底层最左边节点…

ClkLog企业版(CDP)预售开启,更有鸿蒙SDK前来助力

新版本发布 ClkLog在上线近1年后&#xff0c;获得了客户的一致肯定与好评&#xff0c;并收到了不少客户对功能需求的反馈。根据客户的反馈&#xff0c;我们在今年三季度对ClkLog的版本进行了重新的规划与调整&#xff0c;简化了原有的版本类型&#xff0c;方便客户进行选择。 与…

C++:set和map的使用

目录 序列式容器和关联式容器 set set类的介绍 构造和迭代器 增删查 insert find和erase erase迭代器失效 lower_bound与upper_bound multiset和set的区别 map map类的介绍 pair类型介绍 构造和迭代器 增删查 map数据修改&#xff1a;重载operator[] multimap…