FreeRTOS实时操作系统(十三)任务通知

系列文章

FreeRTOS实时操作系统(一)RTOS的基本概念

FreeRTOS实时操作系统(二)任务创建与任务删除(HAL库)

FreeRTOS实时操作系统(三)任务挂起与恢复

FreeRTOS实时操作系统(四)中断任务管理

FreeRTOS实时操作系统(五)进入临界区、任务调度器挂起与恢复

FreeRTOS实时操作系统(六)列表与列表项

FreeRTOS实时操作系统(七)时间片调度及RTOS的滴答定时器

FreeRTOS实时操作系统(八)任务状态查询及时间统计函数

FreeRTOS实时操作系统(九)时间延时函数及消息队列

FreeRTOS实时操作系统(十)信号量

FreeRTOS实时操作系统(十一)队列集

FreeRTOS实时操作系统(十二)事件标志组

FreeRTOS实时操作系统(十三)任务通知

FreeRTOS实时操作系统(十四)软件定时器

FreeRTOS实时操作系统(十五)Tickless低功耗模式

FreeRTOS实时操作系统(十六)内存管理



任务通知

任务通知:用来通知任务的,任务控制块中的结构体成员变量 ulNotifiedValue就是这个通知值。
(相比队列、信号量和事件标志组,内存消耗比较小,不需要创建)

更新方式:
可以覆盖或不覆盖接收任务的通知值
更新接受任务通知值的一个或多个bit
增加接收任务的通知值

优势:
效率更高,比队列、信号量和事件标志组速度更快
使用内存更小,无需额外创建结构体
劣势:
无法发送数据给ISR,但ISR可以发送数据给任务
无法广播给多个任务:任务通知只能指定一个任务接收处理
无法缓存多个数据:结构体中只有一个任务通知值,只能保存一个数据
发送受阻不支持阻塞:发送方无法进入阻塞状态

任务通知值和通知状态

任务通知值

任务都有一个结构体:任务控制块TCB,它里边有两个结构体成员变量

typedef  struct  tskTaskControlBlock 
{
	… …
    	#if ( configUSE_TASK_NOTIFICATIONS  ==  1 )
        	volatile  uint32_t  ulNotifiedValue [ configTASK_NOTIFICATION_ARRAY_ENTRIES ];
        	volatile  uint8_t   ucNotifyState [ configTASK_NOTIFICATION_ARRAY_ENTRIES ];
    	endif
	… …
} tskTCB;
#define  configTASK_NOTIFICATION_ARRAY_ENTRIES	1  	/* 定义任务通知数组的大小, 默认: 1 */

一个是 uint32_t 类型,用来表示通知值
一个是 uint8_t 类型,用来表示通知状态

任务通知值更新方式:
1.计数值:数值累加,类似信号量
2.相应位置1,类似事件标志组
3.任意数值支持覆写和不覆写,类似队列

通知状态

#define	taskNOT_WAITING_NOTIFICATION  	( ( uint8_t ) 0 )		 /* 任务未等待通知 */
#define 	taskWAITING_NOTIFICATION		( ( uint8_t ) 1 )		 /* 任务在等待通知 */
#define 	taskNOTIFICATION_RECEIVED                 	( ( uint8_t ) 2 )		 /* 任务在等待接收 */

任务未等待通知:任务通知的默认初始化,什么都不管
等待通知:接收方已经准备好了(调用了接收任务通知函数),等待发送方给个通知
等待接收:发送方已经发送出去(调用了发送任务通知函数),等待接收方接收

任务通知API函数

分为发送通知和接收通知两种

发送可以在任务或中断服务函数中,接收只能用在任务中。

发送通知函数

函数描述
xTaskNotify()发送通知,带有通知值(事件标志组)
xTaskNotifyAndQuery()发送通知,带有通知值并且保留接收任务的原通知值
xTaskNotifyGive()发送通知,不带通知值(信号量)
xTaskNotifyFromISR()在中断中发送任务通知
xTaskNotifyAndQueryFromISR()在中断中发送任务通知
vTaskNotifyGiveFromISR()在中断中发送任务通知

详细内容: 发送通知的函数:

