1 任务通知简介:
2 发送任务通知的函数(6个):
以下是任务控制块 TCB_t 的结构体种任务通知值:
任务通知的通用函数分析:
其实就是修改任务的通知值和通知状态,再进行任务切换。
#if (configUSE_TASK_NOTIFICATIONS == 1)
BaseType_t xTaskGenericNotify(TaskHandle_t xTaskToNotify,
uint32_t ulValue,
eNotifyAction eAction,
uint32_t *pulPreviousNotificationValue)
{
TCB_t *pxTCB;
BaseType_t xReturn = pdPASS;
uint8_t ucOriginalNotifyState;
configASSERT(xTaskToNotify);
pxTCB = (TCB_t *)xTaskToNotify; // 获取任务控制块
taskENTER_CRITICAL();
{
if (pulPreviousNotificationValue != NULL)
{
// 获取发送任务通知之前的值
*pulPreviousNotificationValue = pxTCB->ulNotifiedValue;
}
ucOriginalNotifyState = pxTCB->ucNotifyState;
pxTCB->ucNotifyState = taskNOTIFICATION_RECEIVED; // 状态改为接收
// 根据动作,来对应的修改任务通知值
switch (eAction)
{
case eSetBits:
pxTCB->ulNotifiedValue |= ulValue;
break;
case eIncrement:
(pxTCB->ulNotifiedValue)++;
break;
case eSetValueWithOverwrite:
pxTCB->ulNotifiedValue = ulValue;
break;
case eSetValueWithoutOverwrite:
if (ucOriginalNotifyState != taskNOTIFICATION_RECEIVED)
{
pxTCB->ulNotifiedValue = ulValue;
}
else
{
xReturn = pdFAIL;
}
break;
case eNoAction:
break;
}
traceTASK_NOTIFY();
// 如果要接收的任务是在等待通知,则加入到就绪列表
if (ucOriginalNotifyState == taskWAITING_NOTIFICATION)
{
(void)uxListRemove(&(pxTCB->xStateListItem));
prvAddTaskToReadyList(pxTCB);
/* The task should not have been on an event list. */
configASSERT(listLIST_ITEM_CONTAINER(&(pxTCB->xEventListItem)) == NULL);
#if (configUSE_TICKLESS_IDLE != 0)
{
prvResetNextTaskUnblockTime();
}
#endif
// 判断从阻塞列表中恢复的任务,是否要进行任务切换
if (pxTCB->uxPriority > pxCurrentTCB->uxPriority)
{
taskYIELD_IF_USING_PREEMPTION();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
taskEXIT_CRITICAL();
return xReturn;
}
#endif /* configUSE_TASK_NOTIFICATIONS */
上面提到的3个发送任务通知的函数,就是对通用函数进行宏定义封装,如下所示:
#define xTaskNotify(xTaskToNotify, ulValue, eAction) \
xTaskGenericNotify((xTaskToNotify), (ulValue), (eAction), NULL) // 任务通知先前值为NULL
#define xTaskNotifyAndQuery(xTaskToNotify, ulValue, eAction, pulPreviousNotifyValue) \
xTaskGenericNotify((xTaskToNotify), (ulValue), (eAction), (pulPreviousNotifyValue))
#define xTaskNotifyGive(xTaskToNotify) \
xTaskGenericNotify((xTaskToNotify), (0), eIncrement, NULL) // 任务通知先前值为NULL,动作递增
3 获取任务通知:
函数说明:
获取任务通知的函数源码:
uint32_t ulTaskNotifyTake(BaseType_t xClearCountOnExit, TickType_t xTicksToWait)
{
uint32_t ulReturn;
taskENTER_CRITICAL();
{
/* 仅在通知计数值不为0时阻塞,通知计数值为0时任务运行 */
if (pxCurrentTCB->ulNotifiedValue == 0UL)
{
/* 将此任务标记为等待通知 */
pxCurrentTCB->ucNotifyState = taskWAITING_NOTIFICATION;
if (xTicksToWait > (TickType_t)0)
{
/* 添加到任务延时列表 */
prvAddCurrentTaskToDelayedList(xTicksToWait, pdTRUE);
traceTASK_NOTIFY_TAKE_BLOCK();
portYIELD_WITHIN_API(); // 然后进行任务调度
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
taskEXIT_CRITICAL();
taskENTER_CRITICAL();
{
traceTASK_NOTIFY_TAKE();
ulReturn = pxCurrentTCB->ulNotifiedValue;
if (ulReturn != 0UL) // 根据参数值更新任务通知值
{
if (xClearCountOnExit != pdFALSE)
{
pxCurrentTCB->ulNotifiedValue = 0UL;
}
else
{
pxCurrentTCB->ulNotifiedValue = ulReturn - 1;
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
pxCurrentTCB->ucNotifyState = taskNOT_WAITING_NOTIFICATION;
}
taskEXIT_CRITICAL();
return ulReturn;
}
全功能版函数:
任务通知模拟二值信号量
程序很简单,直接将释放、获取二值信号量的函数换成任务通知,如下所示:
// 中断发送任务通知
void USART1_IRQHandler(void)
{
static uint16_t i = 0;
uint32_t temp;
BaseType_t pxHigherPriorityTaskWoken;
// 保存接收数据到RX_BUFFER缓冲区
if (__HAL_UART_GET_FLAG(&huart1, UART_FLAG_RXNE))
{
RX_BUFFER[i++] = huart1.Instance->DR;
__HAL_UART_CLEAR_FLAG(&huart1, UART_FLAG_RXNE);
}
// 串口数据接收完成,串口空闲时,释放信号量
if (__HAL_UART_GET_FLAG(&huart1, UART_FLAG_IDLE))
{
i = 0;
// 发送任务通知
vTaskNotifyGiveFromISR(task2_task_Handle, &pxHigherPriorityTaskWoken);
// 判断是否需要进行任务切换
if (pxHigherPriorityTaskWoken == pdTRUE)
{
taskYIELD();
}
// 清除空闲中断(依次读取SR DR)
temp = huart1.Instance->SR;
temp = huart1.Instance->DR;
}
}
// 任务接收任务通知
void task2_task(void *pvParameters)
{
uint16_t count = 0;
for (;;)
{
// 一直等待,直到获取到任务通知
ulTaskNotifyTake(pdTRUE, portMAX_DELAY); // 获取任务通知
if (strcmp((char *)RX_BUFFER, "LED_ON") == 0)
{
LED_RED;
}
if (strcmp((char *)RX_BUFFER, "LED_OFF") == 0)
{
LED_ALL_OFF;
}
if (strcmp((char *)RX_BUFFER, "BEEP_ON") == 0)
{
BEEP_ON;
}
if (strcmp((char *)RX_BUFFER, "BEEP_OFF") == 0)
{
BEEP_OFF;
}
printf("接收到任务通知,串口指令为 \"%s\"\n", RX_BUFFER);
memset(RX_BUFFER, 0, USART_BUFFER_LEN);
printf("we get %d times task_notify\n", ++count);
vTaskDelay(20);
}
}
程序运行结果如下:
任务通知模拟计数型信号量
程序如下所示:
void task1_task(void *pvParameters)
{
for (;;)
{
// 按键KEY1用于发送任务通知
if (key_scan(KEY1_GPIO_Port, KEY1_Pin) == KEY_ON)
{
// 向task2_task发送任务通知
xTaskNotifyGive(Task2_Handle);
printf("KEY1按下,向task2_task发送任务通知\r\n") ;
}
vTaskDelay(50);
}
}
void task2_task(void *pvParameters)
{
uint32_t task_notify_value;
for (;;)
{
// 按键KEY2用于获取任务通知
if (key_scan(KEY2_GPIO_Port, KEY2_Pin) == KEY_ON)
{
// 等待获取任务通知
task_notify_value = ulTaskNotifyTake(pdFALSE, portMAX_DELAY);
// task_notify_value 为 -1 之前的值
printf("KEY2按下,当前任务通知值为%d\r\n", task_notify_value - 1) ;
}
vTaskDelay(50);
}
}
程序执行结果:
任务通知值模拟队列
程序代码如下:
#define TASK_LED_ON 32
#define TASK_LED_OFF 86
void key_task(void *pvParameters)
{
uint8_t flag = 0;
for (;;)
{
if (key_scan(KEY1_GPIO_Port, KEY1_Pin) == KEY_ON)
{
flag = !flag;
// 发送开灯 / 关灯的任务通知值
if (flag)
{
xTaskNotify(print_task_Handle, TASK_LED_ON, eSetValueWithOverwrite);
printf("发送任务通知值%d\r\n", TASK_LED_ON);
}
else
{
xTaskNotify(print_task_Handle, TASK_LED_OFF, eSetValueWithOverwrite);
printf("发送任务通知值%d\r\n", TASK_LED_OFF);
}
printf("任务通知值功能:\nTASK_LED_ON <--> %d\r\nTASK_LED_OFF <--> %d\r\n",
TASK_LED_ON, TASK_LED_OFF);
}
vTaskDelay(50);
}
}
void print_task(void *pvParameters)
{
BaseType_t rx_state;
uint32_t notify_value;
for (;;)
{
if (key_scan(KEY2_GPIO_Port, KEY2_Pin) == KEY_ON)
{
rx_state = xTaskNotifyWait(0, 0xffffffffUL, ¬ify_value, portMAX_DELAY);
if (rx_state == pdPASS)
{
printf("收到任务通知值 = %d\r\n", notify_value);
switch (notify_value)
{
case TASK_LED_ON:
LED_RED;
break;
case TASK_LED_OFF:
LED_ALL_OFF;
break;
default:
break;
}
}
}
vTaskDelay(50);
}
}
程序执行结果如下所示: