FreeRTOS学习——信号量

什么是信号量?

信号量(Semaphore),是在多任务环境下 使用的一种机制,是可以用来保证两个或多个关键代码段不被并发调用。

信号量这个名字,我们可以把它拆分来看,信号可以起到通知信号的作用,然后我们的量还可以用来表示资源的数量,当我们的量只有0和1的时候,它就可以被称作二值信号量,只有两个状态,当我们的那个量没有限制的时候,它就可以被称作为计数型信号量。

信号量也是队列的一种。

1、二值信号量

1.1、什么是二值信号量

二值信号量 其实就是一个长度为1,大小为零的队列,只有0和1两种状态 ,通常情况下,我们用它来进行互斥访问或任务同步

互斥访问:比如门钥匙,只有获取到钥匙才可以开门。

任务同步:比如我录完视频你才可以看视频。

1.2、二值信号量相关 API 函数

1.2.1、创建二值信号量

cpp 复制代码
SemaphoreHandle_t xSemaphoreCreateBinary( void )

参数:

返回值:

成功 ,返回对应二值信号量的句柄; 失败,返回 NULL 。

1.2.2、释放二值信号量

cpp 复制代码
BaseType_t xSemaphoreGive( SemaphoreHandle_t xSemaphore )

参数:

xSemaphore:要释放的信号量句柄

返回值:

成功,返回 pdPASS ; 失败,返回 errQUEUE_FULL 。

1.2.3、获取二值信号量

cpp 复制代码
BaseType_t xSemaphoreTake( 
    SemaphoreHandle_t     xSemaphore,
    TickType_t            xTicksToWait 
);

参数:

xSemaphore:要获取的信号量句柄

xTicksToWait:超时时间,0 表示不超时,portMAX_DELAY表示卡死等待;

返回值:

成功,返回 pdPASS ; 失败,返回 errQUEUE_FULL 。

1.3、实操

1.3.1、任务需要

创建一个二值信号量,按下 KEY1 则释放信号量,按下 KEY2 获取信号量。

1.3.2、代码

cpp 复制代码
/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * File Name          : freertos.c
  * Description        : Code for freertos applications
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2024 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */

/* Includes ------------------------------------------------------------------*/
#include "FreeRTOS.h"
#include "task.h"
#include "main.h"
#include "cmsis_os.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "stdio.h"
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */

/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN Variables */

/* USER CODE END Variables */
osThreadId TaskKEY1Handle;
osThreadId TaskKEY2Handle;
osSemaphoreId myBinaryHandle;				/*创建二值信号量句柄*/

/* Private function prototypes -----------------------------------------------*/
/* USER CODE BEGIN FunctionPrototypes */

/* USER CODE END FunctionPrototypes */

void StartTaskKEY1(void const * argument);
void StartTaskKEY2(void const * argument);

void MX_FREERTOS_Init(void); /* (MISRA C 2004 rule 8.1) */

/* GetIdleTaskMemory prototype (linked to static allocation support) */
void vApplicationGetIdleTaskMemory( StaticTask_t **ppxIdleTaskTCBBuffer, StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize );

/* USER CODE BEGIN GET_IDLE_TASK_MEMORY */
static StaticTask_t xIdleTaskTCBBuffer;
static StackType_t xIdleStack[configMINIMAL_STACK_SIZE];

void vApplicationGetIdleTaskMemory( StaticTask_t **ppxIdleTaskTCBBuffer, StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize )
{
  *ppxIdleTaskTCBBuffer = &xIdleTaskTCBBuffer;
  *ppxIdleTaskStackBuffer = &xIdleStack[0];
  *pulIdleTaskStackSize = configMINIMAL_STACK_SIZE;
  /* place for user code */
}
/* USER CODE END GET_IDLE_TASK_MEMORY */

/**
  * @brief  FreeRTOS initialization
  * @param  None
  * @retval None
  */
