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…

FastAPI 核心概念:构建高性能的 Python Web 服务

FastAPI 核心概念&#xff1a;构建高性能的 Python Web 服务 本文详细解析了如何利用 FastAPI 构建高性能的 Python Web 服务&#xff0c;帮助开发者从零开始创建功能丰富、响应迅速的 API。结合完整的代码示例和分步操作指导&#xff0c;文章深入讲解了 FastAPI 的核心概念&a…

Redis 实战 问题

前言 相关系列 《Redis & 目录》《Redis & 实战 & 源码》《Redis & 实战 & 总结》《Redis & 实战 & 问题》 什么缓存击穿&#xff1f;怎么避免&#xff1f; 所谓缓存击穿是指请求因缓存失效而直接访问数据库的情况&#xff0c;由于热点数据访…

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;信息交换、售后服务、销售、电子支付、运输、组建虚拟企业、公司和贸易伙伴可以共同…

挑战Java面试题复习第4天,坚持就是胜利

挑战第 4 天 Excption与Error包结构OOM 知识点SOF 知识点线程程序进程知识点有些字段不想序列化&#xff0c;怎么办&#xff1f;说说 IO 流Java IO与 NIO的区别 Excption与Error包结构 运行时异常&#xff08;RuntimeException&#xff09;&#xff1a; 包括RuntimeException…

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

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

dependencyManagement保持maven的多模块依赖版本一致

在maven的多模块中, 为保持jar包在每个子模块中版本一致, 一般会有两种选择&#xff1a; 在父pom的dependencies标签中声明一个jar&#xff0c;每个子模块都会默认继承该jar 如果只有部分子模块用到了一个jar, 则会选择将该jar坐标信息声明在父pom的dependencyManagement标签中…

axis 参数的方向

axis0&#xff1a;表示沿着行的方向进行操作&#xff0c;即对每一列进行操作。 axis1&#xff1a;表示沿着列的方向进行操作&#xff0c;即对每一行进行操作。 示例&#xff1a; import numpy as np# 创建一个二维数组 array np.array([[1, 2, 3],[4, 5, 6],[7, 8, 9] ])axis0…

golang的循环引用解决方法

循环引用是指在数据结构中&#xff0c;两个或多个实体相互引用&#xff0c;形成一个闭环。例如&#xff0c;在 Golang 的结构体中&#xff0c;如果结构体 A 包含结构体 B 的实例&#xff0c;而结构体 B 又包含结构体 A 的实例&#xff0c;这种情况就称为循环引用。以下是关于循…

【C++】关联式容器

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

Spring Cloud Function快速入门Demo

1.什么是Spring Cloud Function&#xff1f; Spring Cloud Function是一个具有以下高级目标的项目&#xff1a; 通过功能促进业务逻辑的实现。将业务逻辑的开发生命周期与任何特定的运行时目标脱钩&#xff0c;以便可以将相同的代码作为Web终结点&#xff0c;流处理器或任务来…

第3章 继承与多态

Java面向对象程序设计-T3(继承与多态) 一、封装 1、概述 封装是指隐藏对象的属性和实现细节,仅对外提供公共访问方式。 public class Student {//1、私有化属性private String name;private int age;//2、提供公共方法供外部访问//传入数据public void setName(String nam…

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

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