二值信号量

Q: 什么是信号量?

A: 信号量(Semaphore),是在多任务环境下使用的一种机制,是可以用来保证两个或多个关键代码段不被并发调用。 信号量这个名字,我们可以把它拆分来看,“信号”可以起到通知信号的作用,“量”还可以用来表示资源的数量,当“量”只有0和1的时候,它就可以被称作“二值信号量”,只有两个状 ,当我们的那个量没有限制的时候,它就可以被称作为“计数型信号量”。 信号量也是队列的一种

Q: 什么是二值信号量?

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

举例理解:

  • 互斥访问:比如门钥匙,只有获取到钥匙才可以开门
  • 任务同步:比如我写完一本书,别人才可以看这本书

二值信号量相关 API 函数

创建二值信号量

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

释放二值信号量

  • xSemaphore:要释放的信号量句柄
  • 返回值: 成功,返回 pdPASS ; 失败,返回 errQUEUE_FULL 

获取二值信号量

  • xSemaphore:要获取的信号量句柄
  • xTicksToWait:超时时间(阻塞时间),0 表示不超时,portMAX_DELAY表示卡死等待;
  • 返回值: 成功,返回 pdPASS ; 失败,返回 errQUEUE_FULL 

实操演示

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

在 C:\mjm_CubeMX_proj 路径下,复制一份Cube的母版并重命名为 :mjm_freeRTOS_Sema_Bi:

打开相应的Cube文件: 

1. 配置按钮的GPIO:

2. 找到左侧的Middleware --> FREERTOS,然后在下方找到"Task and Queues",然后创建两个任务:

3. 在“Timers and Semaphores” 中创建二值信号量:

4. 生成代码打开Keil:

4.1 在freertos.c中可以看到创建信号量和任务的代码:

注意,此处的osSemaphoreCreate()函数也是Cube对于创建二值信号量函数的封装:

但是有一个问题,上文中提到二值信号量的创建函数是“xSemaphoreCreateBinary()”,但是Cube中封装的对应函数确是“vSemaphoreCreateBinary()”,根据我关于FreeRTOS的函数命名规则的博客可知:本篇上文中提到“x”开头的函数返回的是任务句柄;而Cube中“v”开头的函数返回类型是void型也就是不返回。

我认为之所以Cube使用了“v”开头的函数是因为Cube把返回句柄的功能也封装起来了

可见上图函数返回的效果也是“成功创建返回句柄,失败返回NULL”

如果跳转这个“vSemaphoreCreateBinary()”:

如上图,和“xSemaphoreCreateBinary()”实现的效果也略有不同,这个“v”开头的函数不仅会创建一个二值信号量,并且如果创建成功,就会自动释放一个信号量

4.2 代码编写:
#include "stdio.h"

void StartTaskGive(void const * argument)
{
  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){ //按键消抖
				printf("KEY1 has been pressed\r\n");
				if (xSemaphoreGive(myBinarySemHandle) == pdTRUE){ //释放信号量并判断返回值,如果返回成功
					printf("successfully give\r\n");
				}else{//如果返回失败
					printf("fail to give\r\n");
				}		
			}
			while (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET); //等待按键松开,防止出现按钮一直按着,就一直删除创建任务			
		}
		osDelay(10);
  }
}


void StartTaskTake(void const * argument)
{
  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){ //按键消抖
				printf("KEY2 has been pressed\r\n");
				if (xSemaphoreTake(myBinarySemHandle, 0 ) == pdTRUE){ //获取信号量并判断返回值,如果返回成功 
					printf("successfully take\r\n");
				}else{//如果返回失败
					printf("fail to take\r\n");
				}
			}
			while (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_RESET); //等待按键松开,防止出现按钮一直按着,就一直删除创建任务			
		}
		osDelay(10);
  }
}

实现效果

打开串口助手,先尝试按一下KEY1:

发现无法释放,原因就是刚刚提到的,Cube封装的函数在成功创建信号量之后就会直接释放,所以再次释放肯定会失败。

那如果现在按下KEY2:

 

和预想的一样,因为信号量已经被Cube的代码释放,所以可以成功获取

如果再按一下KEY2:

同时,获取信号量立刻fail的原因是我将获取信号量函数的“xTicksToWait”设置为了0,即只要没有就立刻返回,但是如果我设置为“portMAX_DELAY”此处就不会打印“fail to take”,而是会一直等待信号量的释放而卡死。

由于信号量在刚刚已经被获取,所以显然不能再获取一次

此时按下KEY1,再按下KEY2:

 

此时就可以成功释放一个信号量,并成功获取了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值