FreeRTOS (binary semaphore)

The information comes from Hardware Home:Information summary - FreeRTOS real-time operating system course (multi-task management)

Table of contents

1. The concept of semaphore

1. Basic concept of semaphore

2. Classification of semaphores

2. Definition and application of binary semaphore

1. Definition of binary semaphore

2. Application of binary semaphore

3. Operating mechanism of binary semaphore

1. Implementation of binary semaphore between FreeRTOS tasks

2. Implementation of binary semaphore in FreeRTOS interrupt mode

4. Common API functions of binary semaphores

1. Typical process and API for using binary semaphores

2. Create and delete binary semaphores

3. Release of binary semaphores in tasks and interrupts

5. Binary semaphore programming

 1. Semaphore creation

2. Avoid serial port mis-synchronization

3. Task releases semaphore

 4. The task obtains the semaphore

5. Interrupt releases semaphore

6. Obtain the semaphore released by the interrupt in the task


1. The concept of semaphore

1. Basic concept of semaphore

Message queue is a data structure that implements communication between tasks or tasks and interrupts. Analogy to arrays in bare metal programming

Semaphore is a mechanism for realizing communication between tasks or tasks and interrupts, which can be compared Flag bits in bare metal programming

Semaphore (semaphore) can realize the synchronization function between tasks and tasks or tasks and interrupts(binary semaphore), a>Resource management (counting semaphore), Mutually exclusive access to critical resources (mutually exclusive semaphore)< a i=6>etc

The semaphore is a non-negative positive number,The value range of binary semaphores and mutually exclusive semaphores is 0-1,< /span>The value range of the counting semaphore is 0-N(N>1)

0:The semaphore is empty, and all tasks trying to obtain it will be blocked until a timeout exits or other tasks release the semaphore

Positive number:Indicates that there are one or more semaphores for acquisition

2. Classification of semaphores

Binary semaphore (synchronous application)

Counting semaphores (resource management)

Mutually exclusive semaphore (mutually exclusive access)

Recursive mutex semaphore (just understand it briefly)

2. Definition and application of binary semaphore

1. Definition of binary semaphore

When the semaphore is acquired, the semaphore value becomes 0; when the semaphore is released, the semaphore value becomes 1. This kind of semaphore with only two values ​​​​of 0 and 1 is called a binary semaphore. When creating a binary semaphore, the system will allocate memory for the created binary semaphore.

Binary semaphore is a special message queue with length 1 and message size 0. Because this queue only has two states: empty or full, and the message size is 0, when using it, you only need to know whether there is a message in the queue, without paying attention to what the message is.

2. Application of binary semaphore

In embedded operating systems, binary semaphores are an important means of synchronizing between tasks or tasks and interrupts. Binary semaphore can also be used to access critical resources, but it is not recommended because of the existence of task priority flipping Question.

Application scenarios for synchronization between tasks and tasks:

Suppose there is a temperature and humidity sensor that collects data every 1 second, and then lets it display the data on the LCD screen. This period is also 1 second. If the LCD screen refresh period is 100ms, then the temperature and humidity data at this time have not been updated, and the LCD screen There is no need to refresh at all. It only needs to be refreshed when the temperature and humidity data are updated after 1 second. Otherwise, the CPU will perform multiple invalid data update operations in vain, resulting in a waste of CPU resources. If the refresh cycle of the LCD screen is 10 seconds, then the temperature and humidity data have changed 10 times before the LCD screen updates the data, then the measured results of this product will be inaccurate, so synchronization and coordination are still required to collect the temperature and humidity data. After completion, refresh the LCD screen data so that the results obtained are the most accurate and CPU resources will not be wasted.

Application scenarios for synchronization between tasks and interrupts:

In serial port reception, we don't know when data will be sent, but if we set up a task to specifically query whether data has arrived, it will waste CPU resources, so using a binary semaphore is a good idea in this case. : When no data arrives, the task enters the blocking state and does not participate in task scheduling; when the data arrives, a binary semaphore is released, and the task is immediately released from the blocking state, enters the ready state, and then processes the data at runtime. In this way, the system resources will be well utilized.

3. Operating mechanism of binary semaphore

1. Implementation of binary semaphore between FreeRTOS tasks

The implementation of binary semaphores between tasks refers to the use of semaphores between tasks to realize the synchronization function of tasks.

Operating conditions:

Create 2 tasks Task1 and Task2.​ 

The default initial value for creating a binary semaphore is 0, which means there are no available resources.

The running process is described as follows:

 During the running process, task Task1 calls the function xSemaphoreTake to obtain the semaphore resource. However, since the initial value of creating a binary signal is 0, there is no semaphore available, and task Task1 will move from the running state to the blocking state. During the running process, task Task2 releases the semaphore through the function xSemaphoreGive, and task Task1 enters the ready state from the blocking state, and then from the ready state to the running state under the action of the scheduler, realizing the synchronization function of Task1 and Task2.

