FreeRTOS任务通知

一、任务通知简介

         FreeRTOS 从 V8.2.0 版本开始提供任务通知这个功能,每个任务都有一个 32 位的通知值,在大多数情况下任务通知可以代替二值信号量,计数信号量 ,事件组,以及长度为1的队列。对于之前学习的二值信号量,计数信号量和事件组都需要我们创建队列,使用任务通知则不需要创建队列,使用起来更加的方便,使用任务 通知比通过信号量等 ICP 通信方式解除阻塞的任务要快    45%,并且更加省 RAM 内存空间 ,使用之前记得将FreeRTOSConfig.h 中的宏定义 configUSE_TASK_NOTIFICATIONS 设置为 1。

         FreeRTOS 提供以下几种方式发送通知给任务 :

        1、发送通知给任务,如果有通知没有读取,不覆盖通知值。

        2、发送通知给任务,不管读与未读都直接覆盖通知值。   

        3、 发送通知给任务,设置通知值的一个或者多个位,可以当做事件组来使用。

        4、发送通知给任务,递增通知值,可以当做计数信号量使用。 通过对以上任务通知方式的合理使用,可以在一定场合下替代 FreeRTOS 的信号量, 队列、事件组等。

       FreeRTOS的任务也有所弊端,只能有一个任务接收通知消息,因为发送通知消息的任务要指定是哪个接收任务,因此一个发送通知消息的任务只能发送通知给一个指定的接收通知消息的任务,无法发送给其它的任务。另外接收通知的任务可以被阻塞但是发送通知的任务,在任何情况下都不会因发送失败而进入阻塞。

二、任务通知的运作机制

        任务通知是任务中附带的资源,在创建任务时已经就被初始化,而对于消息队列和信号量来说,我们在使用队列和信号量之前必须先对队列和信号量进行创建,其目的是为了创建队列数据结构,而任务通知就免去了这一创建工作,任务创建完毕之后,任务通知就可以直接用。 FreeRTOS 的每个任务都有一个 32 位的通知值,任务控制块中的成员变量 ulNotifiedValue 就是这个通知值。获取任务通知只能在任务中等待,不能在中断中等待任务通知。如果任务在等待的通知暂时无效,任务会根据用户指定的阻塞时间进入阻塞状态。我们可以把等待任务通知的任务看作嗷嗷待哺的大学生,把发送通知的任务看作外卖员(中断和任务都可以发送通知),当饿的到头昏的大学生拿到外卖员送的外卖,顿时就不饿了(前提是外卖不被偷!),也就相当于任务接收到通知后结束阻塞状态,进入就绪态。

三、常用API函数

1、xTaskGenericNotify()

        xTaskGenericNotify()函数是一个通用的任务通知发送函数,在任务中发送通知的 API 函 数 ,如xTaskNotifyGive() 、 xTaskNotify() ,xTaskNotifyAndQuery() ,都是以 xTaskGenericNotify()为原型的,只不过指定的发送方式不同而已。

2、xTaskNotifyGive()

        该函数运用于任务当中,不能再中断中使用。该函数向任务发送一个通知,并将对方的任务通知加1。该函数可以作为二值信号量和计数信号量的一种轻量级实现,速度更快,在这种情况下对象任务在等待任务 通 知 的 时 候 应 该 是 使 用 函 数 ulTaskNotifyTake() 而不是xTaskNotifyWait()。  

 static void prvTask1( void *pvParameters );
 static void prvTask2( void *pvParameters );
 