void MX_FREERTOS_Init(void) {
  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* USER CODE BEGIN RTOS_MUTEX */
  /* add mutexes, ... */
  /* USER CODE END RTOS_MUTEX */

  /* Create the semaphores(s) */
  /* definition and creation of myBinary */
//  osSemaphoreDef(myBinary);
//  myBinaryHandle = osSemaphoreCreate(osSemaphore(myBinary), 1);	/*CubeMX创建二值信号量,osSemaphore函数会放入一个二值信号量*/
	myBinaryHandle = xSemaphoreCreateBinary();				/*创建二值信号量,创建时不会自动放入信号量*/
  /* USER CODE BEGIN RTOS_SEMAPHORES */
  /* add semaphores, ... */
  /* USER CODE END RTOS_SEMAPHORES */

  /* USER CODE BEGIN RTOS_TIMERS */
  /* start timers, add new ones, ... */
  /* USER CODE END RTOS_TIMERS */

  /* USER CODE BEGIN RTOS_QUEUES */
  /* add queues, ... */
  /* USER CODE END RTOS_QUEUES */

  /* Create the thread(s) */
  /* definition and creation of TaskKEY1 */
  osThreadDef(TaskKEY1, StartTaskKEY1, osPriorityNormal, 0, 128);
  TaskKEY1Handle = osThreadCreate(osThread(TaskKEY1), NULL);

  /* definition and creation of TaskKEY2 */
  osThreadDef(TaskKEY2, StartTaskKEY2, osPriorityNormal, 0, 128);
  TaskKEY2Handle = osThreadCreate(osThread(TaskKEY2), NULL);

  /* USER CODE BEGIN RTOS_THREADS */
  /* add threads, ... */
  /* USER CODE END RTOS_THREADS */

}

/* USER CODE BEGIN Header_StartTaskKEY1 */
/**
  * @brief  Function implementing the TaskKEY1 thread.
  * @param  argument: Not used
  * @retval None
  */
/* USER CODE END Header_StartTaskKEY1 */
void StartTaskKEY1(void const * argument)
{
  /* USER CODE BEGIN StartTaskKEY1 */
	BaseType_t binary_semaphore_flag = 0;		/*用来保存释放二值信号量函数的返回值*/
  /* Infinite loop */
  for(;;)
  {
		if(HAL_GPIO_ReadPin(GPIOA ,GPIO_PIN_0) == GPIO_PIN_RESET)
		{
			osDelay(20);		/*防抖*/
			if(HAL_GPIO_ReadPin(GPIOA ,GPIO_PIN_0) == GPIO_PIN_RESET)
			{
				binary_semaphore_flag = xSemaphoreGive(myBinaryHandle);		/*参数:二值信号量句柄*/
				if(binary_semaphore_flag == pdPASS)
					printf("成功释放二值信号量!\r\n");
				else
					printf("释放二值信号量失败\r\n");
			}
			while(HAL_GPIO_ReadPin(GPIOA ,GPIO_PIN_0) == GPIO_PIN_RESET);		/*防止按键被按下,任务被多次执行*/
		}
    osDelay(10);
  }
  /* USER CODE END StartTaskKEY1 */
}

/* USER CODE BEGIN Header_StartTaskKEY2 */
/**
* @brief Function implementing the TaskKEY2 thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_StartTaskKEY2 */
void StartTaskKEY2(void const * argument)
{
  /* USER CODE BEGIN StartTaskKEY2 */
	BaseType_t binary_semaphore_flag = 0;
  /* Infinite loop */
  for(;;)
  {
		if(HAL_GPIO_ReadPin(GPIOA ,GPIO_PIN_1) == GPIO_PIN_RESET)
		{
			osDelay(20);		/*防抖*/
			if(HAL_GPIO_ReadPin(GPIOA ,GPIO_PIN_1) == GPIO_PIN_RESET)
			{
				binary_semaphore_flag = xSemaphoreTake(myBinaryHandle ,portMAX_DELAY);		/*参数:信号量句柄		阻塞时间*/
				if(binary_semaphore_flag == pdPASS)
					printf("成功获取二值信号量!\r\n");
				else
					printf("获取二值信号量失败\r\n");
			}
			while(HAL_GPIO_ReadPin(GPIOA ,GPIO_PIN_1) == GPIO_PIN_RESET);		/*防止按键被按下,任务被多次执行*/
		}
    osDelay(10);
  }
  /* USER CODE END StartTaskKEY2 */
}

