FreeRTOS communication mode

Posted by Sandip on Tue, 14 Dec 2021 01:13:47 +0100

1, Message queue

Message queue is a data structure commonly used for inter task communication. Queue can transfer information between tasks, interrupts and tasks. Both read and write queues support timeout mechanism.

1. Create queue

QueueHandle_t xQueueCreate( UBaseType_t uxQueueLength,//queue length 
							UBaseType_t uxItemSize );//The size of the message unit in the queue, in bytes

2. Delete queue

vQueueDelete()

3. Queue send

BaseType_t xQueueSend(QueueHandle_t xQueue,//Queue handle
				const void * pvItemToQueue,//Pointer to the queue message to be sent to the end of the queue
				TickType_t xTicksToWait);//waiting time

4. Send function used in interrupt service program

BaseType_t xQueueSendFromISR(QueueHandle_t xQueue,
							const void *pvItemToQueue,
						    BaseType_t *pxHigherPriorityTaskWoken//Is an optional parameter that can be set to NULL.
						    );

5. Send message to queue header

BaseType_t xQueueSendToFront( QueueHandle_t xQueue,
						const void * pvItemToQueue,
							TickType_t xTicksToWait );

6. Used to send a message to the head of the message queue in the interrupt service program

BaseType_t xQueueSendToFrontFromISR(QueueHandle_t xQueue,
							const void *pvItemToQueue,
				BaseType_t *pxHigherPriorityTaskWoken);

7. Receive queue
Receive messages from a queue and delete messages from the queue

BaseType_t xQueueReceive(QueueHandle_t xQueue,
							void *pvBuffer,
					TickType_t xTicksToWait);

8. Receive in interrupt

xQueueReceiveFromISR()

9. Message queuing application instance

QueueHandle_t Test_Queue =NULL;
#define  QUEUE_ Len 4 / * message queue length*/
#define  QUEUE_ Size 4 / * size of each message*/

/
static void AppTaskCreate(void)
{
  BaseType_t xReturn = pdPASS;
taskENTER_CRITICAL();     //Enter critical zone
  /* Create Test_Queue */
  Test_Queue = xQueueCreate((UBaseType_t ) QUEUE_LEN,(UBaseType_t ) QUEUE_SIZE);
  
 Create task 1: Send_Task
 Create task 2: Receive_Task

  vTaskDelete(AppTaskCreate_Handle); 
  taskEXIT_CRITICAL();  
}

/
  
//Task 1: send
static void Send_Task(void* parameter)
{	 
  BaseType_t xReturn = pdPASS;
  uint32_t send_data1 = 1;
  uint32_t send_data2 = 2;
  while (1)
  {
      xReturn = xQueueSend( Test_Queue,&send_data1,0 );       
      if(pdPASS == xReturn)
        printf("send_data1 Sent successfully!\n\n");
    vTaskDelay(20);
  }
}

//Task 2: receiving
static void Receive_Task(void* parameter)
{	
  BaseType_t xReturn = pdTRUE;
  uint32_t r_queue;
  while (1)
  {
    xReturn = xQueueReceive( Test_Queue, &r_queue,portMAX_DELAY); 
    if(pdTRUE == xReturn)
      printf("Data received%d\n\n",r_queue);
    else
      printf("No data received 0 x%lx\n",xReturn);
  }
}                             

2, Semaphore

Achieve synchronization between tasks or mutually exclusive access to critical resources

1. Create binary semaphore

 xSemaphoreCreateBinary()

2. Create count semaphore

SemaphoreHandle_t xSemaphoreCreateCounting( UBaseType_t uxMaxCount,//Maximum value of count semaphore
 UBaseType_t uxInitialCount//Creates the initial value of the count semaphore
 );

3. Semaphore deletion function

vSemaphoreDelete()

4. Semaphore release function

The released semaphore object must be created and can be used to release binary semaphores, count semaphores and mutexes, but the recursive mutex created by the function xsemaphotorecreaterecursivemutex() cannot be released. In addition, this function cannot be used in interrupts

xSemaphoreGive( xSemaphore ) 

5. Release semaphore in interrupt
Used to release a semaphore with interrupt protection. It cannot release mutexes because they are mutexes
Cannot be used in interrupts.

