FreeRTOS 互斥量

基本概念

  • 互斥量又称互斥信号量(本质是信号量),是一种特殊的二值信号量
  • 互斥量 支持互斥量所有权、递归访问以及防止优先级翻转的特性,用于实现对临界资源(如显示器、打印机)的独占式访问。
  • 任意时刻互斥量的状态只有两种,开锁或闭锁。
  • 持有该互斥量的任务也能够重复获得这个锁而不被挂起,这就是递归访问,也就是递归互斥量的特性,信号量重复获取则会被挂起
  • 互斥量与二值信号量最大的不同是:互斥量具有优先级继承机制,而信号量没有

优先级继承机制

  • 优先级继承算法是指,暂时提高某个占有某种资源的低优先级任务的优先级,使之和所有等待该资源的任务中优先级最高那个任务的优先级相等,而当这个低优先级任务执行完毕释放该资源时,优先级重新回到初始设定值
  • 优先级提升的过程叫做优先级继承

优先级翻转

高优先级任务无法运行而低优先级任务可以运行的现象称为“优先级翻转”

互斥量应用场景

  • 可能会引起优先级翻转的情况
  • 任务可能会多次获取互斥量的情况下。这样可以避免同一任务多次递归持有而造成死锁的问题
  • 多任务环境下往往存在多个任务竞争同一临界资源的应用场景,互斥量可被用于对临界资源的保护从而实现独占式访问
  • 例如串口资源的保护

函数

头文件

#include <semphr.h>

互斥量句柄

和消息队列一样

QueueHandle_t 

控制块

互斥量控制块结构体与消息队列结构体是一模一样的,只不过结构体中某些成员变量代表的含义不一样而已

typedef struct QueueDefinition
{
    
    
	int8_t *pcHead;					
	int8_t *pcTail;					
	int8_t *pcWriteTo;				
	union							
	{
    
    
		int8_t *pcReadFrom;			
		UBaseType_t uxRecursiveCallCount; //当结构体用于互斥量时,uxRecursiveCallCount 用于计数,记录递归互斥量被“调用”的次数
	} u;

	List_t xTasksWaitingToSend;		
	List_t xTasksWaitingToReceive;	

	volatile UBaseType_t uxMessagesWaiting;/* 
	这个值就表示有效互斥量个数,这个值是 1则表示互斥量有效,如果是 0 则表示互斥量无效*/
	UBaseType_t uxLength;			/*
如果控制块结构体被用于互斥量的时候,uxLength 表示最大的信号量可用个数,uxLength 最大为 1,因为信号量要么是有效的,要么是无效的*/
	UBaseType_t uxItemSize;			/*如果控制块结构体被用于互斥量的时候,则无需存储空间,为 0 即可。 */

	volatile int8_t cRxLock;		
	volatile int8_t cTxLock;		

	#if( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) )
		uint8_t ucStaticallyAllocated;	
	#endif

	#if ( configUSE_QUEUE_SETS == 1 )
		struct QueueDefinition *pxQueueSetContainer;
	#endif

	#if ( configUSE_TRACE_FACILITY == 1 )
		UBaseType_t uxQueueNumber;
		uint8_t ucQueueType;
	#endif

} xQUEUE;

函数接口

创建互斥量xSemaphoreCreateMutex()


	#define xSemaphoreCreateMutex() xQueueCreateMutex( queueQUEUE_TYPE_MUTEX )
	
	QueueHandle_t xQueueCreateMutex( const uint8_t ucQueueType )

递归互斥量创建函数 xSemaphoreCreateRecursiveMutex()

	#define xSemaphoreCreateRecursiveMutex() xQueueCreateMutex( queueQUEUE_TYPE_RECURSIVE_MUTEX )

互斥量删除函数 vSemaphoreDelete()

和信号量一样的函数

互斥量获取函数 xSemaphoreTake()



#define xSemaphoreTake( xSemaphore, xBlockTime )		xQueueGenericReceive( ( QueueHandle_t ) ( xSemaphore ), NULL, ( xBlockTime ), pdFALSE )
/*

xSemaphore 		信号量句柄。
xBlockTime 		等待信号量可用的最大超时时间,单位为 tick(即系统节拍周期)。如果宏 INCLUDE_vTaskSuspend 定义为 1 且形参xTicksToWait 设置为 portMAX_DELAY ,则任务将一直阻塞在该信号量上(即没有超时时间)。
返回值				获 取 成 功 则 返 回 pdTRUE , 获取失败 返 回 errQUEUE_EMPTY。
*/