#define   	xTaskNotifyAndQuery( xTaskToNotify ,  ulValue ,  eAction ,  pulPreviousNotifyValue  )	\ 
		    xTaskGenericNotify( ( xTaskToNotify ), 
					  ( tskDEFAULT_INDEX_TO_NOTIFY ), 
					  ( ulValue ), 
					  ( eAction ),
					  ( pulPreviousNotifyValue ) )

#define	xTaskNotify  (  xTaskToNotify ,  ulValue ,  eAction  ) 							\   
 		xTaskGenericNotify( (xTaskToNotify) , (tskDEFAULT_INDEX_TO_NOTIFY) , (ulValue) , (eAction) , NULL )
 		
#define 	xTaskNotifyGive(  xTaskToNotify  )									 \   
		xTaskGenericNotify(  ( xTaskToNotify ) ,  ( tskDEFAULT_INDEX_TO_NOTIFY ) ,  ( 0 ) ,   eIncrement ,  NULL )

看到其核心函数都是xTaskGenericNotify()

BaseType_t	 xTaskGenericNotify(  TaskHandle_t 	xTaskToNotify,
                       			   UBaseType_t 	uxIndexToNotify,
                       			    uint32_t 		ulValue,
                       			    eNotifyAction 	eAction,
                      			    uint32_t * 		pulPreviousNotificationValue  )
形参描述
xTaskToNotify接收任务通知的任务句柄
uxIndexToNotify任务的指定通知(任务通知相关数组成员)
ulValue任务通知值
eAction通知方式(通知值更新方式)
pulPreviousNotificationValue用于保存更新前的任务通知值(为NULL则不保存)

任务通知方式:

typedef  enum
{    
	eNoAction = 0, 			/* 无操作 */
	eSetBits				/* 更新指定bit */
	eIncrement				/* 通知值加一 */
 	eSetValueWithOverwrite		/* 覆写的方式更新通知值 */
	eSetValueWithoutOverwrite	/* 不覆写通知值 */
} eNotifyAction;

接收通知函数

函数描述
ulTaskNotifyTake()获取任务通知,可以设置在退出此函数的时候将任务通知值清零或者减一。当任务通知用作二值信号量或者计数信号量的时候,使用此函数来获取信号量。
xTaskNotifyWait()获取任务通知,比 ulTaskNotifyTak()更为复杂,可获取通知值和清除通知值的指定位

ulTaskNotifyTake在将任务通知用作信号量的时候
xTaskNotifyWait在将任务通知用作事件标志组或者队列的时候

1.ulTaskNotifyTake

#define ulTaskNotifyTake( xClearCountOnExit , xTicksToWait ) 			\
ulTaskGenericNotifyTake( ( tskDEFAULT_INDEX_TO_NOTIFY ),( xClearCountOnExit ), (xTicksToWait ) ) 

用于接收任务通知值,可以设置在退出此函数的时候将任务通知值清零或者减一。(信号量)

形参描述
uxIndexToWaitOn任务的指定通知(任务通知相关数组成员)
xClearCountOnExit指定在成功接收通知后,将通知值清零或减 1,pdTRUE:把通知值清零;pdFALSE:把通知值减一
xTicksToWait阻塞等待任务通知值的最大时间
返回值描述
0接收失败
非0接收成功,返回任务的通知值

2.xTaskNotifyWait