/* Private application code --------------------------------------------------*/
/* USER CODE BEGIN Application */

/* USER CODE END Application */

2、计数型信号量

2.1、什么是计数型信号量?

计数型信号量相当于队列长度大于1 的队列,因此计数型信号量能够容纳多个资源,这在计数型信号量被创建的时候确定的。

2.2、计数型信号量相关 API 函数

2.2.1、创建计数型信号量

cpp 复制代码
SemaphoreHandle_t xSemaphoreCreateCounting( 
    UBaseType_t     uxMaxCount,
    UBaseType_t     uxInitialCount
);

参数:

uxMaxCount:可以达到的最大计数值

uxInitialCount:创建信号量时分配给信号量的计数值

返回值:

成功,返回对应计数型信号量的句柄; 失败,返回 NULL 。

2.2.2、释放和获取计数型信号量

计数型信号量的释放和获取与二值信号量完全相同。

2.3、实操

2.3.1、任务需求

创建一个计数型信号量,按下 KEY1 则释放信号量,按下 KEY2 获取信号量。

2.3.2、代码

cpp 复制代码
/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * File Name          : freertos.c
  * Description        : Code for freertos applications
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2024 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */

/* Includes ------------------------------------------------------------------*/
#include "FreeRTOS.h"
#include "task.h"
#include "main.h"
#include "cmsis_os.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "stdio.h"
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */

/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN Variables */

/* USER CODE END Variables */
osThreadId TaskKEY1Handle;
osThreadId TaskKEY2Handle;
osSemaphoreId myCountingHandle;				/*计数型信号量句柄*/

/* Private function prototypes -----------------------------------------------*/
/* USER CODE BEGIN FunctionPrototypes */

/* USER CODE END FunctionPrototypes */

void StartTaskKEY1(void const * argument);
void StartTaskKEY2(void const * argument);

void MX_FREERTOS_Init(void); /* (MISRA C 2004 rule 8.1) */

/* GetIdleTaskMemory prototype (linked to static allocation support) */
void vApplicationGetIdleTaskMemory( StaticTask_t **ppxIdleTaskTCBBuffer, StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize );

/* USER CODE BEGIN GET_IDLE_TASK_MEMORY */
static StaticTask_t xIdleTaskTCBBuffer;
static StackType_t xIdleStack[configMINIMAL_STACK_SIZE];

void vApplicationGetIdleTaskMemory( StaticTask_t **ppxIdleTaskTCBBuffer, StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize )
{
  *ppxIdleTaskTCBBuffer = &xIdleTaskTCBBuffer;
  *ppxIdleTaskStackBuffer = &xIdleStack[0];
  *pulIdleTaskStackSize = configMINIMAL_STACK_SIZE;
  /* place for user code */
}
/* USER CODE END GET_IDLE_TASK_MEMORY */

/**
  * @brief  FreeRTOS initialization
  * @param  None
  * @retval None
  */