/*定义任务句柄 */
 static TaskHandle_t xTask1 = NULL, xTask2 = NULL;
 
 /* 主函数:创建两个任务,然后开始任务调度 */
 void main( void )
 {
 xTaskCreate(prvTask1, "Task1", 200, NULL, tskIDLE_PRIORITY, &xTask1);
 xTaskCreate(prvTask2, "Task2", 200, NULL, tskIDLE_PRIORITY, &xTask2);
 vTaskStartScheduler();
 }
 /*-----------------------------------------------------------*/
 
 static void prvTask1( void *pvParameters )
 {
 for ( ;; ) {
 /* 向 prvTask2()发送一个任务通知,让其退出阻塞状态 */
 xTaskNotifyGive( xTask2 );
 
 /* 阻塞在 prvTask2()的任务通知上
 如果没有收到通知,则一直等待*/
 ulTaskNotifyTake( pdTRUE, portMAX_DELAY );
 }
 }
 /*-----------------------------------------------------------*/
 
 static void prvTask2( void *pvParameters )
 {
 for ( ;; ) {
 /* 阻塞在 prvTask1()的任务通知上
 如果没有收到通知,则一直等待*/
 ulTaskNotifyTake( pdTRUE, portMAX_DELAY );
 
 /* 向 prvTask1()发送一个任务通知,让其退出阻塞状态 */
 xTaskNotifyGive( xTask1 );
 }
}

3、vTaskNotifyGiveFromISR() 

        xTaskNotifyGive()的中断版,运用于中断程序当中。每次调用函数时都会增加任务的通知量,任务通过接收函数的返回值是否大于0来判断是否获取了通知,如果大于零则表示获取通知而后将任务通知量初始化为零。

         

 static TaskHandle_t xTaskToNotify = NULL;
 
 /* 外设驱动的数据传输函数 */
 void StartTransmission( uint8_t *pcData, size_t xDataLength )
 {
 /* 在这个时候,xTaskToNotify 应为 NULL,因为发送并没有进行。
 如果有必要,对外设的访问可以用互斥量来保护*/
 configASSERT( xTaskToNotify == NULL );
 
 /* 获取调用函数 StartTransmission()的任务的句柄 */
 xTaskToNotify = xTaskGetCurrentTaskHandle();
 
 /* 开始传输,当数据传输完成时产生一个中断 */
 vStartTransmit( pcData, xDatalength );
 }
 /*-----------------------------------------------------------*/
 /* 数据传输完成中断 */
 void vTransmitEndISR( void )
 {
 BaseType_t xHigherPriorityTaskWoken = pdFALSE;
 
 /* 这个时候不应该为 NULL,因为数据传输已经开始 */
 configASSERT( xTaskToNotify != NULL );
 
 /* 通知任务传输已经完成 */
 vTaskNotifyGiveFromISR( xTaskToNotify, &xHigherPriorityTaskWoken );
 
 /* 传输已经完成,所以没有任务需要通知 */
 xTaskToNotify = NULL;
 
 /* 如果为 pdTRUE,则进行一次上下文切换 */
 portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
 }
 /*-----------------------------------------------------------*/
 /* 任务:启动数据传输,然后进入阻塞态,直到数据传输完成 */
 void vAFunctionCalledFromATask( uint8_t ucDataToTransmit,
 size_t xDataLength )
 {
 uint32_t ulNotificationValue;
 const TickType_t xMaxBlockTime = pdMS_TO_TICKS( 200 );
 
 /* 调用上面的函数 StartTransmission()启动传输 */
 StartTransmission( ucDataToTransmit, xDataLength );
 
 /* 等待传输完成 */
 ulNotificationValue = ulTaskNotifyTake( pdFALSE, xMaxBlockTime );
 
 /* 当传输完成时,会产生一个中断
在中断服务函数中调用 vTaskNotifyGiveFromISR()向启动数据
 传输的任务发送一个任务通知,并将对象任务的任务通知值加 1
 任务通知值在任务创建的时候是初始化为 0 的,当接收到任务后就变成 1 */
 if ( ulNotificationValue == 1 ) {
 /* 传输按预期完成 */
 } else {
 /* 调用函数 ulTaskNotifyTake()超时 */
 }
 }

4.xTaskNotify() 

        等效于事件组的使用,若用来类似于二值信号量,计数信号量可用前两个介绍的函数。eAction的取值不同有着不同的含义,如下图二。

                                                                        图一

                                                                        图二

 /* 设置任务 xTask1Handle 的任务通知值的位 8 为 1*/
 xTaskNotify( xTask1Handle, ( 1UL << 8UL ), eSetBits );
 
 /* 向任务 xTask2Handle 发送一个任务通知
 有可能
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值