这里写目录标题
- 一、任务通知的简介(了解)
- 二、任务通知值和通知状态(熟悉)
- 三、任务通知相关API函数介绍(熟悉)
- 四、任务通知模拟信号量实验(掌握)
- 五、任务通知模拟消息邮箱实验(掌握)
- 六、任务通知模拟事件标志组实验(掌握)
一、任务通知的简介(了解)
任务通知:用来通知任务的,任务控制块中的结构体成员变量 ulNotifiedValue就是这个通知值
使用队列、信号量、事件标志组时都需另外创建一个结构体,通过中间的结构体进行间接通信!
使用任务通知时,任务结构体TCB中就包含了内部对象,可以直接接收别人发过来的"通知"
任务通知值的更新方式
1、不覆盖接受任务的通知值
2、覆盖接受任务的通知值
3、更新接受任务通知值的一个或多个bit
4、增加接受任务的通知值
只要合理,灵活的利用任务通知的特点,可以在一些场合中替代队列、信号量、事件标志组!
任务通知的优势及劣势
二、任务通知值和通知状态(熟悉)
任务都有一个结构体:任务控制块TCB,它里边有两个结构体成员变量:
c
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 类型,用来表示通知状态
任务通知值
任务通知值的更新方式有多种类型:
任务通知状态
其中任务通知状态共有3种取值:
c
#define taskNOT_WAITING_NOTIFICATION ( ( uint8_t ) 0 ) /* 任务未等待通知 */
#define taskWAITING_NOTIFICATION ( ( uint8_t ) 1 ) /* 任务在等待通知 */
#define taskNOTIFICATION_RECEIVED ( ( uint8_t ) 2 ) /* 任务在等待接收 */

三、任务通知相关API函数介绍(熟悉)
任务通知API函数主要有两类:①发送通知 ,②接收通知。
注意:发送通知API函数可以用于任务和中断服务函数中;接收通知API函数只能用在任务中。
①发送通知相关API函数:
c
#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 )
BaseType_t xTaskGenericNotify( TaskHandle_t xTaskToNotify,
UBaseType_t uxIndexToNotify,
uint32_t ulValue,
eNotifyAction eAction,
uint32_t * pulPreviousNotificationValue )

任务通知方式共有以下几种:
c
typedef enum
{
eNoAction = 0, /* 无操作 */
eSetBits /* 更新指定bit */
eIncrement /* 通知值加一 */
eSetValueWithOverwrite /* 覆写的方式更新通知值 */
eSetValueWithoutOverwrite /* 不覆写通知值 */
} eNotifyAction;
②接收通知相关API函数:
总结:
当任务通知用作于信号量时,使用函数获取信号量:ulTaskNotifyTake()
当任务通知用作于事件标志组或队列时,使用此函数来获取: xTaskNotifyWait()
c
#define ulTaskNotifyTake( xClearCountOnExit , xTicksToWait ) \
ulTaskGenericNotifyTake( ( tskDEFAULT_INDEX_TO_NOTIFY ), \
( xClearCountOnExit ), \
( xTicksToWait ) )
此函数用于接收任务通知值,可以设置在退出此函数的时候将任务通知值清零或者减一
c
#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 );

四、任务通知模拟信号量实验(掌握)
1、实验目的:学习使用 FreeRTOS 中的任务通知功能模拟二值信号量和计数型信号量
2、实验设计:将设计三个任务:start_task、task1、task2
三个任务的功能如下:
a.模拟二值信号量
c
/* 任务一,发送任务通知值 */
void task1( void * pvParameters )
{
uint8_t key = 0;
while(1)
{
key = key_scan(0);
if(key == KEY0_PRES)
{
printf("任务通知模拟二值信号量释放!\r\n");
xTaskNotifyGive(task2_handler);
}
vTaskDelay(10);
}
}
/* 任务二,接收任务通知值 */
void task2( void * pvParameters )
{
uint32_t rev = 0;
while(1)
{
rev = ulTaskNotifyTake(pdTRUE , portMAX_DELAY);
if(rev != 0)
{
printf("接收任务通知成功,模拟获取二值信号量!\r\n");
}
}
}
b.计数型信号量
c
/* 任务一,发送任务通知值 */
void task1( void * pvParameters )
{
uint8_t key = 0;
while(1)
{
key = key_scan(0);
if(key == KEY0_PRES)
{
printf("任务通知模拟计数型信号量释放!\r\n");
xTaskNotifyGive(task2_handler);
}
vTaskDelay(10);
}
}
/* 任务二,接收任务通知值 */
void task2( void * pvParameters )
{
uint32_t rev = 0;
while(1)
{
rev = ulTaskNotifyTake(pdFALSE , portMAX_DELAY);
if(rev != 0)
{
printf("rev:%d\r\n",rev);
}
vTaskDelay(1000);
}
}
五、任务通知模拟消息邮箱实验(掌握)
1、实验目的:学习使用 FreeRTOS 中的任务通知功能模拟消息邮箱
2、实验设计:将设计三个任务:start_task、task1、task2
三个任务的功能如下:
c
/* 任务一,发送任务通知值 */
void task1( void * pvParameters )
{
uint8_t key = 0;
while(1)
{
key = key_scan(0);
if((key != 0) && (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 );
switch(noyify_val)
{
case KEY0_PRES:
{
printf("接收到的通知值为:%d\r\n",noyify_val);
LED0_TOGGLE();
break;
}
case KEY1_PRES:
{
printf("接收到的通知值为:%d\r\n",noyify_val);
LED1_TOGGLE();
break;
}
default : break;
}
}
}
六、任务通知模拟事件标志组实验(掌握)
1、实验目的:学习使用 FreeRTOS 中的任务通知功能模拟事件标志组
2、实验设计:将设计三个任务:start_task、task1、task2
三个任务的功能如下:
c
#define EVENTBIT_0 (1 << 0)
#define EVENTBIT_1 (1 << 1)
/* 任务一,发送任务通知值*/
void task1( void * pvParameters )
{
uint8_t key = 0;
while(1)
{
key = key_scan(0);
if(key == KEY0_PRES)
{
printf("将bit0位置1\r\n");
xTaskNotify( task2_handler, EVENTBIT_0, eSetBits );
}else if(key == KEY1_PRES)
{
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, ¬ify_val, portMAX_DELAY );
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;
}
}
}