void MX_FREERTOS_Init(void) {
  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* USER CODE BEGIN RTOS_MUTEX */
  /* add mutexes, ... */
  /* USER CODE END RTOS_MUTEX */

  /* Create the semaphores(s) */
  /* definition and creation of myCounting */
//  osSemaphoreDef(myCounting);
//  myCountingHandle = osSemaphoreCreate(osSemaphore(myCounting), 5);				/*CubeMx创建计数型信号量,最大计数值5,默认已经释放5个信号量*/
	myCountingHandle = xSemaphoreCreateCounting(5,3);													/*创建计数型信号量,最大计数值5,开始计数值为3(最多能释放2个信号量)*/
  /* USER CODE BEGIN RTOS_SEMAPHORES */
  /* add semaphores, ... */
  /* USER CODE END RTOS_SEMAPHORES */

  /* USER CODE BEGIN RTOS_TIMERS */
  /* start timers, add new ones, ... */
  /* USER CODE END RTOS_TIMERS */

  /* USER CODE BEGIN RTOS_QUEUES */
  /* add queues, ... */
  /* USER CODE END RTOS_QUEUES */

  /* Create the thread(s) */
  /* definition and creation of TaskKEY1 */
  osThreadDef(TaskKEY1, StartTaskKEY1, osPriorityNormal, 0, 128);
  TaskKEY1Handle = osThreadCreate(osThread(TaskKEY1), NULL);

  /* definition and creation of TaskKEY2 */
  osThreadDef(TaskKEY2, StartTaskKEY2, osPriorityNormal, 0, 128);
  TaskKEY2Handle = osThreadCreate(osThread(TaskKEY2), NULL);

  /* USER CODE BEGIN RTOS_THREADS */
  /* add threads, ... */
  /* USER CODE END RTOS_THREADS */

}

/* USER CODE BEGIN Header_StartTaskKEY1 */
/**
  * @brief  Function implementing the TaskKEY1 thread.
  * @param  argument: Not used
  * @retval None
  */
/* USER CODE END Header_StartTaskKEY1 */
void StartTaskKEY1(void const * argument)
{
  /* USER CODE BEGIN StartTaskKEY1 */
	BaseType_t binary_semaphore_flag = 0;		/*用来保存释放二值信号量函数的返回值*/
  /* Infinite loop */
  for(;;)
  {
		if(HAL_GPIO_ReadPin(GPIOA ,GPIO_PIN_0) == GPIO_PIN_RESET)
		{
			osDelay(20);		/*防抖*/
			if(HAL_GPIO_ReadPin(GPIOA ,GPIO_PIN_0) == GPIO_PIN_RESET)
			{
				binary_semaphore_flag = xSemaphoreGive(myCountingHandle);		/*参数:计数型信号量句柄*/
				if(binary_semaphore_flag == pdPASS)
					printf("成功释放计数型信号量!\r\n");
				else
					printf("释放计数型信号量失败\r\n");
			}
			while(HAL_GPIO_ReadPin(GPIOA ,GPIO_PIN_0) == GPIO_PIN_RESET);		/*防止按键被按下,任务被多次执行*/
		}
    osDelay(10);
  }
  /* USER CODE END StartTaskKEY1 */
}

/* USER CODE BEGIN Header_StartTaskKEY2 */
/**
* @brief Function implementing the TaskKEY2 thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_StartTaskKEY2 */
void StartTaskKEY2(void const * argument)
{
  /* USER CODE BEGIN StartTaskKEY2 */
	BaseType_t binary_semaphore_flag = 0;
  /* Infinite loop */
  for(;;)
  {
		if(HAL_GPIO_ReadPin(GPIOA ,GPIO_PIN_1) == GPIO_PIN_RESET)
		{
			osDelay(20);		/*防抖*/
			if(HAL_GPIO_ReadPin(GPIOA ,GPIO_PIN_1) == GPIO_PIN_RESET)
			{
				binary_semaphore_flag = xSemaphoreTake(myCountingHandle ,0);		/*参数:计数型信号量句柄		阻塞时间*/
				if(binary_semaphore_flag == pdPASS)
					printf("成功获取计数型信号量!\r\n");
				else
					printf("获取计数型信号量失败\r\n");
			}
			while(HAL_GPIO_ReadPin(GPIOA ,GPIO_PIN_1) == GPIO_PIN_RESET);		/*防止按键被按下,任务被多次执行*/
		}
    osDelay(10);
  }
  /* USER CODE END StartTaskKEY2 */
}

