FreeRTOS—计数型信号量
文章目录
- 一、计数型信号量简介
- 二、计数型信号量相关API函数
- 2.1.动态方法创建计数型信号量
- 2.2.获取信号量的计数值
- 三、实验
- 3.1.实验设计
- 3.2.软件设计
一、计数型信号量简介
计数型信号量相当于队列长度大于 1 的队列,因此计数型信号量能够容纳多个资源,这在计数型信号量被创建的时候确定的。它的使用场合有:
- 事件计数:当每次事件发生后,在事件处理函数中释放计数型信号量(计数值+1),其他任务会获取计数型信号量(计数值-1),这种场合一般在创建时将初始计数值设置为 0
- 资源管理:信号量表示有效的资源数目,任务必须先获取信号量(信号量计数值-1)才能获取资源控制权,当计数值减为零时表示没有的资源。当任务使用完资源后,必须释放信号量(信号量计数值+1),信号量创建时计数值应等于最大资源数目
二、计数型信号量相关API函数
使用计数型信号量的过程:创建计数型信号量 - 释放信号量 - 获取信号量,该方法与使用二值信号量一致:
函数 | 描述 |
---|---|
xSemaphoreCreateCounting( ) | 使用动态方法创建计数型信号量 |
uxSemaphoreGetCount( ) | 获取信号量的计数值 |
2.1.动态方法创建计数型信号量
此函数用于使用动态方式创建计数型信号量,创建计数型信号量所需的内存,由 FreeRTOS 从 FreeRTOS 管理的堆中进行分配。该函数实际上是一个宏定义,在 semphr.h 中有定义,具体的代码如下所示:
#define xSemaphoreCreateCounting(uxMaxCount, uxInitialCount) xQueueCreateCountingSemaphore((uxMaxCount), (uxInitialCount))
下面表格是它的形参和描述:
形参 | 描述 |
---|---|
uxMaxCount | 计数值的最大值限定 |
uxInitialCount | 计数值的初始值 |
下面表格是它的返回值:
返回值 | 描述 |
---|---|
NULL | 创建失败 |
其他值 | 创建成功返回计数型信号量的句柄 |
2.2.获取信号量的计数值
此函数用于获取信号量当前计数值的大小:
#define uxSemaphoreGetCount(xSemaphore)uxQueueMessagesWaiting((QueueHandle_t)(xSemaphore))
下面表格是它的形参和描述:
形参 | 描述 |
---|---|
xSemaphore | 信号量的句柄 |
下面表格是它的返回值:
返回值 | 描述 |
---|---|
整数 | 当前信号量的计数值大小 |
三、实验
3.1.实验设计
本实验将设计三个任务:
- start_task:用来创建 task1 和 task2 任务
- task1:用于按键扫描,当检测到按键 KEY0 被按下时,释放计数型信号量
- task2:每过一秒获取一次计数型信号量,当成功获取后打印信号量计数值
3.2.软件设计
和二值信号量创建一样,再入口函数里面创建,定义了最大释放数值为 10,从 0 开始计数:
QueueHandle_t count_semphr_handle; //定义这个计数型信号量的句柄
void freertos_demo(void)
{count_semphr_handle = xSemaphoreCreateCounting(10,0);if(count_semphr_handle != NULL){printf("计数型信号量创建成功\r\n");}xTaskCreate((TaskFunction_t) start_task,(char*) "start_task",(uint16_t) START_TASK_STACK_SIZE,(void*) NULL,(UBaseType_t) START_TASK_PRIO,(TaskHandle_t*) &start_task_handler);vTaskStartScheduler();
}
下面是 task1 的代码,按下按键,就释放一次信号量,最多只能释放到 10:
void task1(void *pvParameters)
{uint8_t key = 0;BaseType_t err = 0;while(1){key = key_scan(0);if(key == KEY0_PRES){err = xSemaphoreGive(count_semphr_handle);if(err == pdPASS){printf("信号量释放成功\r\n");}}}
}
下面是 task2 的代码,每个 2s 就消耗一次资源,当资源为 0 时,task2 进入阻塞态,需要 task1 按键按下才有资源运行:
void task2(void *pvParameters)
{while(1){xSemaphoreTake(count_semphr_handle, portMAX_DELAY);printf("还剩%d资源\r\n",uxSemaphoreGetCount(count_semphr_handle));vTaskDelay(2000);}
}
下图是运行结果,为什么一开始按下按键就显示还剩 0 资源,因为按下按键之后,代码顺序是先消耗,再打印结果出来: