FreeRTOS专栏17:任务通知

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, &notify_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);
  }
}

程序执行结果如下所示:

发布了184 篇原创文章 · 获赞 100 · 访问量 7万+

猜你喜欢

转载自blog.csdn.net/dingyc_ee/article/details/104139687