12.FreeRTOS任务通知

任务通知概述

任务通知并不像队列、信号量、互斥量这些方法需要在使用之前先进行创建队列、创建信号量、创建互斥量。任务通知的相关存储空间,在创建任务时就已经分配。

在调用xTaskCreate创建任务时,会将任务以TCB_t类型的结构体进行管理,在这个结构体中分配了一部分空间给任务通知使用,下面为TCB_t结构体中关于任务通知的部分:

#if ( configUSE_TASK_NOTIFICATIONS == 1 )
    
    volatile uint32_t ulNotifiedValue[ configTASK_NOTIFICATION_ARRAY_ENTRIES ];
    volatile uint8_t ucNotifyState[ configTASK_NOTIFICATION_ARRAY_ENTRIES ];

#endif

可以看到,对于任务通知的空间,只有一个通知值和一个通知状态,并没有任何像队列结构体那些定义了等待读入任务链表、等待写入任务链表。因此任务通知的阻塞也不同于队列:

  • 对于发送任务通知的任务,不会进入到阻塞状态,只会发送成功或发送失败
  • 对于接收任务通知的任务,可以进入阻塞状态进行等待通知。有任务发送通知时,解除阻塞。

任务通知的状态有三种取值:

/* 任务没有在等待通知 */
#define taskNOT_WAITING_NOTIFICATION              ( ( uint8_t ) 0 ) 
/* 任务在等待通知 */
#define taskWAITING_NOTIFICATION                  ( ( uint8_t ) 1 )
/* 任务接收到了通知(pending) */
#define taskNOTIFICATION_RECEIVED                 ( ( uint8_t ) 2 )

状态转变的过程如下(假设任务A向任务B发送任务通知):

当任务B调用接收任务通知的函数时,通知状态被设置成 “ 任务在等待通知 ”。当任务A调用发送任务通知的函数时,任务B的通知值val被改变,通知状态被设置成 “ 任务接收到了通知 ”。此时任务B被唤醒,不再阻塞,并且将通知状态设置成 “ 任务没有在等待通知 ”。

相关函数

在接下来的函数描述中,假设任务A向任务B发送通知,通知值用val描述

任务通知的收发函数有两组,第一组是简单的,第二组是复杂的。实质上第一组是第二组的一部分

发送任务通知(1)

函数声明如下:

/* 这是一个宏,发送任务通知 */
xTaskNotifyGive( xTaskToNotify )

/* 宏定义 */
#define xTaskNotifyGive( xTaskToNotify )                 \
    xTaskGenericNotify( ( xTaskToNotify ),               \
                        ( tskDEFAULT_INDEX_TO_NOTIFY ),  \
                        ( 0 ),                           \
                        eIncrement,                      \
                        NULL )                           \

/* 实际调用函数 */
BaseType_t xTaskGenericNotify( TaskHandle_t xTaskToNotify,
                               UBaseType_t uxIndexToNotify,
                               uint32_t ulValue,
                               eNotifyAction eAction,
                               uint32_t * pulPreviousNotificationValue )

xTaskToNotify:目标任务的句柄,即任务B

该函数并没有设置发送的通知值是什么,最终操作是让任务B的val值+1

接收任务通知(1)

函数声明如下:

/* 这是一个宏,接收任务通知 */
ulTaskNotifyTake( xClearCountOnExit, xTicksToWait ) 

/* 宏定义 */
#define ulTaskNotifyTake( xClearCountOnExit, xTicksToWait ) \
    ulTaskGenericNotifyTake( ( tskDEFAULT_INDEX_TO_NOTIFY ),\
                             ( xClearCountOnExit ),         \
                             ( xTicksToWait ) )             \

/* 实际调用函数 */
uint32_t ulTaskGenericNotifyTake( UBaseType_t uxIndexToWait,
                                  BaseType_t xClearCountOnExit,
                                  TickType_t xTicksToWait )

返回值:任务B中获得到的通知值val,这个val是清除之前的val

xClearCountOnExit:函数退出时是否清除val,传入值为pdFALSE、pdTRUE

  • 传入pdFALSE时,函数退出时,val--
  • 传入pdTRUE时,函数退出时,val=0

发送任务通知(2)

函数声明如下:

/* 这是一个宏,发送任务通知 */
#define xTaskNotify( xTaskToNotify, ulValue, eAction )

