FreeRTOS (task notification)

1. What is a task notification?

FreeRTOS provides task notification function starting from version V8.2.0.Each task has a 32-bit notification value. According to FreeRTOS officials, using message notifications is 45% faster than unblocking tasks through binary semaphores, and is more memory-saving (no need to create a queue). In most cases, task notifications can replace binary semaphores, counting semaphores, event flag groups, and queues of length 1 (which can store a 32-bit integer or pointer value), And task notifications are faster and use less RAM!

How to update task notification values

FreeRTOS provides the following ways to send notifications to tasks:

  • Send a message to the task. If there is an unread notification, the notification value will not be overwritten.
  • Send a message to the task and directly override the notification value
  • Send a message to a task, setting one or more bits of the notification value
  • Send a message to the task and increment the notification value

Through reasonable use of the above methods, the original queues, semaphores, event flag groups, etc. can be replaced in certain situations.

2. Advantages and disadvantages of task notifications

Advantages of task notifications

1. Using task notifications to send events or data to tasks is much faster than using queues, event flag groups or semaphores.

2. When using other methods, you must first create the corresponding structure. When using task notification, there is no need to create an additional structure.

Disadvantages of task notifications

1. Only tasks can wait for notifications, not in interrupt service functions, because interrupts do not have TCB.

2. Notifications can only be sentone-to-one because the notification must specify a task.

3. Tasks waiting for notifications can be blocked, but tasks that send messages will not be blocked waiting under any circumstances.

4. Task notification sends data by updating the task notification value. There is only one task notification value in the task structure. Keep a data.

3. Task notification related API functions

1. Send notification

 Send notification with notification value

BaseType_t xTaskNotify( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction );

parameter:

  • xTaskToNotify: task handle that needs to receive notifications;
  • ulValue: used to update the receiving task notification value. The specific update method is determined by the formal parameter eAction;
  • eAction: an enumeration representing the value of how to use task notifications;

Return value: If the notified task receives another notification before it has received the previous notification, the notification value cannot be updated this time and pdFALSE is returned. In other cases, pdPASS is returned.​ 

Send notification with notification value

BaseType_t xTaskNotifyAndQuery( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction, uint32_t *pulPreviousNotifyValue );

parameter:

  • xTaskToNotify: task handle that needs to receive notifications;
  • ulValue: used to update the receiving task notification value. The specific update method is determined by the formal parameter eAction;
  • eAction: an enumeration representing the value of how to use task notifications;
  • pulPreviousNotifyValue: The previous task notification value of the object task. If it is NULL, there is no need to return it. At this time, it is equivalent to the function xTaskNotify().

Return value: If the notified task receives another notification before it has received the previous notification, the notification value cannot be updated this time and pdFALSE is returned. In other cases, pdPASS is returned.

Send notification without notification value

BaseType_t xTaskNotifyGive( TaskHandle_t xTaskToNotify );

Parameters: xTaskToNotify: The task handle that receives the notification and adds 1 to its own task notification value.

 Return value: pdPASS is always returned.

2. Wait for notification

The wait notification API function can only be used in tasks and cannot be used in interrupts!

 uint32_t ulTaskNotifyTake( BaseType_t xClearCountOnExit, TickType_t xTicksToWait );

parameter:

  • xClearCountOnExit: specifies that after successfully receiving the notification, the notification value will be cleared or decremented by 1, pdTRUE: the notification value will be cleared to zero (two-valued semaphore); pdFALSE: the notification value will be decremented by one (counting semaphore);
  • xTicksToWait: The maximum time for blocking and waiting for task notification value;
  • Return value: 0: Reception failed Non-0: Reception successful, return the notification value of the task notification

BaseType_t xTaskNotifyWait( uint32_t ulBitsToClearOnEntry, uint32_t ulBitsToClearOnExit, uint32_t *pulNotificationValue, TickType_t xTicksToWait );

  • ulBitsToClearOnEntry: Clear those bits of the task notification value before function execution.
  • ulBitsToClearOnExit: Indicates that the bits of the task notification value are cleared before the function exits. Before clearing to 0, the received task notification value will first be saved to the formal parameter *pulNotificationValue.
  • pulNotificationValue: used to save the received task notification value. If it is not needed, just set it to NULL.
  • xTicksToWait: Maximum waiting time for message notification

4. Experiment

1. Simulate a binary semaphore (press button 1 to send a task notification, press button 2 to receive a task notification)

Code

void StartTaskSend(void const * argument)
{
  /* USER CODE BEGIN StartTaskSend */
  /* Infinite loop */
  for(;;)
  {
    if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == GPIO_PIN_RESET){
			osDelay(20);
			if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == GPIO_PIN_RESET){
				
				xTaskNotifyGive(TaskReceiveHandle);//发送任务通知,没有通知值
				printf("任务通知:模拟二值信号量发送成功!\r\n");
			}
			while(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == GPIO_PIN_RESET);
		}
		osDelay(10);
  }
  /* USER CODE END StartTaskSend */
}