递归互斥量获取函数 xSemaphoreTakeRecursive()

	#define xSemaphoreTakeRecursive( xMutex, xBlockTime )	xQueueTakeMutexRecursive( ( xMutex ), ( xBlockTime ) )
/*
xMutex 						信号量句柄。
xBlockTime 					如果不是持有互斥量的任务去获取无效的互斥量,那么任务将进行等待用户指定 超时时间,单位 为 tick(即系统节拍周期) 。如果宏 INCLUDE_vTaskSuspend 定义为 1 且形参 xTicksToWait 设置为portMAX_DELAY ,则任务将一直阻塞在该递归互斥量上(即没有超时时间)。
返回值							获取成功则返回 pdTRUE,在超时之前没有获取成功则返回 errQUEUE_EMPTY。*/

互斥量释放函数 xSemaphoreGive()


BaseType_t xSemaphoreGive(SemaphoreHandle_t  xSemaphore )	

#define xSemaphoreGive( xSemaphore )		xQueueGenericSend( ( QueueHandle_t ) ( xSemaphore ), NULL, semGIVE_BLOCK_TIME, queueSEND_TO_BACK )
/*
如果信号量满,则返回错误代码(err_QUEUE_FULL)
*/

递归互斥量释放函数 xSemaphoreGiveRecursive()


	#define xSemaphoreGiveRecursive( xMutex )	xQueueGiveMutexRecursive( ( xMutex ) )

示例:
之前2个任务的优先级一样,导致就是发送信号任务要打印,接收信号端不打印,引入锁后问题解决

#include "FreeRTOS.h" 
#include "task.h"
#include<bsp_key.h>
#include "semphr.h"
#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 PV */

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
static TaskHandle_t send_Task_Handle = NULL;
static TaskHandle_t recv_Task_Handle = NULL;
static SemaphoreHandle_t xsem=NULL;
static SemaphoreHandle_t xlock=NULL;


static void recv_Task(void * param)
{
    
    
	uint32_t data1;
	while (1)
		{
    
    
		
			if(pdTRUE==xSemaphoreTake(xsem,portMAX_DELAY))
			{
    
    
				xSemaphoreTake(xlock,portMAX_DELAY );
				printf("take sem\r\n");
				xSemaphoreGive(xlock);
				HAL_GPIO_TogglePin(LED0_GPIO_Port, LED0_Pin);

			}
		
		}

}


static void send_Task(void * param)
{
    
    
	uint32_t data1=10;
	while (1)
		{
    
    
			if(Key_Scan(KEY1_GPIO_Port,KEY1_Pin)==KEY_ON){
    
    

				xSemaphoreGive(xsem);
				xSemaphoreTake(xlock,portMAX_DELAY );
				printf("give sem\r\n");
				xSemaphoreGive(xlock);
			}
			vTaskDelay(100);
		}

}


/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
    
    
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */
  	printf("hello FreeRTOS\r\n");

	//xqueue =  xQueueCreate(10, 4);
	xsem = xSemaphoreCreateBinary();
	xlock = xSemaphoreCreateMutex();
	if(xsem==NULL)
	{
    
    
		printf("create queue fail\r\n");
	}
	
	BaseType_t		xReturn;

	xReturn 			= xTaskCreate((TaskFunction_t) recv_Task, /* 任务入口函数 */
	(const char *) "recv_Task", 					/* 任务名字 */
	(uint16_t) 512, 								/* 任务栈大小 */
	(void *) NULL,									/* 任务入口函数参数 */
	(UBaseType_t) 2,								/* 任务的优先级 */
	(TaskHandle_t *) &recv_Task_Handle);			/* 任务控制块指针 */

	
	xReturn 			= xTaskCreate((TaskFunction_t) send_Task, /* 任务入口函数 */
	(const char *) "send_Task", 					/* 任务名字 */
	(uint16_t) 512, 								/* 任务栈大小 */
	(void *) NULL,									/* 任务入口函数参数 */
	(UBaseType_t) 2,								/* 任务的优先级 */
	(TaskHandle_t *) &send_Task_Handle);			/* 任务控制块指针 */

	if (pdPASS == xReturn)
		vTaskStartScheduler(); /* 启动任务,开启调度 */

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
	while (1)
		{
    
    
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
		HAL_GPIO_TogglePin(LED0_GPIO_Port, LED0_Pin);
		HAL_Delay(50);
		}

  /* USER CODE END 3 */
}

おすすめ

転載: blog.csdn.net/u010261063/article/details/121392870