Semaphores can be used for resource management and task synchronization. Semaphores in FreeRTOS are divided into binary semaphores, computational semaphores, mutually exclusive semaphores and recursive mutually exclusive semaphores.
0x01 binary semaphore
Binary semaphore is actually a queue with only one queue item. This special queue is either full or empty. Tasks and interrupts don't care what messages are stored in the queue. They only need to know whether the queue is full or empty. This mechanism can be used to complete the synchronization between tasks and interrupts.
1. Create binary semaphore
vSemaphoreCreateBinary
vSemaphoreCreateBinary( SemaphoreHandle_t xSemaphore )
- xSemaphore: save the successfully created binary semaphore
This function is a binary semaphore function created by the old version of FreeRTOS, which is no longer used in the new version.
xSemaphoreCreateBinary:
SemaphoreHandle_t xSemaphoreCreateBinary( void )
This function is used uniformly in the new version of FreeRTOS to create binary semaphores. If this function is used to create binary semaphores, the memory required by semaphores is dynamically allocated by the memory management part of FreeRTOS. The binary semaphores created by this function are empty by default, that is, the binary semaphores created by this function cannot be obtained by using the function xsemaphotoretask.
xSemaphoreCreateBinaryStatic
SemaphoreHandle_t xSemaphoreCreateBinaryStatic( StaticSemaphore_t *pxSemaphoreBuffer )
- pxSemaphoreBuffer: save semaphore structure
The memory required by this function to create binary semaphores needs to be allocated by the user
2. Release semaphore
xSemaphoreGive
xSemaphoreGive( SemaphoreHandle_t xSemaphore )
- xSemaphore: semaphore handle to release
This function releases binary semaphores, count semaphores, or mutually exclusive semaphores
xSemaphoreGiveFromISR
xSemaphoreGiveFromISR( SemaphoreHandle_t xSemaphore, BaseType_t *pxHigherPriorityTaskWoken )
- xSemaphore: semaphore handle to release
- BaseType_t *pxHigherPriorityTaskWoken: indicates whether to perform task switching after exiting this function. When this value is pdTRUE, task switching must be performed before exiting the interrupt
This function can only be used to release binary semaphores and count semaphores
3. Acquire semaphore
xSemaphoreTake
xSemaphoreTake( * SemaphoreHandle_t xSemaphore, * TickType_t xBlockTime * )
- SemaphoreHandle_ T xssemaphore: handle to get semaphore
- TickType_t xBlockTime: blocking time
This function is used to obtain binary semaphores, count semaphores, or mutually exclusive semaphores
xSemaphoreTakeFromISR
xSemaphoreTakeFromISR( SemaphoreHandle_t xSemaphore, BaseType_t *pxHigherPriorityTaskWoken )
- SemaphoreHandle_ T xssemaphore: handle to get semaphore
- BaseType_t *pxHigherPriorityTaskWoken: marks whether to switch tasks after exiting this function, which means that pdTURE must switch tasks
4. Verification
Design an experiment to control LED1 and BEEP switches on the development version by sending specified instructions through the serial port. The instructions are as follows:
LED1ON: open LED1
LED1OFF: close LED1
BEEFON: open BEEF
BEEFOFF: turn off BEEF
Three tasks are designed with the following functions:
start_task: used to create two other tasks
task1_task: control LED0 to flash, indicating that the system is running
DataProcess_task: instruction processing task, which controls different peripherals according to the received instructions
In the experiment, a binary semaphore is also created to complete the serial port interrupt and dataprocess task_ Synchronization between tasks.
DataProcess_task function
void DataProcess_task(void *pvParameters) { u8 len = 0; u8 CommandValue = COMMANDERR; BaseType_t err = pdFALSE; u8 *CommandStr; POINT_COLOR = BLUE; while(1) { if(BinarySemaphore!=NULL) { err=xSemaphoreTake(BinarySemaphore,portMAX_DELAY); //Get semaphore if(err==pdTRUE) //Semaphore acquisition succeeded { len=USART_RX_STA&0x3fff; //Get the length of the data received this time CommandStr=mymalloc(SRAMIN,len+1); //Request memory sprintf((char*)CommandStr,"%s",USART_RX_BUF); CommandStr[len]='\0'; //Add end of string symbol LowerToCap(CommandStr,len); //Convert string to uppercase CommandValue=CommandProcess(CommandStr); //Command parsing if(CommandValue!=COMMANDERR) { LCD_Fill(10,90,210,110,WHITE); //Clear display area LCD_ShowString(10,90,200,16,16,CommandStr); //Display commands on LCD printf("Command is:%s\r\n",CommandStr); switch(CommandValue) //Processing command { case LED1ON: LED1=0; break; case LED1OFF: LED1=1; break; case BEEPON: BEEP=0; break; case BEEPOFF: BEEP=1; break; } } else { printf("Invalid command, please re-enter!!\r\n"); } USART_RX_STA=0; memset(USART_RX_BUF,0,USART_REC_LEN); //Serial port receive buffer reset myfree(SRAMIN,CommandStr); //Free memory } } else if(err==pdFALSE) { vTaskDelay(10); //Delay 10ms, that is, 10 clock beats } } }
DataProcess_task is used to apply for obtaining semaphores. If the application is received, it will be executed next. If nothing happens, it will be blocked all the time. After the application is received, the data will be processed and the corresponding response will be made.
Serial port 1 interrupt service program
//Serial port 1 interrupt service program void USART1_IRQHandler(void) { u32 timeout=0; u32 maxDelay=0x1FFFF; BaseType_t xHigherPriorityTaskWoken; HAL_UART_IRQHandler(&UART1_Handler); //Call HAL library interrupt handling common function timeout=0; while (HAL_UART_GetState(&UART1_Handler) != HAL_UART_STATE_READY)//Waiting ready { timeout++;timeout handler if(timeout>maxDelay) break; } timeout=0; while(HAL_UART_Receive_IT(&UART1_Handler, (u8 *)aRxBuffer, RXBUFFERSIZE) != HAL_OK)//After one processing, restart the interrupt and set RxXferCount to 1 { timeout++; //timeout handler if(timeout>maxDelay) break; } //Release binary semaphore if((USART_RX_STA&0x8000)&&(BinarySemaphore!=NULL))//The data is received and the binary semaphore is valid { xSemaphoreGiveFromISR(BinarySemaphore,&xHigherPriorityTaskWoken); //Release binary semaphore portYIELD_FROM_ISR(xHigherPriorityTaskWoken);//Make a task switch if necessary } }
The serial port 1 interrupt service program is used to receive the data sent by the serial port. If the reception is completed, the binary semaphore will be released, dataprocess_ The task can only be executed downward. If data is not received or data is not received, DataProcess_task will always block.