2. Implementation of binary semaphore in FreeRTOS interrupt mode

Operating conditions:

Create a task Task1 and a serial port receiving interrupt.​ 

The initial value of the binary semaphore is 0, the serial port interrupt calls the function xSemaphoreGiveFromISR to release the semaphore, and task Task1 calls the function xSemaphoreTake to obtain the semaphore resource.

The running process is described as follows:

 Task Task1 calls the function xSemaphoreTake during the running process. Since the initial value of the semaphore is 0 and no semaphore resources are available, Task1 enters the blocking state from the running state.

When Task1 is blocked, the serial port receives data and enters the serial port interrupt service program. The function xSemaphoreGiveFromISR is called in the serial port interrupt service program to release the semaphore resource. The semaphore value is increased by 1. At this time, the semaphore count value is 1, and task Task1 is The blocking state enters the ready state, and then enters the running state from the ready state under the action of the scheduler. After Task1 obtains the semaphore, the semaphore value is reduced by 1, and the semaphore count value becomes 0 again.​ 

When the cycle is executed again, Task1 calls the function xSemaphoreTake and enters the suspended state again because no resources are available, waiting for the serial port to release the binary semaphore resource, and so on.

In practical applications, the following four issues should be paid attention to when using the interrupt message mechanism:

 The shorter the execution time of the interrupt function, the better, to prevent other exceptions lower than this interrupt priority from not being responded to in time.

 In practical applications, it is recommended not to implement message processing in interrupts. Users can send messages to notify tasks in the interrupt service program and implement message processing in the task. This can effectively ensure the real-time response of the interrupt service program. At the same time, this task also needs to be set to a high priority so that the task can be executed in time after exiting the interrupt function.

 The interrupt service routine must call the function dedicated to setting the binary semaphore, that is, the function ending with FromISR

 If the FreeRTOS binary semaphore API function is called in the interrupt function of the FreeRTOS project, it is necessary to check whether a high-priority task is ready when exiting. If there is a ready task, task switching needs to be performed after exiting the interrupt.

4. Common API functions of binary semaphores

1. Typical process and API for using binary semaphores

> Create a binary semaphore xSemaphoreCreateBinary()

> Release binary semaphore xSemaphoreGive() and xSemaphoreGiveFromISR() 

> Get binary semaphore xSemaphoreTake()

> Delete binary semaphore vSemaphoreDelete()

2. Create and delete binary semaphores

Binary semaphore control block (handle): The handle of the binary semaphore is the handle of the message queue, because the binary semaphore is a special message queue with a length of 1 and a message size of 0

Binary semaphore creation

Function prototype: SemaphoreHandle_t xSemaphoreCreateBinary(void)

Function description:The function xSemaphoreCreateBinary is used to create a binary semaphore. 

 Return value. If the creation is successful, the handle of the binary semaphore will be returned. If the required space cannot be provided for the binary semaphore due to insufficient heap size in the FreeRTOSConfig.h file, NULL will be returned.

This function is implemented based on the message queue function:

picture

 Application examples

picture

 Binary semaphore deletion

Function prototype: void vSemaphoreDelete(void)

Function description: The function vSemaphoreDelete can be used to delete a binary semaphore.​ 

3. Release of binary semaphores in tasks and interrupts

Binary semaphore release in task

Function prototype: xSemaphoreGive(SemaphoreHandle_t xSemaphore); /* Semaphore handle */

Function description: The function xSemaphoreGive is used to release the semaphore in the task code.

 The first parameter is the semaphore handle.

 Return value, if the semaphore is released successfully, it returns pdTRUE, otherwise it returns pdFALSE, because the implementation of the semaphore is based on the message queue, and the main reason for failure to return is that the message queue is full.

Please pay attention to the following issues when using this function:

1. This function is used to be called in the task code, so this function cannot be called in the interrupt service program. xSemaphoreGiveFromISR is used in the interrupt service program.

2. Before using this function, be sure to create a semaphore using the function xSemaphoreCreateBinary(), xSemaphoreCreateMutex() or xSemaphoreCreateCounting().

3. This function does not support semaphores created using xSemaphoreCreateRecursiveMutex().

Release of binary semaphore during interrupt

Function prototype:xSemaphoreGiveFromISR ( SemaphoreHandle_t xSemaphore, /* Semaphore handle */

    signed BaseType_t *pxHigherPriorityTaskWoken /* Save the status of whether the high-priority task is awakened */ )

Function description: The function xSemaphoreGiveFromISR is used to release the semaphore in the interrupt service program.

 The first parameter is the semaphore handle.

 The second parameter is used to save whether a high-priority task is ready. If the value of this parameter is pdTRUE after the function is executed, it means that there is a high-priority task to be executed, otherwise there is not.

 Return value, if the semaphore is released successfully, pdTRUE is returned, otherwise errQUEUE_FULL is returned.

Please pay attention to the following issues when using this function:

