基本特性
计数信号量,也可以看成是队列,但是长度大于1。用户只需关心是否为空。
典型应用
(1)计数
事件发生的时候,在事件处理函数中给一个信号量(既就是信号量值计数值加1),任务处理函数获取这个信号量(既就是信号量计数值值减1)。
信号量计数值初始为0。
(2)资源管理
用于指示可用的资源。
当信号好计数值到0的时候,表示没有资源可用。
一个任务想要使用资源,需要先获取信号量——也就是信号量值减1;当一个任务使用完资源(释放资源)的时候,需要给一个信号——也就是信号量值加1。
信号量初始值为最大值。
典型场景:
比如有个30人的电脑机房,我们就可以创建信号量的初始化值是30,表示30个可用资源,是的,信号量说白了就是共享资源的数量。另外我们要求一个同学使用一台电脑,这样每有一个同学使用一台电脑,那么信号量的数值就减一,直到30台电脑都被占用,此时信号量的数值就是0。如果此时还有几个同学没有电脑可以使用,那么这几个同学就得等待,直到有同学离开。有一个同学离开,那么信号量的数值就加1,有两个就加2,依此类推。刚才没有电脑用的同学此时就有电脑可以用了,有几个同学用,信号量就减几,直到再次没有电脑可以用,这么一个过程就是使用信号量来管理共享资源的过程。
以上两种应用的最大区别就是,信号量计数值的初始值。
部分API
typedef void * QueueHandle_t;
typedef QueueHandle_t SemaphoreHandle_t;//信号量句柄,从这里也可以看到源码实现还是用的队列
xSemaphoreGive( SemaphoreHandle_t xSemaphore ) //给一个信号量
xSemaphoreTake( SemaphoreHandle_t xSemaphore,TickType_t xBlockTime)//获取信号量
xSemaphoreGiveFromISR(SemaphoreHandle_t xSemaphore,BaseType_t *pxHigherPriorityTaskWoken)
//给一个信号量,用于中断函数里面
可以看出以上的API与二值信号量API是一样的。
SemaphoreHandle_t xSemaphoreCreateCounting( UBaseType_t uxMaxCount, UBaseType_t uxInitialCount )
//创建计数信号量,uxMaxCount:表示最大计数值,uxInitialCount :初始化计数值
uxSemaphoreGetCount( SemaphoreHandle_t xSemaphore )//获取当前的计数
上面两个是计数信号量特有的API。
测试程序
总体设计:2个任务,1个计数信号量。
printcounttask:打印当前计数信号量的值;
serialtask:获取计数信号量,并打印获取之后的计数信号量的值;
串口中断:接收到串口数据后,给一个信号量。
创建任务和计数信号量
#define PRINT_COUNT_TASK_PRIO 1 //任务优先级
#define PRINT_COUNT_TASK_STK_SIZE 80 //任务堆栈大小
TaskHandle_t PrintCountTaskHandler; //任务句柄
void PrintCountFunc(void *pvParameters); //任务函数
#define SERIAL_TASK_PRIO 4 //任务优先级
#define SERIAL_TASK_STK_SIZE 80 //任务堆栈大小
TaskHandle_t SerialTaskHandler; //任务句柄
void SerialTaskFunc(void *pvParameters); //任务函数
SemaphoreHandle_t TaskToTrqSemaphoreCounting;
void OtherTest(void )
{
BaseType_t ret;
BoardInitMcu();
BoardInitPeriph();
TaskToTrqSemaphoreCounting=xSemaphoreCreateCounting( 10, 0 );
ret=xTaskCreate((TaskFunction_t )PrintCountFunc,
(const char* )"printcount",
(uint16_t )PRINT_COUNT_TASK_STK_SIZE,
(void* )NULL,
(UBaseType_t )PRINT_COUNT_TASK_PRIO,
(TaskHandle_t* )&PrintCountFunc);
ret=xTaskCreate((TaskFunction_t )SerialTaskFunc,
(const char* )"serialtask",
(uint16_t )SERIAL_TASK_STK_SIZE,
(void* )NULL,
(UBaseType_t )SERIAL_TASK_PRIO,
(TaskHandle_t* )&SerialTaskHandler);
vTaskStartScheduler();
}
任务函数
void PrintCountFunc(void *pvParameters)
{
UBaseType_t count;
for(;;)
{
count=uxSemaphoreGetCount(TaskToTrqSemaphoreCounting);
printf("before =%d\r\n",(int)count);
vTaskDelay(500);
}
}
void SerialTaskFunc(void *pvParameters)
{
UBaseType_t count;
for(;;)
{
if(xSemaphoreTake(TaskToTrqSemaphoreCounting,10))//阻塞10ms
{
count=uxSemaphoreGetCount(TaskToTrqSemaphoreCounting);
printf("after =%d\r\n",(int)count);
}
vTaskDelay(1000);
}
}
串口中断
void USART1_IRQHandler( void )
{
BaseType_t pxHigherPriorityTaskWoken=pdFALSE;
//省略部分代码,只列出重要部分
if(RESET != __HAL_UART_GET_FLAG(&UartHandle,UART_FLAG_IDLE))//空闲中断
{
__HAL_UART_CLEAR_IDLEFLAG(&UartHandle);
/*设置信号量*/
xSemaphoreGiveFromISR(TaskToTrqSemaphoreCounting,&pxHigherPriorityTaskWoken);
portYIELD_FROM_ISR(pxHigherPriorityTaskWoken);
}
}
运行结果