xSemaphoreGiveFromISR()

6. Semaphore acquisition
Get a semaphore, which can be binary semaphore, count semaphore and mutual exclusion semaphore

xSemaphoreTake( xSemaphore, xBlockTime )

Parameter: xSemaphore: semaphore handle; xBlockTime: the maximum timeout for waiting for semaphores to be available.

7. Function to get semaphores without blocking mechanism
Cannot be used for mutexes

xSemaphoreTakeFromISR()

8. Semaphore application example

Create two tasks, one is to obtain semaphores and the other is to release mutexes

The semaphore acquisition task is always waiting for semaphores, and its waiting time is portMAX_DELAY, wait until the semaphore is obtained, and the task starts to execute the task code.

SemaphoreHandle_t BinarySem_Handle =NULL;

static void Receive_Task(void* parameter)
{	
  BaseType_t xReturn = pdPASS;
  while (1) {
  	//Get the binary semaphore. If not, wait all the time
	xReturn = xSemaphoreTake(BinarySem_Handle,portMAX_DELAY); 
    if(pdTRUE == xReturn)
      printf("BinarySem_Handle  get successful |!\n\n");
  }
}


static void Send_Task(void* parameter)
{	 
  BaseType_t xReturn = pdPASS;
  while (1)
  {
    if( Key_Scan(KEY1_GPIO_PORT,KEY1_GPIO_PIN) == KEY_ON )
    {
      xReturn = xSemaphoreGive( BinarySem_Handle );//Give semaphore
      if( xReturn == pdTRUE )
        printf("BinarySem_Handle  Release successful\r\n");
      else
        printf("BinarySem_Handle  Release failed\r\n");
    } 
    vTaskDelay(20);
  }
}

static void AppTaskCreate(void)
{
  BaseType_t xReturn = pdPASS;
  taskENTER_CRITICAL(); 
  
  /* Create binary semaphore*/
  BinarySem_Handle = xSemaphoreCreateBinary();	 
  if(NULL != BinarySem_Handle)
    printf("BinarySem_Handle create successful!\r\n");

	Create task 1: Receive_Task
	Create task 2: Send_Task
  
  vTaskDelete(AppTaskCreate_Handle); 
  taskEXIT_CRITICAL();  
}

3, Mutex

It supports mutex ownership, recursive access and preventing priority reversal. It is used to realize the exclusive processing of critical resources and to protect the interlocking of resources. Cannot be used in interrupt functions

Difference from semaphore
Binary semaphore: used to achieve synchronization (between tasks or between tasks and interrupts)

Application scenario
Conditions that may cause priority reversal

Recursive mutexes are more suitable for:
When the task may get the mutex multiple times. This can avoid the deadlock caused by multiple recursive holding of the same task.

1,Mutex creation ,It can only be obtained by the same task once
xSemaphoreCreateMutex()

2,Recursive mutex creation, It can be obtained many times by the same task, and it needs to be released as many times as it is obtained
xSemaphoreCreateRecursiveMutex()

3,Mutex deletion
vSemaphoreDelete()

4,Mutex acquisition
xSemaphoreTake()

5,Recursive mutex acquisition function
xSemaphoreTakeRecursive( xMutex, xBlockTime )

6,Mutex release
xSemaphoreGive()

7,Recursive mutex release
xSemaphoreGiveRecursive()

8. Mutex application example
Experiments show that when low priority tasks are running, medium priority tasks can not preempt low priority tasks.

SemaphoreHandle_t MuxSem_Handle =NULL;

static void AppTaskCreate(void)
{
  BaseType_t xReturn = pdPASS;/
  
  taskENTER_CRITICAL();  
  /* MuxSem */
  MuxSem_Handle = xSemaphoreCreateMutex();	 
  if(NULL != MuxSem_Handle)
    printf("MuxSem_Handle Created successfully¦!\r\n");

  xReturn = xSemaphoreGive( MuxSem_Handle );//Give semaphore

	Create task 1: LowPriority_Task
	Create task 2: MidPriority_Task
	Create task 3: HighPriority_Task

  vTaskDelete(AppTaskCreate_Handle); 
  
  taskEXIT_CRITICAL(); 
}


