FreeRTOS semaphore --- binary semaphore

Posted by Saethyr on Fri, 26 Nov 2021 13:09:45 +0100

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.

Topics: Single-Chip Microcomputer stm32 ARM FreeRTOS