/* Private application code --------------------------------------------------*/
/* USER CODE BEGIN Application */

/* USER CODE END Application */

3、互斥量

3.1、什么是互斥量

在多数情况下,互斥型信号量和二值型信号量非常相似,但是从功能上二值型信号量用于同步 ,而互斥型信号量用于资源保护

互斥型信号量和二值型信号量还有一个最大的区别 ,互斥型信号量可以有效解决优先级反转现象。

3.2、什么是优先级翻转

实质:低优先级任务将二值信号量获取,导致高优先级无法获取二值信号量阻塞,而不用获取信号量的中优先级任务能够打断低优先级任务,等到中优先级任务执行完后,得到信号量的低优先级任务继续执行,等到执行完后归还二值信号量,高优先级任务获取信号量后才能执行,这种现象称之为优先级翻转。

以上图为例,系统中有3个不同优先级的任务H/M/L,最高优先级任务H和最低优先级任务L通过信号量机 制,共享资源。目前任务L占有资源,锁定了信号量,Task H运行后将被阻塞,直到Task L释放信号量后, Task H才能够退出阻塞状态继续运行。但是Task H在等待Task L释放信号量的过程中,中等优先级任务M抢 占了任务L,从而延迟了信号量的释放时间,导致Task H阻塞了更长时间,这种现象称为优先级倒置或反转。

优先级继承: 当一个互斥信号量正在被一个低优先级的任务持有时, 如果此时有个高优先级的任务也尝试获取这个互斥信号量,那么这个高优先级的任务就会被阻塞。不过这个高优先级的任务会将低优先级任务的优先级提升到与自己相同的优先级。

3.3、互斥量相关函数

注意:互斥信号量不能用于中断服务函数中!

参数:

返回值:

成功,返回对应互斥量的句柄; 失败,返回 NULL 。

3.4、实操

3.4.1、演示优先级翻转

cpp 复制代码
/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * File Name          : freertos.c
  * Description        : Code for freertos applications
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2024 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */

/* Includes ------------------------------------------------------------------*/
#include "FreeRTOS.h"
#include "task.h"
#include "main.h"
#include "cmsis_os.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "stdio.h"
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */

/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN Variables */

/* USER CODE END Variables */
osThreadId TaskHIGHHandle;
osThreadId TaskMIDDLEHandle;
osThreadId TaskLOWHandle;
osSemaphoreId myBinarySem01Handle;		/*二值信号量句柄*/

/* Private function prototypes -----------------------------------------------*/
/* USER CODE BEGIN FunctionPrototypes */

/* USER CODE END FunctionPrototypes */

void StartTaskHIGH(void const * argument);
void StartTaskMIDDLE(void const * argument);
void StartTaskLOW(void const * argument);

void MX_FREERTOS_Init(void); /* (MISRA C 2004 rule 8.1) */

/* GetIdleTaskMemory prototype (linked to static allocation support) */
void vApplicationGetIdleTaskMemory( StaticTask_t **ppxIdleTaskTCBBuffer, StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize );

/* USER CODE BEGIN GET_IDLE_TASK_MEMORY */
static StaticTask_t xIdleTaskTCBBuffer;
static StackType_t xIdleStack[configMINIMAL_STACK_SIZE];

void vApplicationGetIdleTaskMemory( StaticTask_t **ppxIdleTaskTCBBuffer, StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize )
{
  *ppxIdleTaskTCBBuffer = &xIdleTaskTCBBuffer;
  *ppxIdleTaskStackBuffer = &xIdleStack[0];
  *pulIdleTaskStackSize = configMINIMAL_STACK_SIZE;
  /* place for user code */
}
/* USER CODE END GET_IDLE_TASK_MEMORY */

