FreeRTOS系列|递归互斥信号量

递归互斥信号量

1. 递归互斥信号量

递归互斥信号量是一种特殊的互斥信号量,已经获取了互斥信号量的任务不能再次获取这个互斥信号量,但是递归互斥信号量不同;已经获取了递归互斥信号量的任务可以再次获取这个递归互斥信号量(即可以嵌套使用),且次数不限。

递归互斥信号量也有优先级继承的问题。一个任务获取了多少次递归互斥信号量就必须释放多少次。比如,若某个任务成功获取了3次递归互斥量,那么该任务也需要同样释放3次递归信号量。同互斥信号量一样,递归互斥信号量不能用在中断服务函数中

2. 递归互斥信号量的API函数
2.1 创建递归互斥信号量
/********************动态创建递归互斥信号量**********************************************/
SemaphoreHandle_t xSemaphoreCreateRecursiveMutex(void)
/********************静态创建递归互斥信号量**********************************************/
SemaphoreHandle_t xSemaphoreCreateRecursiveMutexStatic(StaticSemaphore_t * pxSemaphoreBuffer)
//参数:pxSemaphoreBuffer 指向一个StaticSemaphore_t类型的变量,用来保存信号量结构体
/***********************************************************************************/
返回值:创建成功返回互斥信号量句柄;失败返回NULL


动态递归互斥信号量创建函数是一个宏,最终是通过xQueueCreateMutex()函数来完成,其源码如下:

#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 )
#define xSemaphoreCreateRecursiveMutex()  	\
		xQueueCreateMutex(queueQUEUE_TYPE_RECURSIVE_MUTEX)		
#endif

xQueueCreateMutex函数的源码分析,可参考互斥信号量章节3.1的介绍

2.2 释放递归互斥信号量

递归互斥信号量有专门的释放函数:xSemaphoreGiveRecursive()

/********************递归互斥信号量释放*************************************/
BaseType_t xSemaphoreGiveRecursive(SemaphoreHandle_t  xMutex)
/************************************************************************/
返回值:释放成功返回pdPASS;释放失败返回pdFAIL                   

递归互斥信号量释放函数是一个宏,最终调用xQueueGiveMutexRecursive()函数,源码如下示:

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

 
 
  • 1
/**************xQueueGiveMutexRecursive()函数*********************/
BaseType_t xQueueGiveMutexRecursive( QueueHandle_t xMutex ){
  BaseType_t xReturn;
  Queue_t * const pxMutex = ( Queue_t * ) xMutex;
  /* 检查递归互斥信号量是不是被当前任务获取 */
  if(pxMutex->pxMutexHolder == (void *)xTaskGetCurrentTaskHandle()){
	( pxMutex->u.uxRecursiveCallCount )--;//用来记录递归信号量被释放的次数
	/* uxRecursiveCallCount为0时,说明是最后一次释放 */
	if(pxMutex->u.uxRecursiveCallCount == (UBaseType_t) 0){
	  /* 此时调用xQueueGenericSend完成真正的释放  */
	  (void)xQueueGenericSend(pxMutex,NULL,queueMUTEX_GIVE_BLOCK_TIME,queueSEND_TO_BACK);
	}
	else{
	  mtCOVERAGE_TEST_MARKER();
	}
	xReturn = pdPASS;//释放成功
  }
  else{
	xReturn = pdFAIL;//不是被当前任务获取,释放失败
  }
  return xReturn;
}

2.3 获取递归互斥信号量

递归互斥信号量有专门的获取函数:xSemaphoreTakeRecursive()

/********************递归互斥信号量获取*******************************************/
BaseType_t xSemaphoreTakeRecursive(SemaphoreHandle_t  xMutex//要获取的信号量句柄
						  		   TickType_t xBlockTime)//阻塞时间
/******************************************************************************/
返回值:获取成功返回pdPASS;释放失败返回pdFALSE                      

递归互斥信号量获取函数是一个宏,最终调用xQueueTakeMutexRecursive()函数,源码如下示:

#if( configUSE_RECURSIVE_MUTEXES == 1 )
#define xSemaphoreTakeRecursive( xMutex, xBlockTime ) \
xQueueTakeMutexRecursive(( xMutex ), ( xBlockTime ))
#endif