/* USER CODE BEGIN Header_StartTaskReceive */
/**
* @brief Function implementing the TaskReceive thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_StartTaskReceive */
void StartTaskReceive(void const * argument)
{
  /* USER CODE BEGIN StartTaskReceive */
	uint32_t rev = 0;
  /* Infinite loop */
  for(;;)
  {
    if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_1) == GPIO_PIN_RESET){
			osDelay(20);
			if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_1) == GPIO_PIN_RESET){
				
				rev = ulTaskNotifyTake(pdTRUE,portMAX_DELAY);//获取任务通知值,并清空通知值
				if(rev != 0)
				printf("任务通知:模拟二值信号量接收成功!\r\n");
			}
			while(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_1) == GPIO_PIN_RESET);
		}
		osDelay(10);
  }
  /* USER CODE END StartTaskReceive */
}

 2. Analog counting semaphore (press button 1 to send task notification, press button 2 to receive task notification)

This is basically the same as simulating a binary semaphore. You only need to change the parameters of the ulTaskNotifyTake function to pdFALSE.

pdTRUE: Clear the notification value to zero (two-valued semaphore); pdFALSE: Decrease the notification value by one (counting semaphore);

 

void StartTaskReceive(void const * argument)
{
  /* USER CODE BEGIN StartTaskReceive */
	uint32_t rev = 0;
  /* Infinite loop */
  for(;;)
  {
    if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_1) == GPIO_PIN_RESET){
			osDelay(20);
			if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_1) == GPIO_PIN_RESET){
				
				rev = ulTaskNotifyTake(pdFALSE,portMAX_DELAY);
				if(rev != 0)
				printf("任务通知:模拟计数信号量接收成功! rev = %d\r\n",rev);
			}
			while(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_1) == GPIO_PIN_RESET);
		}
		osDelay(10);
  }
  /* USER CODE END StartTaskReceive */
}

 3. Simulation event flag group (press button 1 to set task notification value bit0 to 1, press button 2 to set task notification value bit1 to 1)

void StartTaskSend(void const * argument)
{
  /* USER CODE BEGIN StartTaskSend */
  /* Infinite loop */
  for(;;)
  {
    if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == GPIO_PIN_RESET){
			osDelay(20);
			if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == GPIO_PIN_RESET){
				
				printf("将bit0位置1!\r\n");
				xTaskNotify(TaskReceiveHandle,0x01,eSetBits);
			}
			while(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == GPIO_PIN_RESET);
		}
		if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_1) == GPIO_PIN_RESET){
			osDelay(20);
			if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_1) == GPIO_PIN_RESET){
				
				printf("将bit1位置1!\r\n");
				xTaskNotify(TaskReceiveHandle,0x02,eSetBits);
			}
			while(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_1) == GPIO_PIN_RESET);
		}
		
		osDelay(1);
  }
  /* USER CODE END StartTaskSend */
}

/* USER CODE BEGIN Header_StartTaskReceive */
/**
* @brief Function implementing the TaskReceive thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_StartTaskReceive */
void StartTaskReceive(void const * argument)
{
  /* USER CODE BEGIN StartTaskReceive */
	uint32_t notify_val = 0,event_bit =0;
	
  /* Infinite loop */
  for(;;)
  {
    xTaskNotifyWait(0,0xFFFFFFFF,&notify_val,portMAX_DELAY);
		if(notify_val & 0x01)
			event_bit |= 0x01;
		if(notify_val & 0x02)
			event_bit |= 0x02;
		if(event_bit == (0x01 | 0x02))
		{
			printf("任务通知模拟事件标志组接收成功!\r\n");
			event_bit = 0;
		}
		osDelay(1);
  }
  /* USER CODE END StartTaskReceive */
}

 4. Simulate mailbox (press button 1 to set the task notification value to 1, press button 1 to set the task notification value to 2)

void StartTaskSend(void const * argument)
{
  /* USER CODE BEGIN StartTaskSend */
  /* Infinite loop */
  for(;;)
  {
    if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == GPIO_PIN_RESET){
			osDelay(20);
			if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == GPIO_PIN_RESET){
				
				printf("按键1按下!\r\n");
				xTaskNotify(TaskReceiveHandle,1,eSetValueWithOverwrite);//将任务通知值设置为1
			}
			while(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == GPIO_PIN_RESET);
		}
		if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_1) == GPIO_PIN_RESET){
			osDelay(20);
			if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_1) == GPIO_PIN_RESET){
				
				printf("按键2按下!\r\n");
				xTaskNotify(TaskReceiveHandle,2,eSetValueWithOverwrite);//将任务通知值设置为2
			}
			while(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_1) == GPIO_PIN_RESET);
		}
		
		osDelay(1);
  }
  /* USER CODE END StartTaskSend */
}

/* USER CODE BEGIN Header_StartTaskReceive */
/**
* @brief Function implementing the TaskReceive thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_StartTaskReceive */
void StartTaskReceive(void const * argument)
{
  /* USER CODE BEGIN StartTaskReceive */
	uint32_t notify_val = 0;
	
  /* Infinite loop */
  for(;;)
  {
    xTaskNotifyWait(0,0xFFFFFFFF,&notify_val,portMAX_DELAY);
		printf("接收到的通知值为 %d\r\n",notify_val);
		osDelay(1);
  }
  /* USER CODE END StartTaskReceive */
}

Guess you like

Origin blog.csdn.net/m0_70987863/article/details/131902284