系列文章目录
文章目录
- 系列文章目录
- 任务通知
- 任务通知值和通知状态
- 任务通知值
- 通知状态
- 任务通知API函数
- 发送通知函数
- 接收通知函数
- 实验测试
- 模拟二值信号量实验
- 模拟计数型信号量
- 模拟事件标志组实验
- 模拟消息邮箱实验
任务通知
任务通知:用来通知任务的,任务控制块中的结构体成员变量 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, ¬ify_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);
}
}