目录
一、计数型信号量简介
二、计数型信号量相关API
1、创建计数型信号量
2、释放计数型信号量
3、获取计数型信号量
4、获取计数型信号量的计数值
三、计数型信号量实操
1、实验需求
2、CubeMX配置
3、代码实现
一、计数型信号量简介
①取值只有0与1两种状态的信号量称之为二值信号量;取值大于1的信号量称之为计数信号量
②计数信号量是一种长度大于1,消息大小为0的特殊消息队列。
③计数信号量的取值也可以为1,但通常大于1,如果取值为1,相当于只有0与1两种状态,用二值信号量即可。
④创建计数信号量时,系统会为创建的计数信号量分配内存。
计数型信号量相当于队列长度大于1 的队列,因此计数型信号量能够容纳多个资源,这在计数型信号量被创建的时候确定的。
使用计数型信号量可以解决多个任务之间的同步问题,例如控制对共享资源的访问和协调任务的执行顺序。
二、计数型信号量相关API
函数 | 描述 |
xSemaphoreCreateCounting() | 使用动态方法创建计数型信号量 |
xSemaphoreCreateCountingStatic() | 使用静态方法创建计数型信号量 |
xSemaphoreGive() | 释放信号量 |
xSemaphoreGiveFromISR() | 在中断中释放信号量 |
xSemaphoreTake() | 获取信号量 |
xSemaphoreTakeFromISR() | 在中断中获取信号量 |
uxSemaphoreGetCount() | 获取信号量的计数值 |
1、创建计数型信号量
SemaphoreHandle_t xSemaphoreCreateCounting( UBaseType_t uxMaxCount,UBaseType_t uxInitialCount);
参数:
- uxMaxCount:可以达到的最大计数值
- uxInitialCount:创建信号量时分配给信号量的计数值,即计数值的初始值(一般设置为0才能正常计数)
返回值:
- 成功,返回对应计数型信号量的句柄;
- 失败,返回 NULL。
2、释放计数型信号量
BaseType_t xSemaphoreGive( SemaphoreHandle_t xSemaphore );
参数:
- xSemaphore:要释放的信号量句柄
返回值:
- 成功,返回 pdPASS ;
- 失败,返回 errQUEUE_FULL 。
3、获取计数型信号量
BaseType_t xSemaphoreTake( SemaphoreHandle_t xSemaphore,TickType_t xTicksToWait );
参数:
- xSemaphore:要获取的信号量句柄
- xTicksToWait:超时时间,0 表示不超时,portMAX_DELAY表示卡死等待;
返回值:
- 成功,返回 pdPASS ;
- 失败,返回 errQUEUE_FULL 。
4、获取计数型信号量的计数值
BaseType_t uxSemaphoreGetCount( SemaphoreHandle_t xSemaphore );
参数:
- xSemaphore:要获取的信号量句柄
返回值:
得到计数型信号量的值。
三、计数型信号量实操
1、实验需求
创建一个计数型信号量,按下 KEY1 则释放信号量,按下 KEY2 获取信号量。
2、CubeMX配置
这里已经将FreeRTOS移植到STM32F103C8T6,具体操作流程看前面的文章。
查看原理图配置按键引脚
创建两个任务用来放入和获取信号量
使能计数型信号量
创建计数型信号量
3、代码实现
uart.c 重定向printf
#include "stdio.h"
int fputc(int ch,FILE *f)
{unsigned char temp[1] = {ch};HAL_UART_Transmit(&huart1,temp,1,0xffff);return ch;
}
需要打开魔术棒勾上红框内选项实现串口打印
打开freertos.c并添加代码
void StartTaskKey1(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){if(xSemaphoreGive(myCountingSemHandle) == pdTRUE) printf("计数型信号量放入成功\r\n");elseprintf("计数型信号量放入失败\r\n");}while (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET);}osDelay(10);}
}void StartTaskKey2(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){ if(xSemaphoreTake(myCountingSemHandle,0) == pdTRUE) printf("计数型信号量获取成功\r\n");elseprintf("计数型信号量获取失败\r\n");}while (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_RESET);}osDelay(10);}
}
两个红框说明当释放信号量成功时,能成功获取信号量(在设置最大范围之内);当超过最大范围后再次释放会失败,同时成功获取信号量次数也是3次(设置的最大数),超过了再次获取也会失败。
注意:
创建后的计数型信号量原先代码和修改后的代码如下:
由于CubeMX内置函数设置计数值的初始值为最大值,即我们一开始设置的可以达到的最大计数值为3,所以第一次复位后释放信号量总是失败,初始值为最大值说明已经释放完毕,此时可以直接获取成功,当全部被获取后,计数值才从0开始,即此时可以正常释放信号量直至设定的最大值,如下: