彻底掌握FreeRTOS中的务通知(Task Notifications)

news2024/9/21 13:15:02

​在之前的文章中已经讲解了很多种用于任务件通信的机制,包括队列、事件组和各种不同类型的信号量。使用这些机制都需要创建一个通信对象

事件和数据不会直接发送到接收任务或接收ISR,而是发送到通信对象(也就是发送到队列、事件组、信号量)。同样,任务和ISR从通信对象接收事件和数据,而不是直接从发送事件或数据的任务或ISR接收事件和数据。

任务通知允许任务与其他任务交互,并与ISR同步,而不需要单独的通信对象。通过使用任务通知,任务或ISR可以直接向接收任务发送事件。

原文链接:FreeRTOS全解析-11.任务通知(Task Notifications)

目录

1.任务通知的优缺点

1.1优点

1.2缺点

2.使用任务通知

2.1简易版

2.2复杂版


1.任务通知的优缺点

1.1优点

1.更快,使用任务通知将事件或数据发送到任务要比使用队列、信号量或事件组更快。

2.更节约内存,使用任务通知将事件或数据发送到任务所需的RAM比使用队列、信号量或事件组执行等效操作所需的RAM少得多。因为通信对象(队列、信号量或事件组),要先创建,才能使用,而启用任务通知功能每个任务只有8字节的RAM的固定开销。

1.2缺点

部分情况无法使用

(1)向ISR发送事件或数据

通信对象可用于将事件和数据从ISR发送到任务,并从任务发送到ISR。任务通知可用于将事件和数据从ISR发送到任务,但是它们不能用于将事件或数据从任务发送到ISR。

(2)有多个接收任务

通信对象可以被任何知道其句柄的任务或ISR访问。任意数量的任务和ISR都可以接收通信对象。任务通知直接发送到接收任务,因此只能由接收通知的任务处理。

(3)缓冲多个数据项

队列是一种通信对象,一次可以保存多个数据项。已发送到队列但尚未从队列接收的数据将在队列对象中进行缓冲。任务通知通过更新接收任务的通知值来向任务发送数据。任务的通知值一次只能保存一个值。

(3)发送到多个任务

事件组是一个通信对象,可用于一次向多个任务发送事件。任务通知直接发送给接收任务,因此只能由接收任务处理。

(3)在阻塞态下等待发送完成

如果一个通信对象暂时处于不能再写入数据或事件的状态(例如,当队列已满时,不能再向队列发送数据),那么尝试写入该对象的任务可以选择进入阻塞态,等待机会去完成写入操作。如果任务试图将任务通知发送给已经有通知挂起的任务,则发送任务不能在阻塞态下等待接收任务重置其通知状态。

2.使用任务通知

要开启任务通知功能,首先需要在FreeRTOSConfig.h中将configUSE_TASK_NOTIFICATIONS设置为1。

当configUSE_TASK_NOTIFICATIONS设置为1时,每个任务都有一个“通知状态”,可以是“Pending”或“Not-Pending”,以及一个“通知值”,这是一个32位无符号整数。当任务收到通知时,其通知状态设置为“Pending”。当任务读取其通知值时,其通知状态设置为“Not-Pending”。任务可以在Blocked状态下等待其通知状态变为“Pending”。

2.1简易版

xTaskNotifyGive() API函数

xTaskNotifyGive()直接向任务发送通知,并增加接收任务的通知值。调用xTaskNotifyGive()将把接收任务的通知状态设置为pending(如果它还没有挂起的话)。

BaseType_t xTaskNotifyGive( TaskHandle_t xTaskToNotify );

ulTaskNotifyTake() API函数

ulTaskNotifyTake()允许任务在Blocked状态下等待其通知值大于零,并在返回之前减少(减去1)或清除任务的通知值。

uint32_t ulTaskNotifyTake( BaseType_t xClearCountOnExit, TickType_t xTicksToWait );

例子1:延迟中断处理