static void LowPriority_Task(void* parameter)
{	
  static uint32_t i;
  BaseType_t xReturn = pdPASS;
  while (1)
  {
  	//Get semaphore, wait if there is no semaphore
  	printf("LowPriority_Task Get mutex\n");
	xReturn = xSemaphoreTake(MuxSem_Handle,
                              portMAX_DELAY); 
    if(pdTRUE == xReturn)
  	  printf("LowPriority_Task Running\n\n");
    
    for(i=0;i<2000000;i++)//Occupy low-level task mutex
	{
			taskYIELD();//dispatch
	}
    
    printf("LowPriority_Task Release semaphore!\r\n");
    xReturn = xSemaphoreGive( MuxSem_Handle );//Release semaphore

    vTaskDelay(1000);
  }
}

static void MidPriority_Task(void* parameter)
{	 
  while (1)
  {
   printf("MidPriority_Task Running\n");
   vTaskDelay(1000);
  }
}

static void HighPriority_Task(void* parameter)
{	
  BaseType_t xReturn = pdTRUE;
  while (1)
  {
	xReturn = xSemaphoreTake(MuxSem_Handle,
                              portMAX_DELAY); 
    printf("HighPriority_Task  Get mutex!\r\n");                          
    if(pdTRUE == xReturn)
      printf("HighPriority_Task Running\n");

    printf("HighPriority_Task Release mutex!\r\n");
    xReturn = xSemaphoreGive( MuxSem_Handle );//Give mutex
    vTaskDelay(1000);
  }
}

Output printing

HighPriority_Task Get mutex
HighPriority_Task Running
HighPriority_Task Release mutex!

MidPriority_Task Running

LowPriority_Task Get mutex
LowPriority_Task Running
LowPriority_Task Release mutex!

HighPriority_Task Get mutex

4, Events

Events can be used as flag bits to judge whether some events have occurred.

1,Event creation function xEventGroupCreate()

2,Event deletion function vEventGroupDelete()

