回顾上节所讲:
Q: 什么是信号量?
A: 信号量(Semaphore),是在多任务环境下使用的一种机制,是可以用来保证两个或多个关键代码段不被并发调用。 信号量这个名字,我们可以把它拆分来看,“信号”可以起到通知信号的作用,“量”还可以用来表示资源的数量,当“量”只有0和1的时候,它就可以被称作“二值信号量”,只有两个状 ,当我们的那个量没有限制的时候,它就可以被称作为“计数型信号量”。 信号量也是队列的一种。
计数型信号量相当于队列长度大于1 的队列,因此计数型信号量能够容纳多个资源,计数型信号量的值反应的是资源的状态。
关于计数信号量的理解,比较经典的就是“停车场”,假如停车场有3个车位,那么这就是个计数值为3的计数信号量,每当有一辆车开进停车场,计数值就会减1,如果停车场停满了3辆车,计数值就会为0,此时有新的车开过停车场,只需要查看计数值就知道,无法进入停车,只能返回或者继续等着。
计数型信号量相关 API 函数
- uxMaxCount:可以达到的最大计数值
- uxInitialCount:创建信号量时分配给信号量的计数值
- 返回值: 成功,返回对应计数型信号量的句柄; 失败,返回 NULL
除了创建有略微的区别,计数型信号量的释放和获取与二值信号量完全相同 !
实操演示
需求:创建一个计数型信号量,按下 KEY1 则释放信号量,按下 KEY2 获取信号量。
由于计数信号量和二值信号量的实现非常类似,所以直接复制上节二值信号量的Cube文件“mjm_freeRTOS_Sema_Bi” 并重命名为 “mjm_freeRTOS_Sema_coun”
打开相应的Cube文件:
1. 配置按钮的GPIO:
2. 找到左侧的Middleware --> FREERTOS:
2.1 在“Config parameters”,将“USE_COUNTING_SEMAPHORES” 设置为 “Enabled”:
2.2 在“Timers and Semaphores”,删除刚刚创建的二值信号量,并创建一个新的计数信号量:
3. 生成代码打开Keil:
3.1 查看生成计数信号量的函数:
跳转这个函数并查看计数型信号量的部分:
可见,Cube封装的这个函数在创建计数型信号量的时候,将“xSemaphoreCreateCounting()" 的两个输入参数都设置为了count,所以和二值信号量一样,在创建计数信号量的时候,会同时释放所有的信号量。
3.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(myCountingSemHandle) == 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(myCountingSemHandle, 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:
如上面所说,创建计数信号量的函数将“uxInitialCount” 设为了3,所以一旦创建就先全部被释放了,所以再次释放不成功。
那么此时连按四下KEY2:
每获取一次,计数量减1,三次之后计数信号量被全部获取,计数值归0,所以在第四次获取失败
此时再按四下KEY1:
每释放一次,计数值加1,三次之后计数信号量全部被释放,计数值为3,所以在第四次释放失败