目录
特性
每个任务都有一个32位的任务任务通知值,任务通知是直接发送到任务的事件,可以解除接收任务的阻塞。
任务通知可以通过下面四种方式更新任务通知值:
设置接收任务的通知值(不覆盖前面的值);
设置接收任务的通知值(覆盖前面的值);
设置接收任务通知值的一位或多位;
增加接收任务的通知值。
由于任务通知的灵活性,可以在需要使用单队列、二值信号量、计数信号量、事件标志组的时候使用它替代。使用任务通知解锁任务要比上面的方式快45%,且需要的ram更少。
应用限制
- 任务通知只能被用在任务只有一个接收事件的情况下;
- 任务通知可以被用来代替队列:接收任务可以等待任务通知进入阻塞态,发送任务不会进入阻塞态,即使没有发送成功。
任务通知用作事件标志组
xTaskNotifyWait() 用来代替 xEventGroupWaitBits()
xTaskNotify() 用来代替 xEventGroupSetBits()
xTaskNotifyFromISR() 用来代替 xEventGroupSetBitsFromISR()
任务通知用作事件标志组的优点
与xEventGroupSetBitsFromISR()相比,xTaskNotifyFromISR()具有显著的性能优势,因为xTaskNotifyFromISR()完全在ISR中执行,而xEventGroupSetBitsFromISR()必须将一些处理延迟到RTOS守护进程任务。
任务通知用作事件标志组的缺点
任务通知不能组合位判断,当有一位满足条件时,就离开阻塞状态,如果是组合位判断,需要额外判断。
部分API
xTaskNotify( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction );
//发送任务通知,参数xTaskToNotify是发向的任务句柄,ulValue是发送的任务通知值,eAction 是发送的形式
xTaskNotifyFromISR( TaskHandle_t xTaskToNotify,
uint32_t ulValue,
eNotifyAction eAction,
BaseType_t *pxHigherPriorityTaskWoken );//同上,用于中断函数中
xTaskNotifyWait( uint32_t ulBitsToClearOnEntry,
uint32_t ulBitsToClearOnExit,
uint32_t *pulNotificationValue,
TickType_t xTicksToWait );
//接收任务通知,参数ulBitsToClearOnEntry表示读取之前是否清除,设置为0xffffffff将会清除
//参数ulBitsToClearOnExit表示读取之后是否清楚,设置为0xffffffff将会清除
//参数pulNotificationValue用于获取任务通知值
//参数xTicksToWait表示阻塞时间
发送任务通知的形式eNotifyAction
/* Actions that can be performed when vTaskNotify() is called. */
typedef enum
{
eNoAction = 0,/* Notify the task without updating its notify value. */
eSetBits, //设置位
eIncrement, //增加任务通知值
eSetValueWithOverwrite, //覆盖的方式设置任务通知值
eSetValueWithoutOverwrite //不覆盖的方式设置任务通知值
} eNotifyAction;
测试程序
总体设计:2个任务,1个中断
counttask:计数并且打印,计数到50的时候发送任务通知
serialtask:获取任务通知,并判断是何种事件
串口中断:收到数据之后,发送任务通知
创建任务
#define COUNT_TASK_PRIO 1 //任务优先级
#define COUNT_TASK_STK_SIZE 80 //任务堆栈大小
TaskHandle_t CountTaskHandler; //任务句柄
void CountTaskFunc(void *pvParameters); //任务函数
#define SERIAL_TASK_PRIO 4 //任务优先级
#define SERIAL_TASK_STK_SIZE 80 //任务堆栈大小
TaskHandle_t SerialTaskHandler; //任务句柄
void SerialTaskFunc(void *pvParameters); //任务函数
#define BIT_0 1<<0
#define BIT_1 1<<1
#define BIT_2 1<<2
void OtherTest(void )
{
BaseType_t ret;
BoardInitMcu();
BoardInitPeriph();
ret=xTaskCreate( (TaskFunction_t )CountTaskFunc,
(const char* )"counttask",
(uint16_t )COUNT_TASK_STK_SIZE,
(void* )NULL,
(UBaseType_t )COUNT_TASK_PRIO,
(TaskHandle_t* )&CountTaskHandler);
ret=xTaskCreate((TaskFunction_t )SerialTaskFunc,
(const char* )"serialtask",
(uint16_t )SERIAL_TASK_STK_SIZE,
(void* )NULL,
(UBaseType_t )SERIAL_TASK_PRIO,
(TaskHandle_t* )&SerialTaskHandler);
vTaskStartScheduler();
}
各个任务函数
void CountTaskFunc(void *pvParameters)
{
static uint8_t i;
for(;;)
{
i++;
printf("count =%d\r\n",i);
if(i==50)
{
i=0;
xTaskNotify( SerialTaskHandler, BIT_1, eSetBits );
}
vTaskDelay(500); //延时500ms,也就是500个时钟节拍
}
}
void SerialTaskFunc(void *pvParameters)
{
BaseType_t ret;
uint32_t pulNotificationValue;
for(;;)
{
ret= xTaskNotifyWait(0, //读取之前,设置相应的位,0:不设置,ffffffffff:设置所有的位
0xffffffff, //读取之后,清除相应的位
&pulNotificationValue, //读取到的值
10); //阻塞时间
if(ret==pdPASS)
{
if((pulNotificationValue&BIT_0)==BIT_0)
{
printf("serial \r\n");
}
else
{
if((pulNotificationValue&BIT_1)==BIT_1)
{
printf("count \r\n");
}
else
{
printf("default");
}
}
}
else
{
vTaskDelay(10);
}
}
}
中断函数
void USART1_IRQHandler( void )
{
BaseType_t pxHigherPriorityTaskWoken=pdFALSE;
//省略部分代码
if(RESET != __HAL_UART_GET_FLAG(&UartHandle,UART_FLAG_IDLE))
{
__HAL_UART_CLEAR_IDLEFLAG(&UartHandle);
/*任务通知用作事件标志组*/
xTaskNotifyFromISR( SerialTaskHandler,
(1<<0),
eSetBits,
&pxHigherPriorityTaskWoken);
portYIELD_FROM_ISR(pxHigherPriorityTaskWoken);
}
}
运行结果
与使用事件标志组是一样的。
freemote 发布了35 篇原创文章 · 获赞 25 · 访问量 1万+ 私信 关注