1. This function is implemented based on the message queue function xQueueGiveFromISR: #define xSemaphoreGiveFromISR( xSemaphore, pxHigherPriorityTaskWoken ) \xQueueGiveFromISR( ( QueueHandle_t ) ( xSemaphore ), ( pxHigherPriorityTaskWoken ) )

2. This function is used to be called in the interrupt service routine, so this function cannot be called in the task code. xSemaphoreGive is used in the task code.

3. Before using this function, be sure to create a semaphore using the function xSemaphoreCreateBinary() or xSemaphoreCreateCounting().

4. This function does not support semaphores created using xSemaphoreCreateMutex ().

5. Binary semaphore programming

 1. Semaphore creation

2. Avoid serial port mis-synchronization

When the FreeRTos code generated by STM32Cube creates a binary semaphore, the default is 1. Release it here to avoid mis-synchronization of the serial port.

  xSemaphoreTake(myBinarySem01Handle,0);//STM32CubeMX生成的FreeRTos代码创建二值信号量时,默认为1,此处释放,避免串口误同步
  xSemaphoreTake(myBinarySemISRHandle,0);//STM32CubeMX生成的FreeRTos代码创建二值信号量时,默认为1,此处释放,避免串口误同步

3. Task releases semaphore

void ReleaseSem_Task(void const * argument)
{
  /* USER CODE BEGIN ReleaseSem_Task */
	BaseType_t xResult;
	uint16_t GiveCnt=0;   //释放计数
	char buff[50];
  /* Infinite loop */
  for(;;)
  {
	HAL_UART_Transmit(&huart2, (uint8_t *)"发送同步信号!!! \r\n",18, HAL_MAX_DELAY);
	xResult=xSemaphoreGive(myBinarySem01Handle);
	if(xResult==pdTRUE)
	{
		sprintf(buff,"成功发送二值信号量同步信号,次数 = %u \r\n",++GiveCnt);
		HAL_UART_Transmit(&huart2, (uint8_t *)buff, strlen(buff), HAL_MAX_DELAY);
	}
	else
	{
		HAL_UART_Transmit(&huart2, (uint8_t *)"发送同步信号失败 \r\n\r\n", 17, HAL_MAX_DELAY);
	}
    osDelay(1000);
  }
  /* USER CODE END ReleaseSem_Task */
}

 4. The task obtains the semaphore

void BinarySem_Syn_Task(void const * argument)
{
  /* USER CODE BEGIN BinarySem_Syn_Task */
	BaseType_t xResult;
	uint16_t TakeCnt=0;   //获取计数
	char buff[50];
  /* Infinite loop */
  for(;;)
  {
	HAL_UART_Transmit(&huart2,(uint8_t *)"等待同步信号,无限等待 \r\n", 25, HAL_MAX_DELAY);
	xResult=xSemaphoreTake(myBinarySem01Handle,portMAX_DELAY);
	if(xResult==pdTRUE)
	{
		sprintf(buff,"成功接收到二值信号量同步信号,次数 = %u \r\n\r\n",++TakeCnt);
		HAL_UART_Transmit(&huart2, (uint8_t *)buff, strlen(buff), HAL_MAX_DELAY);
	}
  }
  /* USER CODE END BinarySem_Syn_Task */
}

5. Interrupt releases semaphore

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	BaseType_t xHigherPriorityTaskWoken =pdTRUE;
	if(huart->Instance==huart2.Instance)
	{
		xSemaphoreGiveFromISR(myBinarySemISRHandle,&xHigherPriorityTaskWoken);
		//如果有高优先级任务就绪,执行一次任务切换
		portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
	}
}

6. Obtain the semaphore released by the interrupt in the task

void BinarySemSyneISR_Task(void const * argument)
{
  /* USER CODE BEGIN BinarySemSyneISR_Task */
	BaseType_t xResult;
	uint16_t TakeCnt=0;   //获取计数
	char buff[50];
	char rxBuff[10];
  /* Infinite loop */
	for (;;)
	{
		HAL_UART_Receive_IT(&huart2, (uint8_t*) rxBuff, strlen(rxBuff));

		HAL_UART_Transmit(&huart2, (uint8_t*) "等待串口中断同步信号,无限等待 \r\n", 21,
		HAL_MAX_DELAY);
		xResult = xSemaphoreTake(myBinarySemISRHandle, portMAX_DELAY);
		if (xResult == pdTRUE)
		{
			sprintf(buff, "成功接收到二值信号量同步信号,次数 = %u \r\n\r\n", ++TakeCnt);
			HAL_UART_Transmit(&huart2, (uint8_t*) buff, strlen(buff),HAL_MAX_DELAY);
			sprintf(buff, "成功接收到串口数据: %s \r\n\r\n", rxBuff);
			HAL_UART_Transmit(&huart2, (uint8_t*) buff, strlen(buff),HAL_MAX_DELAY);
		}
	}
  /* USER CODE END BinarySemSyneISR_Task */
}

Guess you like

Origin blog.csdn.net/qq_57594025/article/details/132230928