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); } }