#define xTaskNotifyWait(ulBitsToClearOnEntry, ulBitsToClearOnExit, pulNotificationValue, xTicksToWait) \
xTaskGenericNotifyWait( tskDEFAULT_INDEX_TO_NOTIFY,( ulBitsToClearOnEntry ), ( ulBitsToClearOnExit ), ( pulNotificationValue ), ( xTicksToWait ) 

用于获取通知值和清除通知值的指定位值,适用于模拟队列和事件标志组,使用该函数来获取任务通知 。

BaseType_t  xTaskGenericNotifyWait( UBaseType_t uxIndexToWaitOn,
									uint32_t ulBitsToClearOnEntry,
									uint32_t ulBitsToClearOnExit,
									uint32_t * pulNotificationValue,
									TickType_t xTicksToWait	); 
形参描述
uxIndexToWaitOn任务的指定通知(任务通知相关数组成员)
ulBitesToClearOnEntry等待前清零指定任务通知值的比特位(旧值对应bit清0),将需要清零的位置1:例如0x01
ulBitesToClearOnExit成功等待后清零指定的任务通知值比特位(新值对应bit清0)
pulNotificationValue用来取出通知值(如果不需要取出,可设为NULL)
xTicksToWait阻塞等待任务通知值的最大时间
返回值描述
pdTRUE等待任务通知成功
pdFALSE等待任务通知失败

实验测试

模拟二值信号量实验

开始任务:创建任务1和任务2
任务1完成任务通知的功能
任务2完成接收任务通知,并且打印相关提示信息

void task1( void * pvParameters )
{
    while(1) 
    {
		printf("任务通知模拟二值信号量释放!\r\n");
		xTaskNotifyGive(task2_handler);
        vTaskDelay(20);
    }
}

void task2( void * pvParameters )
{
	uint32_t rev = 0;
    while(1)
    {
		 rev = ulTaskNotifyTake(pdTRUE , portMAX_DELAY);
        if(rev != 0)
        {
            printf("接收任务通知成功,模拟获取二值信号量!\r\n");
        }
    }
}

在这里插入图片描述

模拟计数型信号量

计数型信号量和二值信号量没啥区别,就是多次进行了任务通知,采用pdTRUE的形参在接收任务通知的时候,将通知值全部清空。

开始任务:创建任务1和任务2
任务1完成任务通知(计数型信号量不断累计)的功能
任务2完成接收任务通知,并且打印相关提示信息

void task1( void * pvParameters )
{
    while(1) 
    {
		printf("任务通知模拟计数型信号量释放!\r\n");
		xTaskNotifyGive(task2_handler);
        vTaskDelay(20);
    }
}

void task2( void * pvParameters )
{
	uint32_t rev = 0;
    while(1)
    {
		 rev = ulTaskNotifyTake(pdTRUE , portMAX_DELAY);
        if(rev != 0)
        {
            printf("rev:%d\r\n",rev);
        }
		vTaskDelay(1000);
    }
}

在这里插入图片描述

模拟事件标志组实验

开始任务:创建任务1和任务2
任务1完成任务通知,设置不同标志位
任务2完成接收任务通知,并且打印相关提示信息

#define EVENTBIT_0  (1 << 0)
#define EVENTBIT_1  (1 << 1)
 
void task1( void * pvParameters )
{
    while(1) 
    {
		printf("将bit0位置1\r\n");
		xTaskNotify( task2_handler, EVENTBIT_0, eSetBits );//模拟事件标志组,eSetBits代表更新指定bit的更新方式
		printf("将bit1位置1\r\n");
		xTaskNotify( task2_handler, EVENTBIT_1, eSetBits );
		vTaskDelay(10);
    }
}

void task2( void * pvParameters )
{
	 uint32_t notify_val = 0,event_bit = 0;
    while(1)
    {
		 xTaskNotifyWait( 0, 0xFFFFFFFF, &notify_val, portMAX_DELAY ); //成功等待后清零,因为通知值是32位,所以是0xFFFFFFFF
        if(notify_val & EVENTBIT_0)
        {
            event_bit |= EVENTBIT_0;
        }
        if(notify_val & EVENTBIT_1)
        {
            event_bit |= EVENTBIT_1;
        }
        if(event_bit == (EVENTBIT_0|EVENTBIT_1))
        {
            printf("任务通知模拟事件标志组接收成功!!\r\n");
            event_bit = 0;
        }
		
    }
}

在这里插入图片描述

模拟消息邮箱实验

开始任务:创建任务1和任务2
任务1完成任务通知,发送给指定任务
任务2完成接收任务通知,根据接收到数据完成信息打印

void task1( void * pvParameters )
{
	uint8_t key = 6;
    while(1) 
    {
		if(task2_handler != NULL)
		{
		printf("任务通知模拟消息邮箱发送,发送的键值为:%d\r\n",key);
		xTaskNotify( task2_handler, key, eSetValueWithOverwrite );
		}
		vTaskDelay(10);
    }
}

void task2( void * pvParameters )
{
	uint32_t noyify_val = 0;
    while(1)
    {
		xTaskNotifyWait( 0, 0xFFFFFFFF, &noyify_val, portMAX_DELAY );
		 printf("接收到的通知值为:%d\r\n",noyify_val);
    }
}

在这里插入图片描述

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值