3,Event set function xEventGroupSetBits()((task)

4,Event set function xEventGroupSetBitsFromISR()((interrupt)

5,Wait event function xEventGroupWaitBits()

6,xEventGroupClearBits()And xEventGroupClearBitsFromISR()

Wait event function

EventBits_t xEventGroupWaitBits(const EventGroupHandle_t xEventGroup,
									 const EventBits_t uxBitsToWaitFor,//Value by bit or
									 const BaseType_t xClearOnExit,//The pdRTUE system will clear the event flag bit specified by the formal parameter uxBitsToWaitFor
									 const BaseType_t xWaitForAllBits,//pdFALSE: any one of the bits specified by uxBitsToWaitFor is set; Pdtrue uxBitsToWaitFor when the specified bits are set 
									 TickType_t xTicksToWait );//waiting time

Set function

EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup,
								 const EventBits_t uxBitsToSet );//Flag bit of the event

7. Event application instance
Create two tasks, one is to set the event task, and the other is to wait for the event task

Set the event task to set different event flag bits by detecting the press of the key,
When waiting for an event, the task obtains the two event flag bits and judges whether both events occur. If so, the corresponding information is output

static EventGroupHandle_t Event_Handle =NULL;

#define KEY1_EVENT  (0x01 << 0) 
#define KEY2_EVENT  (0x01 << 1) 

static void AppTaskCreate(void)
{
  BaseType_t xReturn = pdPASS;
  taskENTER_CRITICAL(); 
  
  /* Create eventevent_ Handle */
  Event_Handle = xEventGroupCreate();	 
  if(NULL != Event_Handle)
    printf("Event_Handle Created successfully\r\n");
    
	Create task 1: LED_Task
	Create task 2: KEY_Task

  vTaskDelete(AppTaskCreate_Handle); 
  taskEXIT_CRITICAL(); 
}

static void LED_Task(void* parameter)
{	
    EventBits_t r_event;
    while (1)
	{
    r_event = xEventGroupWaitBits(Event_Handle, 
                                  KEY1_EVENT|KEY2_EVENT,//event
                                  pdTRUE,pdTRUE,  portMAX_DELAY);
                        
    if((r_event & (KEY1_EVENT|KEY2_EVENT)) == (KEY1_EVENT|KEY2_EVENT)) 
    {
      printf ( "Receive event\n");		
    }
    else
      printf ( "Event error\n");	
  }
}

static void KEY_Task(void* parameter)
{	 
  while (1)
  {
       if( Key_Scan(KEY1_GPIO_PORT,KEY1_GPIO_PIN) == KEY_ON )  {
			xEventGroupSetBits(Event_Handle,KEY1_EVENT); //Trigger event 1 					
		}
   
		if( Key_Scan(KEY2_GPIO_PORT,KEY2_GPIO_PIN) == KEY_ON )  {
			xEventGroupSetBits(Event_Handle,KEY2_EVENT); Trigger event 2				
		}
		vTaskDelay(20); 
  }
}

5, Notice

Each task has a 32-bit notification value. In most cases, task notification can replace binary semaphores, count semaphores, event groups, or queues with a length of 1; There is no need to create a queue for the use of task notification;

Send task notification function 
xTaskGenericNotify()

Get task notification 
BaseType_t xTaskNotify( TaskHandle_t xTaskToNotify, //task handle 
                                      uint32_t ulValue, //value
                                      eNotifyAction eAction ); 
     eNotifyAction Value of                                 
       * eNoAction = 0//Notify the task without updating its notification value
       * eSetBits//Set the value in the task notification value
       * eIncrement//Increase the channel value of the task
       * eSetvaluewithoverwrite//Overwrite current notification
       * eSetValueWithoutoverwrite//Do not overwrite the current notification

Waiting for task notification
BaseType_t xTaskNotifyWait( uint32_t ulBitsToClearOnEntry,//
							uint32_t ulBitsToClearOnExit,
							uint32_t *pulNotificationValue,//value
							TickType_t xTicksToWait );//event

1. Notification application instance (instead of message queue)

static void AppTaskCreate(void)
{
  BaseType_t xReturn = pdPASS;
  taskENTER_CRITICAL(); 

  Create task 1: Receive1_Task
  Create task 2: Send_Task
  
  vTaskDelete(AppTaskCreate_Handle);
  taskEXIT_CRITICAL();  
}

//Receive task
static void Receive1_Task(void* parameter)
{	
  BaseType_t xReturn = pdTRUE;
  uint32_t r_num;
  xReturn=xTaskNotifyWait(0x0,			//The task bit is not cleared when entering the function
                            ULONG_MAX,	  //Clear all bit s when exiting the function
                            (uint32_t *)&r_char,		  //Task notification value
                            portMAX_DELAY);	//Blocking event
    if( pdTRUE == xReturn )
      printf("Receive1_Task Task notification message %d \n",r_num);                      
}
//Send task
static void Send_Task(void* parameter)
{	 
  BaseType_t xReturn = pdPASS;
  uint32_t send1 = 1;
  
  while (1)
  {

    if( Key_Scan(KEY1_GPIO_PORT,KEY1_GPIO_PIN) == KEY_ON )
    {

      xReturn = xTaskNotify( Receive1_Task_Handle, /*task handle */
                             (uint32_t)&test_str1, /*Task content */
                             eSetValueWithOverwrite );/*Overwrite current notification*/
      
      if( xReturn == pdPASS )
        printf("Receive1_Task_Handle ÈÎÎñ֪ͨÏûÏ¢·¢Ëͳɹ¦!\r\n");
    } 
    vTaskDelay(20);
  }
}

2. Notification application instance (instead of semaphore)

static void Receive1_Task(void* parameter)
{	
  while (1)
  {
  	//Get the task notification. If not, wait all the time
    ulTaskNotifyTake(pdTRUE,portMAX_DELAY);
    printf("Receive1_Task !\n\n");
  }
}

static void Send_Task(void* parameter)
{	 
  BaseType_t xReturn = pdPASS;
  while (1)
  {
    if( Key_Scan(KEY1_GPIO_PORT,KEY1_GPIO_PIN) == KEY_ON )
    {
      xReturn = xTaskNotifyGive(Receive1_Task_Handle);//task handle 
      if( xReturn == pdTRUE )
        printf("Receive1_Task_Handle ÈÎÎñ֪ͨ·¢Ëͳɹ¦!\r\n");
    } 
    vTaskDelay(20);
  }
}

Topics: FreeRTOS