const TickType_t xInterruptFrequency = pdMS_TO_TICKS( 500UL );static void vHandlerTask( void *pvParameters ){  const TickType_t xMaxExpectedBlockTime = xInterruptFrequency + pdMS_TO_TICKS( 10 );  uint32_t ulEventsToProcess;  for( ;; )  {    ulEventsToProcess = ulTaskNotifyTake( pdTRUE, xMaxExpectedBlockTime );    if( ulEventsToProcess != 0 ) {      while( ulEventsToProcess > 0 ) {        vPrintString( "Handler task - Processing event.\r\n" );        ulEventsToProcess--;      }    } else {      //如果运行到这,表示超时时间内,中断没有发生    }  }}static uint32_t ulExampleInterruptHandler( void ){  BaseType_t xHigherPriorityTaskWoken;  xHigherPriorityTaskWoken = pdFALSE;  vTaskNotifyGiveFromISR( xHandlerTask,&xHigherPriorityTaskWoken );  portYIELD_FROM_ISR( xHigherPriorityTaskWoken );}#define mainINTERRUPT_NUMBER 3static void vPeriodicTask( void *pvParameters ){  const TickType_t xDelay500ms = pdMS_TO_TICKS( 500UL );  vTaskDelay( xDelay500ms );  vPrintString( "Periodic task - About to generate an interrupt.\r\n" );  vPortGenerateSimulatedInterrupt( mainINTERRUPT_NUMBER );  vPrintString( "Periodic task - Interrupt generated.\r\n\r\n\r\n" );}

如上是一个利用任务通知来延迟中断处理的例子vPeriodicTask函数模拟周期性中断,在中断前会打印Periodic task - About to generate an interrupt.中断后会打印Periodic task - Interrupt generated.

ulExampleInterruptHandler函数为中断处理函数,但在这个函数中并不真正进行处理,只是采用vTaskNotifyGiveFromISR函数,给出任务通知,vHandlerTask函数,才是真正的处理函数,vHandlerTask函数作为一个任务来运行,调用ulTaskNotifyTake函数进入阻塞态,以等待任务通知的到来。

ulTaskNotifyTake()中xClearCountOnExit参数被设置为pdTRUE,这将导致在ulTaskNotifyTake()返回之前,接收任务的通知值被清除为零。也就是说必须处理所有的任务通知。

例子2:UART发送

外围设备上的一些操作需要较长的时间才能完成。比如高精度ADC转换,以及在UART上传输大数据包。可以通过轮询(重复读取)外设的状态寄存器,以确定操作何时完成。然而,轮询非常浪费CPU,因为它占用CPU,而没有执行任何实质性操作。

当然我们可以利用中断,操作处理任务take信号量进入阻塞,等待发生中断,在其对应中断处理函数中give信号量,来替代轮询。

比如uart发送函数,利用二进制信号量

BaseType_t xUART_Send( xUART *pxUARTInstance, uint8_t *pucDataSource, size_t uxLength ){  BaseType_t xReturn;  /* 确保信号量为0 */  xSemaphoreTake( pxUARTInstance->xTxSemaphore, 0 );  /* 启动传输*/  UART_low_level_send( pxUARTInstance, pucDataSource, uxLength );  /* 等待传输完毕 */  xReturn = xSemaphoreTake( pxUARTInstance->xTxSemaphore, pxUARTInstance->xTxTimeout );  return xReturn;}/*发送完毕中断服务函数*/void xUART_TransmitEndISR( xUART *pxUARTInstance ){  BaseType_t xHigherPriorityTaskWoken = pdFALSE;  /* 清除中断 */  UART_low_level_interrupt_clear( pxUARTInstance );  /* give信号量 */  xSemaphoreGiveFromISR( pxUARTInstance->xTxSemaphore, &xHigherPriorityTaskWoken );  portYIELD_FROM_ISR( xHigherPriorityTaskWoken );}

但是这种方式需要创建信号量才能使用,创建信号量就涉及占用RAM。可以用任务通知代替:

BaseType_t xUART_Send( xUART *pxUARTInstance, uint8_t *pucDataSource, size_t uxLength ){  BaseType_t xReturn;  /* 保存调用该函数的任务句柄. */  pxUARTInstance->xTaskToNotify = xTaskGetCurrentTaskHandle();  /* 确保任务通知为0. */  ulTaskNotifyTake( pdTRUE, 0 );  /*启动传输. */  UART_low_level_send( pxUARTInstance, pucDataSource, uxLength );  /* 阻塞等待发送完成.*/  xReturn = ( BaseType_t ) ulTaskNotifyTake( pdTRUE, pxUARTInstance->xTxTimeout );  return xReturn;}/*-----------------------------------------------------------*//* 发送完成中断服务函数. */void xUART_TransmitEndISR( xUART *pxUARTInstance ){  BaseType_t xHigherPriorityTaskWoken = pdFALSE;  /* 清除中断. */  UART_low_level_interrupt_clear( pxUARTInstance );  /* 发送任务通知. */  vTaskNotifyGiveFromISR( pxUARTInstance->xTaskToNotify, &xHigherPriorityTaskWoken );  portYIELD_FROM_ISR( xHigherPriorityTaskWoken );}

2.2复杂版

xTaskNotify()API函数

xTaskNotify()比xTaskNotifyGive()更灵活、更强大,由于这种额外的灵活性和强大功能,使用起来也稍微复杂一些。

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 );

eAction是一个枚举型变量

eAction的值

作用

eNoAction

接收任务的通知状态设置为挂起,而不更新其通知值。未使用xTaskNotify() ulValue参数。替代二进制信号量

eSetBits

接收任务的通知值与xTaskNotify() ulValue参数中传递的值按位或匹配。例如,如果ulValue设置为0x01,则接收任务的通知值将设置0位。另一个例子,如果ulValue是0x06(二进制0110),那么接收任务的通知值将设置第1位和第2位。替代事件组

eIncrement

接收任务的通知值递增。未使用xTaskNotify() ulValue参数。替代计数信号量

eSetValueWithoutOverwrite

如果在调用xTaskNotify()之前接收任务有一个挂起的通知,则不采取任何操作,xTaskNotify()将返回pdFAIL。如果在调用xTaskNotify()之前接收任务没有挂起的通知,则将接收任务的通知值设置为xTaskNotify() ulValue参数中传递的值。

eSetValueWithOverwrite

接收任务的通知值被设置为xTaskNotify() ulValue参数中传递的值,而不管在调用xTaskNotify()之前接收任务是否有挂起的通知。

xTaskNotifyWait()API函数

xTaskNotifyWait()是ulTaskNotifyTake()的一个更强大的版本。它允许任务使用可选的超时等待,以便调用任务的通知状态变为挂起(如果它尚未挂起)。xTaskNotifyWait()提供了在进入函数和退出函数时在调用任务的通知值中清除比特的选项。

BaseType_t xTaskNotifyWait( uint32_t ulBitsToClearOnEntry,                          uint32_t ulBitsToClearOnExit,                          uint32_t *pulNotificationValue,                          TickType_t xTicksToWait );
参数作用

ulBitsToClearOnEntry

如果调用任务在调用xTaskNotifyWait()之前没有通知挂起,那么在进入该函数时,在任务的通知值中清除在ulBitsToClearOnEntry中设置的任何位。例如,如果ulBitsToClearOnEntry是0x01,那么任务通知值的第0位将被清除。如果将ulBitsToClearOnEntry设置为0xffff (ULONG_MAX)将清除任务通知值中的所有位。

ulBitsToClearOnExit

如果调用任务因为收到通知而退出xTaskNotifyWait(),或者因为在调用xTaskNotifyWait()时已经有通知挂起,那么在ulBitsToClearOnExit中设置的任何位将在任务退出xTaskNotifyWait()函数之前在任务的通知值中被清除。当任务的通知值保存在*pulNotificationValue(参见下面的pulNotificationValue的描述)中后,这些位将被清除。例如,如果ulBitsToClearOnExit是0x03,那么任务的通知值的第0位和第1位将在此之前被清除将ulBitsToClearOnExit设置为0xfffffff (ULONG_MAX)将清除任务通知值中的所有位。

pulNotificationValue

用于传递出任务的通知值(被清除前的值)。pulNotificationValue是一个可选参数,如果不需要,可以设置为NULL。

xTicksToWait

调用任务保持阻塞态以等待其通知状态变为挂起的最长时间。

返回值

pdTRUE成功接到通知,pdFALSE未接收到通知

例子:ADC

void vADCTask( void *pvParameters ){  uint32_t ulADCValue;  BaseType_t xResult;  const TickType_t xADCConversionFrequency = pdMS_TO_TICKS( 50 );  for( ;; ) {    xResult = xTaskNotifyWait(0,0,&ulADCValue,xADCConversionFrequency * 2 );    if( xResult == pdPASS ) {      ProcessADCResult( ulADCValue );    } else {    }  }}void ADC_ConversionEndISR( xADC *pxADCInstance ){  uint32_t ulConversionResult;  BaseType_t xHigherPriorityTaskWoken = pdFALSE, xResult;  ulConversionResult = ADC_low_level_read( pxADCInstance );  xResult = xTaskNotifyFromISR( xADCTaskToNotify,ulConversionResult, eSetValueWithoutOverwrite,&xHigherPriorityTaskWoken );  configASSERT( xResult == pdPASS );  portYIELD_FROM_ISR( xHigherPriorityTaskWoken );}

ADC_ConversionEndISR函数是ADC转换结束的ISR,vADCTask函数是等待ADC值的任务。

ADC_low_level_read是读取ADC值函数,读取后调用xTaskNotifyFromISR函数发送任务通知,ulValue参数为ulConversionResult,即ADC转换的结果,eAction参数为eSetValueWithoutOverwrite,如果在调用xTaskNotify()之前接收任务有一个挂起的通知,则不采取任何操作,xTaskNotify()将返回pdFAIL。如果在调用xTaskNotify()之前接收任务没有挂起的通知,则将接收任务的通知值设置为xTaskNotify() ulValue参数中传递的值。

可见xTaskNotify()API函数和xTaskNotifyWait()API函数组合实现了传值的功能,这是简易版做不到的,不过大多数情况下,使用简易版就够了

往期精彩:
STM32F4+FreeRTOS+LVGL实现快速开发(缝合怪)

嵌入式C语言几个重点(const、static、voliatile、位运算)

嵌入式Linux驱动学习-5.驱动的分层分离思想

从Linux内核中学习高级C语言宏技巧

嵌入式Linux学习经典书籍-学完你就是大佬

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

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

相关文章

2023软考中级《软件设计师》(备考冲刺版) | 操作系统

目录 1.操作体统相关概念 1.1 操作系统的功能 1.2 特殊的操作系统 2.进程管理 2.1进程的概念 2.1.1 线程的概念 2.1.2 进程的状态 2.2 进程调度 2.2.1 PV操作的概念 2.2.2 信号量和PV操作 2.2.3 前趋图与PV操作 3.存储管理 3.1 页式存储 3.2 段式存储 3.3 段页式…

智慧安防小区管控系统解决方案(ppt可编辑)

本资料来源公开网络,仅供个人学习,请勿商用,如有侵权请联系删除 智慧安防小区-建设思路及目标 智慧安防小区管控子系统是,按照“数据向上集中、服务向下延伸”的思路,对相关要素进行重点采集,实现社区态势…

【JAVAEE】网络原理之网络通信基础

目录 1. 💋IP地址 1.1 🍟IP地址的格式 1.2 🎁特殊IP地址 2. ✨端口号 2.1 🎃端口号的格式 3. 😘网络协议 3.1 🎨为什么需要网络协议? 3.2 💛网络协议的概念与组成 3.3 &am…

答题积分小程序云开发实战-界面交互篇:首页页面布局样式与逻辑交互开发

微信小程序云开发实战-答题积分赛小程序 界面交互篇:首页页面布局样式与逻辑交互开发 首页效果图 布局思路 5行布局,即5个块级元素,轮播图、通告栏、个人信息、功能区、版权。

将服务器select模型设置为非阻塞,处理更多业务

timeval结构体在头文件为sys/time.h中,定义如下: struct timeval {long tv_sec; /* seconds */long tv_usec; /* and microseconds */ }; 其中tv_sec是秒,tv_usec是微秒(microsecond )&#xff0…

[单片机框架][bsp层][cx32l003][bsp_tim] Baes TIM 基础定时器配置和使用

文章目录 一、基础定时器介绍二、功能描述(1) Buzzer 功能 三、示例代码(PWM) 一、基础定时器介绍 基础定时器 Base Timer 包含两个定时器 TIM10/11。TIM10/11 功能完全相同。TIM10/11 是同步定时/计数器,可以作为 16/32 位自动重装载功能的定时/计数器&#xff0c…

VS2022配置GDAL

GDAL(Geospatial Data Abstraction Library)是一个用于处理地理空间数据的开源库。它提供了一组功能丰富的API,用于读取、写入、转换和处理各种地理空间数据格式,包括栅格数据(如卫星图像、数字高程模型)和…

Jupyter创建Anaconda多个虚拟环境教程

这里写目录标题 1.1界面化创建虚拟环境1.2命令行创建虚拟环境2.查看是否创建成功3.激活虚拟环境pylessonppt4.更改工作目录5.删除6.查看是否删除成功 1.1界面化创建虚拟环境 1.2命令行创建虚拟环境 conda create -n myenv——name pythonx.xmyenv-name:自己定义的环境名称 pyt…

fastjson反序列化漏洞复现

fastjson反序列化漏洞复现 一.影响版本: Fastjson<1.2.24二.实验过程图三.实验步骤四&#xff0c;实验结果以及参考链接 一.影响版本: Fastjson<1.2.24 二.实验过程图 (踩坑) rmijndi环境&#xff1a;java.sql.SQLException: JdbcRowSet (连接) JNDI 无法连接 2、ldapjn…

上海无纺布制造商【盈兹】申请纳斯达克IPO上市,募资1100万美元

来源&#xff1a;猛兽财经 作者&#xff1a;猛兽财经 猛兽财经获悉&#xff0c;来自上海的无纺布制造商【盈兹】&#xff0c;近期已向美国证券交易委员会&#xff08;SEC&#xff09;提交招股书&#xff0c;申请在纳斯达克IPO上市&#xff0c;股票代码为&#xff08;ETZ&#…

Invalid bound statement (not found)的原因以及解决方法

相信我们在学习Mybatis的时候都出现过 Invalid bound statement (not found) 这个错误&#xff0c;一般由以下几种可能导致这个错误 一&#xff1a;mapper方法名 和 mapper.xml id名不对应 例如&#xff1a; mapper&#xff1a; 对应的mapper.xml 这里建议小伙伴们下载一个插…

Linux中的YUM源仓库和NFS文件共享服务

这里写目录标题 一 、YUM仓库源的介绍和相关信息1.1yum相关介绍1.2 Linux系统各家厂商用的安装源1.3 yum下载方式 二 、 yum 仓库源的三种搭建2.1yum 配置本地源2.2创建ftp源2.3 配置http源2.4 配置yum在线源 三 、NFS的简介3.1 什么是NFS3.2 linux中要使用NFS需要下载的软件包…

User Diverse Preference Modeling by Multimodal Attentive Metric Learning

BACKGROUND 现有模型通常采用一个固定向量去表示用户偏好&#xff0c;在假设——特征向量每一个维度都代表了用户的一种特性或者一个方面&#xff0c;这种方式似乎不妥&#xff0c;因为用户对于不同物品的偏好是不一样的&#xff0c;例如因演员喜欢一部电影&#xff0c;而因特…

C++中的vector容器

文章目录 vector的介绍vector的使用vector的定义vector初始化vector iterator的使用vector空间增长问题vector增删改查vector迭代器失效问题 vector的介绍 vector是封装动态数组的顺序容器。   就像数组一样&#xff0c;vector也采用的连续存储空间来存储元素。这也就意味着我…

Java核心技术 卷1-总结-15

自己实现的hashCode方法应该与equals方法兼容 Java核心技术 卷1-总结-15 视图与包装器子范围不可修改的视图同步视图受查视图 并发线程状态新创建线程可运行线程被阻塞线程和等待线程被终止的线程 视图与包装器 子范围 可以为很多集合建立子范围&#xff08;subrange&#x…

机器学习——朴素贝叶斯

目录 一、贝叶斯 1.什么是贝叶斯 3.贝叶斯下的朴素贝叶斯 二、朴素贝叶斯 1.高斯朴素贝叶斯 2.伯努利朴素贝叶斯 3.多项式朴素贝叶斯 前言 在所有的机器学习分类算法中&#xff0c;朴素贝叶斯和其他绝大多数的分类算法都不同。对于大多数的分类算法&#xff0c;比如决策…

【业务数据分析】—— 用户留存分析(以挖掘Aha时刻为例)

目录 一、用户留存是什么 二、为什么要考虑用户留存 1、为什么要考虑用户留存&#xff1f; 2、影响用户留存的可能因素 3、用户留存的3个阶段 三、怎么进行用户留存分析(挖掘Aha时刻) 1、Aha时刻 2、Aha时刻的作用 3、挖掘Aha时刻 一、用户留存是什么 在互联网行业中…

Three——二、加强对三维空间的认识

Three——二、加强对三维空间的认识 接上个例子我们接着往下看 辅助观察坐标系 THREE.AxesHelper()的参数表示坐标系坐标轴线段尺寸大小&#xff0c;你可以根据需要改变尺寸。 使用方法&#xff1a; // AxesHelper&#xff1a;辅助观察的坐标系 const axesHelper new THRE…

Jetson nano B01学习笔记 -- 系统环境配置以及ROS安装

文章目录 一、Jetson nano 简介二、 系统环境配置1、系统镜像烧录2、CUDA环境配置 三、 ROS安装和环境配置总结 一、Jetson nano 简介 Jetson Nano是一款体积小巧、功能强大的人工智能嵌入式开发板&#xff0c;于2019年3月由英伟达推出。它预装Ubuntu 18.04LTS系统&#xff0c;…

有什么好用的远程工具吗

沟通在任何类型的工作中都扮演着重要的角色。但当谈到远程工作时&#xff0c;这一点就更为重要。因此&#xff0c;您的组织必须找到可以让您的团队保持一致的工具。 在某些方面&#xff0c;项目管理扮演着类似的角色。 您会注意到&#xff0c;下面的大多数工具都会直接影响您的…