FreeRTOS系列第14篇---FreeRTOS任务通知
注:本文介绍任务通知的基础知识,具体源代码分析见《FreeRTOS高级篇8---FreeRTOS任务通知分析》
每一个RTOS任务都有一个32位的通知值,任务创建时,这个值被初始化为0。RTOS任务通知相当于直接向任务发送一个事件,接收到通知的任务能够解除堵塞状态,前提是这个堵塞事件是因等待通知而引起的。
发送通知的同一时候。也能够可选的改变接收任务的通知值。
能够通过下列方法向接收任务更新通知:
- 不覆盖接收任务的通知值
- 覆盖接收任务的通知值
- 设置接收任务通知值的某些位
- 添加接收任务的通知值
相对于用前必须分别创建队列、二进制信号量、计数信号量或事件组的情况。使用任务通知显然更灵活。更好的是,相比于使用信号量解除任务堵塞。使用任务通知能够快45%、使用更少的RAM(使用GCC编译器,-o2优化级别)。
使用API函数xTaskNotify()和xTaskNotifyGive()(中断保护等价函数为xTaskNotifyFromISR()和vTaskNotifyGiveFromISR())发送通知,在接收RTOS任务调用API函数xTaskNotifyWait()或ulTaskNotifyTake()之前。这个通知都被保持着。假设接收RTOS任务已经由于等待通知而进入堵塞状态。则接收到通知后任务解除堵塞并清除通知。
RTOS任务通知功能默认是使能的。能够通过在文件FreeRTOSConfig.h中设置宏configUSE_TASK_NOTIFICATIONS为0来禁止这个功能。禁止后每一个任务节省8字节内存。
尽管RTOS任务通知速度更快而且占用内存更少。但它也有一些限制:
- 仅仅能有一个任务接收通知事件。
- 接收通知的任务能够由于等待通知而进入堵塞状态,可是发送通知的任务即便不能马上完毕通知发送也不能进入堵塞状态。
1.发送通知-方法1
1.1函数描写叙述
BaseType_t xTaskNotify( TaskHandle_txTaskToNotify,
uint32_t ulValue,
eNotifyAction eAction);
向指定任务发送指定的通知值。假设打算使用RTOS任务通知实现轻量级的二进制或计数信号量,推荐使用API函数xTaskNotifyGive()来取代本函数。
此函数不能够在中断服务例程中调用,中断保护等价函数为xTaskNotifyFromISR()。
1.2參数描写叙述
- xTaskToNotify:被通知的任务句柄。
- ulValue:通知更新值
- eAction:枚举类型,指明更新通知值的方法
枚举变量成员以及作用例如以下表所看到的。
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="" />
1.3返回值
參数eAction为eSetValueWithoutOverwrite时,假设被通知任务还没取走上一个通知。又接收到了一个通知,则这次通知值未能更新并返回pdFALSE,否则返回pdPASS。
2.发送通知-方法2
2.1函数描写叙述
BaseType_t xTaskNotifyGive(TaskHandle_t xTaskToNotify );
事实上这是一个宏。本质上相当于xTaskNotify( ( xTaskToNotify ), ( 0 ), eIncrement )。能够使用该API函数取代二进制或计数信号量,但速度更快。在这样的情况下。应该使用API函数ulTaskNotifyTake()来等待通知。而不应该使用API函数xTaskNotifyWait()。
此函数不能够在中断服务例程中调用,中断保护等价函数为vTaskNotifyGiveFromISR()。
2.2參数描写叙述
- xTaskToNotify:被通知的任务句柄。
2.3使用方法举例
staticvoid prvTask1( void *pvParameters );
staticvoid prvTask2( void *pvParameters ); /* 保存任务句柄 */
staticTaskHandle_t xTask1 = NULL, xTask2 = NULL; /* 创建两个任务,它们之间重复发送通知,启动RTOS调度器*/
voidmain( void )
{
xTaskCreate( prvTask1, "Task1",200, NULL, tskIDLE_PRIORITY, &xTask1 );
xTaskCreate( prvTask2, "Task2",200, NULL, tskIDLE_PRIORITY, &xTask2 );
vTaskStartScheduler();
}
/*-----------------------------------------------------------*/ staticvoid prvTask1( void *pvParameters )
{
for( ;; )
{
/*向prvTask2(),发送通知,使其解除堵塞状态 */
xTaskNotifyGive( xTask2 ); /* 等待prvTask2()的通知,进入堵塞 */
ulTaskNotifyTake( pdTRUE, portMAX_DELAY);
}
}
/*-----------------------------------------------------------*/ staticvoid prvTask2( void *pvParameters )
{
for( ;; )
{
/* 等待prvTask1()的通知。进入堵塞 */
ulTaskNotifyTake( pdTRUE, portMAX_DELAY); /*向prvTask1(),发送通知,使其解除堵塞状态 */
xTaskNotifyGive( xTask1 );
}
}
3.获取通知
3.1函数描写叙述
uint32_t ulTaskNotifyTake( BaseType_txClearCountOnExit,
TickType_txTicksToWait );
ulTaskNotifyTake()是专门为使用更轻量级更快的方法来取代二进制或计数信号量而量身打造的。
FreeRTOS获取信号量的API函数为xSemaphoreTake(),能够使用ulTaskNotifyTake()函数等价取代。
当一个任务使用通知值来实现二进制或计数信号量时,其他任务或者中断要使用API函数xTaskNotifyGive()或者使用參数eAction为eIncrement的API函数xTaskNotify()。推荐使用xTaskNotifyGive()函数(事实上是个宏,我们这里把它看作一个API函数)。另外须要注意的是,假设在中断中使用。要使用它们的中断保护等价函数:vTaskNotifyGiveFromISR()和xTaskNotifyFromISR()。
API函数xTaskNotifyTake()有两种方法处理任务的通知值。一种方法是在函数退出时将通知值清零,这样的方法适用于实现二进制信号量;第二种方法是在函数退出时将通知值减1。这样的方法适用于实现计数信号量。
假设RTOS任务的通知值为0,使用xTaskNotifyTake()能够可选的使任务进入堵塞状态,直到该任务的通知值不为0。进入堵塞的任务不消耗CPU时间。
3.2參数描写叙述
- xClearCountOnExit:假设该參数设置为pdFALSE,则API函数xTaskNotifyTake()退出前,将任务的通知值减1;假设该參数设置为pdTRUE。则API函数xTaskNotifyTake()退出前,将任务通知值清零。
- xTicksToWait:因等待通知而进入堵塞状态的最大时间。时间单位为系统节拍周期。
宏pdMS_TO_TICKS用于将指定的毫秒时间转化为对应的系统节拍数。
3.3返回值
返回任务的当前通知值。为0或者为调用API函数xTaskNotifyTake()之前的通知值减1。
3.4使用方法举例
/* 中断处理程序。*/
voidvANInterruptHandler( void )
{
BaseType_txHigherPriorityTaskWoken; prvClearInterruptSource(); /* xHigherPriorityTaskWoken必须被初始化为pdFALSE。假设调用vTaskNotifyGiveFromISR()会解除vHandlingTask任务的堵塞状态,而且vHandlingTask任务的优先级高于当前处于执行状态的任务,则xHigherPriorityTaskWoken将会自己主动被设置为pdTRUE。*/
xHigherPriorityTaskWoken = pdFALSE; /*向一个任务发送通知,xHandlingTask是该任务的句柄。 */
vTaskNotifyGiveFromISR( xHandlingTask,&xHigherPriorityTaskWoken ); /* 假设xHigherPriorityTaskWoken为pdTRUE。则强制上下文切换。这个宏的实现取决于移植层。可能会调用portEND_SWITCHING_ISR */
portYIELD_FROM_ISR(xHigherPriorityTaskWoken );
}
/*---------------------------------------------------------------------------------------------------*/ /* 一个由于等待通知而堵塞的任务。 */
voidvHandlingTask( void *pvParameters )
{
BaseType_txEvent; for( ;; )
{
/*等待通知。无限期堵塞。 參数pdTRUE表示函数退出前会清零通知值。这显然是用于替代二进制信号量的使用方法。 须要注意的是,真实的程序一般不会无限期堵塞。*/
ulTaskNotifyTake( pdTRUE, portMAX_DELAY); /* 当处理全然部事件后,仍然等待下一个通知*/
do
{
xEvent = xQueryPeripheral(); if( xEvent != NO_MORE_EVENTS )
{
vProcessPeripheralEvent( xEvent);
} } while( xEvent != NO_MORE_EVENTS );
}
}
4.等待通知
4.1函数描写叙述
BaseType_t xTaskNotifyWait( uint32_tulBitsToClearOnEntry,
uint32_tulBitsToClearOnExit,
uint32_t*pulNotificationValue,
TickType_txTicksToWait );
假设打算使用RTOS任务通知实现轻量级的二进制或计数信号量,推荐使用API函数ulTaskNotifyTake()来取代本函数。
4.2參数描写叙述
- ulBitsToClearOnEntry:在使用通知之前。先将任务的通知值与參数ulBitsToClearOnEntry的按位取反值按位与操作。
设置參数ulBitsToClearOnEntry为0xFFFFFFFF(ULONG_MAX),表示清零任务通知值。
- ulBitsToClearOnExit:在函数xTaskNotifyWait()退出前,将任务的通知值与參数ulBitsToClearOnExit的按位取反值按位与操作。
设置參数ulBitsToClearOnExit为0xFFFFFFFF(ULONG_MAX),表示清零任务通知值。
- pulNotificationValue:用于向外回传任务的通知值。这个通知值在參数ulBitsToClearOnExit起作用前将通知值复制到*pulNotificationValue中。
假设不须要返回任务的通知值。这里设置成NULL。
- xTicksToWait:因等待通知而进入堵塞状态的最大时间。
时间单位为系统节拍周期。
宏pdMS_TO_TICKS用于将指定的毫秒时间转化为对应的系统节拍数。
4.3返回值
假设接收到通知,返回pdTRUE,假设API函数xTaskNotifyWait()等待超时。返回pdFALSE。
4.4使用方法举例
/*这个任务使用任务通知值的位来传递不同的事件,这在某些情况下能够取代事件组。*/
voidvAnEventProcessingTask( void *pvParameters )
{
uint32_tulNotifiedValue; for( ;; )
{
/*等待通知,无限期堵塞(没有超时,所以不用检查函数返回值)。其他任务或者中断设置的通知值中的不同位表示不同的事件。參数0x00表示使用通知前不清除任务的通知值位,參数ULONG_MAX 表示函数xTaskNotifyWait()退出前将任务通知值设置为0*/
xTaskNotifyWait( 0x00, ULONG_MAX,&ulNotifiedValue, portMAX_DELAY ); /*依据通知值处理事件*/
if( ( ulNotifiedValue & 0x01 ) != 0)
{
prvProcessBit0Event();
} if( ( ulNotifiedValue & 0x02 ) != 0)
{
prvProcessBit1Event();
} if( ( ulNotifiedValue & 0x04 ) != 0)
{
prvProcessBit2Event();
} /* ……*/
}
}
5.任务通知并查询
5.1函数描写叙述
BaseType_t xTaskNotifyAndQuery(TaskHandle_t xTaskToNotify,
uint32_tulValue,
eNotifyActioneAction,
uint32_t*pulPreviousNotifyValue );
此函数与任务通知API函数xTaskNotify()很像。仅仅只是此函数具有一个附加參数,用来回传任务当前的通知值,然后依据參数ulValue和eAction更新任务的通知值。
此函数不能在中断服务例程中使用,在中断服务例程中使用xTaskNotifyAndQueryFromISR()函数。
5.2參数描写叙述
- xTaskToNotify:被通知的任务句柄。
- ulValue:通知更新值
- eAction:枚举类型,指明更新通知值的方法,枚举变量成员以及作用见xTaskNotify()一节。
- pulPreviousNotifyValue:回传未被更新的任务通知值。
假设不须要回传未被更新的任务通知值,这里设置为NULL,这样就等价于调用xTaskNotify()函数。
5.3返回值
參数eAction为eSetValueWithoutOverwrite时。假设被通知任务还没取走上一个通知。又接收到了一个通知,则这次通知值未能更新并返回pdFALSE,否则返回pdPASS。
FreeRTOS系列第14篇---FreeRTOS任务通知的更多相关文章
- FreeRTOS系列第2篇---FreeRTOS入门指南【转】
转自:http://blog.csdn.net/zhzht19861011/article/details/49819309 版权声明:本文为博主原创文章,未经博主允许不得转载.联系邮箱:zhzhch ...
- FreeRTOS系列第17篇---FreeRTOS队列
本文介绍队列的基本知识,具体源代码分析见<FreeRTOS高级篇5---FreeRTOS队列分析> 1.FreeRTOS队列 队列是基本的任务间通讯方式.能够在任务与任务间.中断和任务间传 ...
- FreeRTOS系列第20篇---FreeRTOS信号量API函数
FreeRTOS的信号量包括二进制信号量.计数信号量.相互排斥信号量(以后简称相互排斥量)和递归相互排斥信号量(以后简称递归相互排斥量).我们能够把相互排斥量和递归相互排斥量看成特殊的信号量. 信号量 ...
- FreeRTOS系列第13篇---FreeRTOS内核控制
内核控制的一些功能须要移植层提供,为了方便移植.这些API函数用宏来实现,比方上下文切换.进入和退出临界区.禁止和使能可屏蔽中断.内核控制函数还包含启动和停止调度器.挂起和恢复调度器以及用于低功耗模式 ...
- Mysql高手系列 - 第14篇:详解事务
这是Mysql系列第14篇. 环境:mysql5.7.25,cmd命令中进行演示. 开发过程中,会经常用到数据库事务,所以本章非常重要. 本篇内容 什么是事务,它有什么用? 事务的几个特性 事务常见操 ...
- spring 5.x 系列第14篇 —— 整合RabbitMQ (代码配置方式)
源码Gitub地址:https://github.com/heibaiying/spring-samples-for-all 一.说明 1.1 项目结构说明 本用例关于rabbitmq的整合提供简单消 ...
- java高并发系列 - 第14天:JUC中的LockSupport工具类,必备技能
这是java高并发系列第14篇文章. 本文主要内容: 讲解3种让线程等待和唤醒的方法,每种方法配合具体的示例 介绍LockSupport主要用法 对比3种方式,了解他们之间的区别 LockSuppor ...
- ABP(现代ASP.NET样板开发框架)系列之14、ABP领域层——领域事件(Domain events)
点这里进入ABP系列文章总目录 基于DDD的现代ASP.NET开发框架--ABP系列之14.ABP领域层——领域事件(Domain events) ABP是“ASP.NET Boilerplate P ...
- 一步一步学Silverlight 2系列(14):数据与通信之WCF
一步一步学Silverlight 2系列(14):数据与通信之WCF 概述 Silverlight 2 Beta 1版本发布了,无论从Runtime还是Tools都给我们带来了很多的惊喜,如支持框 ...
随机推荐
- docker centos7 配置和宿主机同网段IP
docker centos7 配置和宿主机同网段IP 1.安装brctl 命令 # yum -y install bridge-utils 2.编辑网卡配置文件 # vi ifcfg-eno16777 ...
- mysql高效率随机获取n条数据写法
今天做项目遇到这个问题,本来想用mysql自带的随机函数来实现,但是想到这样做功能是实现了,但是效率真的好差!一下子想不到好的方法,就去网上找了一下,记录下来,好好研究学习一下. ID连续的情况下(注 ...
- 小甲鱼python疑难点
1.python生成器 2.while 1: num = input('请输入一个整数(输入Q结束程序):') if num != 'Q': num = int(num) print('十进制 -&g ...
- Python 面向对象 组合-多态与多态性-封装-property
面向对象-组合 1.什么是组合 组合指的是某一个对象拥有一个属性,该属性的值是另外一个类的对象 class Foo: xxx = 111 class Bar: yyy = 222 obj = Foo( ...
- java ssm框架 mapper文件里的#符号和$符号的区别
Java SSM框架里面,Mapper.xml文件 (一)#符号生成的sql语句是作为传参的 <!-- 获得数据列表(包括课程相关信息) --> <select id="G ...
- 图解使用IDEA创建第一个Java程序HelloWorld
前几次给大家分享了怎么在自己的电脑上配置 java 环境,准备工作做好了,我们就要开始我们真正的编码学习了.下面介绍使用 IDEA 创建我们的第一个 HelloWorld 程序. 1.打开 IDEA, ...
- Leetcode 147.对链表进行排序
对链表进行插入排序 对链表进行插入排序. 插入排序算法: 插入排序是迭代的,每次只移动一个元素,直到所有元素可以形成一个有序的输出列表. 每次迭代中,插入排序只从输入数据中移除一个待排序的元素,找到它 ...
- 数据结构-B+树
B+ 树是一种树数据结构,是一个n叉排序树,每个节点通常有多个孩子,一棵B+树包含根节点.内部节点和叶子节点.根节点可能是一个叶子节点,也可能是一个包含两个或两个以上孩子节点的节点. B+ 树通常用于 ...
- msp430入门学习20
msp430的USART模式 msp430入门学习
- KMP算法 C#实现 字符串查找简单实现
KMP算法 的C#实现,初级版本 static void Main(string[] args) { #region 随机字符 StringBuilder sb = new StringBuilder ...