/**
  * @brief  FreeRTOS initialization
  * @param  None
  * @retval None
  */
void MX_FREERTOS_Init(void) {
  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* USER CODE BEGIN RTOS_MUTEX */
  /* add mutexes, ... */
  /* USER CODE END RTOS_MUTEX */

  /* Create the semaphores(s) */
  /* definition and creation of myBinarySem01 */
  osSemaphoreDef(myBinarySem01);
  myBinarySem01Handle = osSemaphoreCreate(osSemaphore(myBinarySem01), 1);	/*创建二值信号量,CubeMX函数创建完,里面已经有信号量*/

  /* USER CODE BEGIN RTOS_SEMAPHORES */
  /* add semaphores, ... */
  /* USER CODE END RTOS_SEMAPHORES */

  /* USER CODE BEGIN RTOS_TIMERS */
  /* start timers, add new ones, ... */
  /* USER CODE END RTOS_TIMERS */

  /* USER CODE BEGIN RTOS_QUEUES */
  /* add queues, ... */
  /* USER CODE END RTOS_QUEUES */

  /* Create the thread(s) */
  /* definition and creation of TaskHIGH */
  osThreadDef(TaskHIGH, StartTaskHIGH, osPriorityAboveNormal, 0, 128);
  TaskHIGHHandle = osThreadCreate(osThread(TaskHIGH), NULL);

  /* definition and creation of TaskMIDDLE */
  osThreadDef(TaskMIDDLE, StartTaskMIDDLE, osPriorityNormal, 0, 128);
  TaskMIDDLEHandle = osThreadCreate(osThread(TaskMIDDLE), NULL);

  /* definition and creation of TaskLOW */
  osThreadDef(TaskLOW, StartTaskLOW, osPriorityBelowNormal, 0, 128);
  TaskLOWHandle = osThreadCreate(osThread(TaskLOW), NULL);

  /* USER CODE BEGIN RTOS_THREADS */
  /* add threads, ... */
  /* USER CODE END RTOS_THREADS */

}

/* USER CODE BEGIN Header_StartTaskHIGH */
/**
  * @brief  Function implementing the TaskHIGH thread.
  * @param  argument: Not used
  * @retval None
  */
/* USER CODE END Header_StartTaskHIGH */
void StartTaskHIGH(void const * argument)
{
  /* USER CODE BEGIN StartTaskHIGH */
  /* Infinite loop */
  for(;;)
  {
		xSemaphoreTake(myBinarySem01Handle ,portMAX_DELAY);		 	 /*获取二值信号量,死等*/
		printf("TaskH:我开始进入厕所,发功中~~~\r\n");
		HAL_Delay(1000);
		xSemaphoreGive(myBinarySem01Handle);										/*释放二值信号量*/
		printf("TaskH:我上完厕所了,真舒服~~~\r\n");
    osDelay(1000);
  }
  /* USER CODE END StartTaskHIGH */
}

/* USER CODE BEGIN Header_StartTaskMIDDLE */
/**
* @brief Function implementing the TaskMIDDLE thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_StartTaskMIDDLE */
void StartTaskMIDDLE(void const * argument)
{
  /* USER CODE BEGIN StartTaskMIDDLE */
  /* Infinite loop */
  for(;;)
  {
		printf("TaskM:中优先级任务来故意搞破坏,抢占CPU资源。\r\n");
    osDelay(1000);
  }
  /* USER CODE END StartTaskMIDDLE */
}