/* 宏定义 */
#define xTaskNotify( xTaskToNotify, ulValue, eAction )  \
    xTaskGenericNotify( ( xTaskToNotify ),              \
                        ( tskDEFAULT_INDEX_TO_NOTIFY ), \
                        ( ulValue ),                    \
                        ( eAction ),                    \
                        NULL )                            

/* 实际调用函数 */
BaseType_t xTaskGenericNotify( TaskHandle_t xTaskToNotify,
                               UBaseType_t uxIndexToNotify,
                               uint32_t ulValue,
                               eNotifyAction eAction,
                               uint32_t * pulPreviousNotificationValue )

xTaskToNotify:目标任务的句柄,即任务B

ulValue:传入的通知值,该值是否起作用,取决于eAction的值

eAction:控制传入的通知值,取值及效果如下:

  • eNoAction:A给B发个通知而已,不会修改B中的val值。这时传入的val没用
  • eSetBits:B中的val = 原来的val | 传入的val值
  • eIncrement:B中的val++,此时与xTaskNotifyGive效果一致
  • eSetValueWithoutOverwrite:第二次的通知值不覆盖第一次没有被取走的通知值
  • eSetValueWithOverwrite:第二次的通知值覆盖第一次没有被取走的通知值

接收任务通知(2)

函数声明如下:

/* 这是一个宏,接收任务通知 */
xTaskNotifyWait( ulBitsToClearOnEntry, 
                 ulBitsToClearOnExit, 
                 pulNotificationValue, 
                 xTicksToWait ) \

/* 宏定义 */
#define xTaskNotifyWait( ulBitsToClearOnEntry, 
                         ulBitsToClearOnExit, 
                         pulNotificationValue, 
                         xTicksToWait ) \
        xTaskGenericNotifyWait( tskDEFAULT_INDEX_TO_NOTIFY, 
                                ( ulBitsToClearOnEntry ), 
                                ( ulBitsToClearOnExit ), 
                                ( pulNotificationValue ), 
                                ( xTicksToWait ) )

/* 实际调用函数 */
BaseType_t xTaskGenericNotifyWait( UBaseType_t uxIndexToWait,
                                   uint32_t ulBitsToClearOnEntry,
                                   uint32_t ulBitsToClearOnExit,
                                   uint32_t * pulNotificationValue,
                                   TickType_t xTicksToWait )

ulBitsToClearOnEntry:入口处(进入函数时)是否清除某些位

ulBitsToClearOnExit:出口处(退出函数时)是否清除某些位

  • 这两个参数都以bit传入,实际上的运算为:val &= ~bits。
  • 如果传入0表示不清除任何位,如果传入1<<3代表清除第3位

pulNotificationValue:获取在出口处清除val之前的val值

xTicksToWait:超时

验证实验

实验内容为:使用任务通知实现轻量级信号量,思路如下:

  • 利用xTaskNotifyGive可以实现将任务B中的val++,这类似于放入信号量。
  • 利用ulTaskNotifyTake可以实现将任务B中的val--,这类似于拿出信号量

具体代码实现如下:

TaskHandle_t xHandleTaskA;
TaskHandle_t xHandleTaskB;
void TaskAFunction(void *param){
	
	int i=0;
	
	while(1){
		for(i=0;i<10;i++){
			/* 向TaskB发送任务通知,TaskB的val++ */
			xTaskNotifyGive(xHandleTaskB);	
		}
		/* 发送信号完成,自杀 */
		vTaskDelete(NULL);
	}
}

void TaskBFunction(void *param){
	
	uint32_t val;
	
	while(1){
        /* 获取通知,pdFALSE代表每次val--而不是清零 */
		val = ulTaskNotifyTake(pdFALSE,portMAX_DELAY);
		printf("val = %d\r\n",val);
	}
}

int main( void )
{
	prvSetupHardware();
	SerialPortInit();
	printf("UART TEST\r\n");
	
	xTaskCreate(TaskAFunction,"TaskA",100,(void*)NULL,2,&xHandleTaskA);
	xTaskCreate(TaskBFunction,"TaskB",100,(void*)NULL,1,&xHandleTaskB);
	
	vTaskStartScheduler();
	
	return 0;
}

运行结果如下:

可以看到,当取完val = 1后(相当于信号量没有了),Task2进入到了阻塞状态。这说明实现了类似计数型信号量的功能。

  • 7
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值