/**************xQueueTakeMutexRecursive()函数*********************/
BaseType_t xQueueTakeMutexRecursive(QueueHandle_t xMutex,TickType_t xTicksToWait){
  BaseType_t xReturn;
  Queue_t * const pxMutex = ( Queue_t * ) xMutex;
  /* 检查当前任务是不是递归互斥信号量的拥有者 */
  if(pxMutex->pxMutexHolder == (void *) xTaskGetCurrentTaskHandle()){
	( pxMutex->u.uxRecursiveCallCount )++;//若是,表示本次是重复获取信号量
	xReturn = pdPASS;
  }
  else{//如果任务是第一次获取信号量,就需要调用以下函数完成真正的信号量获取
	xReturn = xQueueGenericReceive( pxMutex, NULL, xTicksToWait, pdFALSE );
	/* 第一次获取信号量成功后,将uxRecursiveCallCount加1 */
	if( xReturn != pdFAIL ){
	  ( pxMutex->u.uxRecursiveCallCount )++;
	}
	else{
	  traceTAKE_MUTEX_RECURSIVE_FAILED( pxMutex );
	}
  }
  return xReturn;
}

3. 递归互斥信号量的应用实例

本实例介绍递归互斥信号量的使用。使用STM32CubeMX将FreeRTOS移植到工程中,创建优先级为高中低的三个任务、一个递归互斥信号量

  • High_Task:高优先级任务,会获取递归互斥信号量2次,获取成功后进行相应的处理,处理完后释放递归互斥信号量两次
  • Middle_Task:中优先级任务,简单的应用任务
  • Low_Task:低优先级任务,会获取递归互斥信号量,获取成功后进行相应的处理,处理完后释放递归互斥信号量。但是任务信号量的时间比高优先级任务占用的时间要长
3.1 STM32CubeMX设置
  • RCC设置外接HSE,时钟设置为72M
  • PC0/PC1设置为GPIO推挽输出模式、上拉、高速、默认输出电平为高电平
  • USART1选择为异步通讯方式,波特率设置为115200Bits/s,传输数据长度为8Bit,无奇偶校验,1位停止位;
  • 激活FreeRTOS,添加任务,设置任务名称、优先级、堆栈大小、函数名称等参数

在这里插入图片描述

  • 动态创建递归互斥信号量

在这里插入图片描述

  • 使用FreeRTOS操作系统,一定要将HAL库的Timebase Source从SysTick改为其他定时器,选好定时器后,系统会自动配置TIM
  • 输入工程名,选择路径(不要有中文),选择MDK-ARM V5;勾选Generated periphera initialization as a pair of ‘.c/.h’ files per IP ;点击GENERATE CODE,生成工程代码
3.2 MDK-ARM软件编程
  • 添加High_Task、Middle_Task、Low_Task任务函数代码
/******************HighTask**************************/
void HighTask(void const * argument){
  for(;;){
    vTaskDelay(500);
	printf("High task take RecursiveMutex1\r\n");
	xSemaphoreTakeRecursive(RecursiveMutexHandle,portMAX_DELAY);
	printf("High task running...!\r\n");
	printf("High task take RecursiveMutex2\r\n");
	xSemaphoreTakeRecursive(RecursiveMutexHandle,portMAX_DELAY);
	printf("High task running...!\r\n");
	HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_1);	
	printf("High task give RecursiveMutex1\r\n");
	xSemaphoreGiveRecursive(RecursiveMutexHandle);
	printf("High task give RecursiveMutex2\r\n");
	xSemaphoreGiveRecursive(RecursiveMutexHandle);
	vTaskDelay(500);
  }
}
/******************MiddleTask***********************/
void MiddleTask(void const * argument){
  for(;;){
    printf("Middle task running!\r\n");
	HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_0);
	vTaskDelay(1000);
  }
}
/******************LowTask**************************/
void LowTask(void const * argument){
  for(;;){
  	printf("Low task take RecursiveMutex\r\n");
    xSemaphoreTakeRecursive(RecursiveMutexHandle,portMAX_DELAY);
	printf("Low task running...!\r\n");
	for(int i=0;i<20000000;i++){
	  taskYIELD();			
	}
	printf("Low task give RecursiveMutex\r\n");		
	xSemaphoreGive(RecursiveMutexHandle);
	vTaskDelay(1000);
  }
}

3.3 下载验证

编译无误下载到开发板后,打开串口调试助手,串口输出如下图示的调试信息:

  • 由于High_Task任务延时500ms,因此Middle_Task最先开始运行;
  • 之后Low_Task运行,获取递归互斥信号;
  • High_Task开始运行,阻塞在请求递归互斥信号量这里,等待Low_Task释放递归互斥信号量
  • 此时由于Low_Task正在使用递归互斥信号量,因此Low_Task的优先级暂时提升到了与High_Task相同的优先级,所以Middle_Task无法打断Low_Task的运行。Low_Task运行完后,释放递归互斥信号量,High_Task获取两次互斥信号量并运行,最后释放两次递归互斥信号量

在这里插入图片描述

关注我的公众号,在公众号里发如下消息,即可获取相应的工程源代码:

FreeRTOS递归互斥信号量实例

在这里插入图片描述

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值