/* USER CODE BEGIN Header_StartTaskLOW */
/**
* @brief Function implementing the TaskLOW thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_StartTaskLOW */
void StartTaskLOW(void const * argument)
{
  /* USER CODE BEGIN StartTaskLOW */
  /* Infinite loop */
  for(;;)
  {
		xSemaphoreTake(myBinarySem01Handle ,portMAX_DELAY);		 	 /*获取二值信号量,死等*/
		printf("TaskL::我开始进入厕所,发功中~~~\r\n");
		HAL_Delay(3000);
		printf("TaskL:我上完厕所了,真舒服~~~\r\n");
		xSemaphoreGive(myBinarySem01Handle);										/*释放二值信号量*/
		osDelay(1000);
  }
  /* USER CODE END StartTaskLOW */
}

/* Private application code --------------------------------------------------*/
/* USER CODE BEGIN Application */

/* USER CODE END Application */

结果:

3.4.2、使用互斥量优化优先级翻转问题

任务:保卫厕所(哈哈哈哈哈哈哈哈哈哈哈哈)

cpp 复制代码
/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * File Name          : freertos.c
  * Description        : Code for freertos applications
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2024 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */

/* Includes ------------------------------------------------------------------*/
#include "FreeRTOS.h"
#include "task.h"
#include "main.h"
#include "cmsis_os.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "stdio.h"
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */

/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN Variables */

/* USER CODE END Variables */
osThreadId TaskHIGHHandle;
osThreadId TaskMIDDLEHandle;
osThreadId TaskLOWHandle;
SemaphoreHandle_t MetuxHandle;		/*互斥信号量句柄*/

/* Private function prototypes -----------------------------------------------*/
/* USER CODE BEGIN FunctionPrototypes */

/* USER CODE END FunctionPrototypes */

void StartTaskHIGH(void const * argument);
void StartTaskMIDDLE(void const * argument);
void StartTaskLOW(void const * argument);

void MX_FREERTOS_Init(void); /* (MISRA C 2004 rule 8.1) */

/* GetIdleTaskMemory prototype (linked to static allocation support) */
void vApplicationGetIdleTaskMemory( StaticTask_t **ppxIdleTaskTCBBuffer, StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize );

/* USER CODE BEGIN GET_IDLE_TASK_MEMORY */
static StaticTask_t xIdleTaskTCBBuffer;
static StackType_t xIdleStack[configMINIMAL_STACK_SIZE];

void vApplicationGetIdleTaskMemory( StaticTask_t **ppxIdleTaskTCBBuffer, StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize )
{
  *ppxIdleTaskTCBBuffer = &xIdleTaskTCBBuffer;
  *ppxIdleTaskStackBuffer = &xIdleStack[0];
  *pulIdleTaskStackSize = configMINIMAL_STACK_SIZE;
  /* place for user code */
}
/* USER CODE END GET_IDLE_TASK_MEMORY */

/**
  * @brief  FreeRTOS initialization
  * @param  None
  * @retval None
  */
void MX_FREERTOS_Init(void) {
  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* USER CODE BEGIN RTOS_MUTEX */
  /* add mutexes, ... */
  /* USER CODE END RTOS_MUTEX */

  /* Create the semaphores(s) */
  /* definition and creation of myBinarySem01 */
	MetuxHandle = xSemaphoreCreateMutex();		/*创建互斥量,创建成功默认放有互斥量*/
  /* USER CODE BEGIN RTOS_SEMAPHORES */
  /* add semaphores, ... */
  /* USER CODE END RTOS_SEMAPHORES */

  /* USER CODE BEGIN RTOS_TIMERS */
  /* start timers, add new ones, ... */
  /* USER CODE END RTOS_TIMERS */

  /* USER CODE BEGIN RTOS_QUEUES */
  /* add queues, ... */
  /* USER CODE END RTOS_QUEUES */

  /* Create the thread(s) */
  /* definition and creation of TaskHIGH */
  osThreadDef(TaskHIGH, StartTaskHIGH, osPriorityAboveNormal, 0, 128);
  TaskHIGHHandle = osThreadCreate(osThread(TaskHIGH), NULL);

  /* definition and creation of TaskMIDDLE */
  osThreadDef(TaskMIDDLE, StartTaskMIDDLE, osPriorityNormal, 0, 128);
  TaskMIDDLEHandle = osThreadCreate(osThread(TaskMIDDLE), NULL);

  /* definition and creation of TaskLOW */
  osThreadDef(TaskLOW, StartTaskLOW, osPriorityBelowNormal, 0, 128);
  TaskLOWHandle = osThreadCreate(osThread(TaskLOW), NULL);

  /* USER CODE BEGIN RTOS_THREADS */
  /* add threads, ... */
  /* USER CODE END RTOS_THREADS */

}

/* USER CODE BEGIN Header_StartTaskHIGH */
/**
  * @brief  Function implementing the TaskHIGH thread.
  * @param  argument: Not used
  * @retval None
  */
/* USER CODE END Header_StartTaskHIGH */
void StartTaskHIGH(void const * argument)
{
  /* USER CODE BEGIN StartTaskHIGH */
  /* Infinite loop */
  for(;;)
  {
		xSemaphoreTake(MetuxHandle ,portMAX_DELAY);		 	 /*获取二值信号量,死等*/
		printf("TaskH:我开始进入厕所,发功中~~~\r\n");
		HAL_Delay(1000);
		xSemaphoreGive(MetuxHandle);										/*释放二值信号量*/
		printf("TaskH:我上完厕所了,真舒服~~~\r\n");
    osDelay(1000);
  }
  /* USER CODE END StartTaskHIGH */
}

/* USER CODE BEGIN Header_StartTaskMIDDLE */
/**
* @brief Function implementing the TaskMIDDLE thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_StartTaskMIDDLE */
void StartTaskMIDDLE(void const * argument)
{
  /* USER CODE BEGIN StartTaskMIDDLE */
  /* Infinite loop */
  for(;;)
  {
		printf("TaskM:中优先级任务来故意搞破坏,抢占CPU资源。\r\n");
    osDelay(1000);
  }
  /* USER CODE END StartTaskMIDDLE */
}

/* USER CODE BEGIN Header_StartTaskLOW */
/**
* @brief Function implementing the TaskLOW thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_StartTaskLOW */
void StartTaskLOW(void const * argument)
{
  /* USER CODE BEGIN StartTaskLOW */
  /* Infinite loop */
  for(;;)
  {
		xSemaphoreTake(MetuxHandle ,portMAX_DELAY);		 	 /*获取二值信号量,死等*/
		printf("TaskL::我开始进入厕所,发功中~~~\r\n");
		HAL_Delay(3000);
		printf("TaskL:我上完厕所了,真舒服~~~\r\n");
		xSemaphoreGive(MetuxHandle);										/*释放二值信号量*/
		osDelay(1000);
  }
  /* USER CODE END StartTaskLOW */
}

/* Private application code --------------------------------------------------*/
/* USER CODE BEGIN Application */

/* USER CODE END Application */

结果:

相关推荐
西岸行者6 天前
学习笔记:SKILLS 能帮助更好的vibe coding
笔记·学习
悠哉悠哉愿意6 天前
【单片机学习笔记】串口、超声波、NE555的同时使用
笔记·单片机·学习
别催小唐敲代码6 天前
嵌入式学习路线
学习
毛小茛6 天前
计算机系统概论——校验码
学习
babe小鑫6 天前
大专经济信息管理专业学习数据分析的必要性
学习·数据挖掘·数据分析
winfreedoms6 天前
ROS2知识大白话
笔记·学习·ros2
在这habit之下6 天前
Linux Virtual Server(LVS)学习总结
linux·学习·lvs
我想我不够好。6 天前
2026.2.25监控学习
学习
im_AMBER6 天前
Leetcode 127 删除有序数组中的重复项 | 删除有序数组中的重复项 II
数据结构·学习·算法·leetcode
CodeJourney_J6 天前
从“Hello World“ 开始 C++
c语言·c++·学习