FreeRTOS计数信号量
计数信号量
1. 计数信号量简介
计数型信号量有以下两种典型用法
- 事件计数:每次事件发生,事件处理函数将释放信号量(信号量计数值加 1),其他处理任务会获取信号量(信号量计数值减 1)来处理事件。因此,计数值是事件发生的数量和事件处理的数量差值。计数信号量在创建时其值为 0
- 资源管理:信号量表示有效的资源数目。任务必须先获取信号量才能获取资源控制权。当计数值减为零时表示没有的资源。当任务完成后,它会返还信号量(信号量计数值增加)。信号量创建时计数值应等于最大资源数目
计数信号量有释放信号量操作和获取信号量操作,释放信号量操作的时候计数器的值会加一,获取信号操作,计数器的值减一,如果减到 0 任务会进入到等待状态;具体操作方式有两种,如下图所示:
2. 计数信号量的 API 函数
2.1 创建计数信号量
1
2
3
4
5
6
7
8
9
/********************动态创建计数信号量**********************************************/
SemaphoreHandle_t xSemaphoreCreateCounting(UBaseType_t uxMaxCount,
UBaseType_t uxInitialCount)
/********************静态创建计数信号量**********************************************/
SemaphoreHandle_t xSemaphoreCreateCountingStatic(UBaseType_t uxMaxCount,//信号量最大计数值
UBaseType_t uxInitialCount,//计数信号量初始值
StaticSemaphore_t * pxSemaphoreBuffer)//保存信号量结构体
/***********************************************************************************/
返回值:创建成功返回计数信号量句柄;失败返回NULL
动态计数信号量创建函数是一个宏,最终是通过 xQueueCreateCountingSemaphore()
函数来完成,其源码如下:
1
2
3
4
#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 )
#define xSemaphoreCreateCounting( uxMaxCount, uxInitialCount ) \
xQueueCreateCountingSemaphore( (uxMaxCount),(uxInitialCount) )
#endif
1
2
3
4
5
6
7
8
9
10
11
12
13
14
QueueHandle_t xQueueCreateCountingSemaphore(const UBaseType_t uxMaxCount,const UBaseType_t uxInitialCount){
QueueHandle_t xHandle;
/* 调用消息队列创建函数,创建队列长度为uxMaxCount,队列项长度为0,类型为计数信号量的队列 */
xHandle = xQueueGenericCreate(uxMaxCount,queueSEMAPHORE_QUEUE_ITEM_LENGTH,queueQUEUE_TYPE_COUNTING_SEMAPHORE);
if( xHandle != NULL ){
/* 将计数信号量的初始值来设置uxMessagesWaiting,代表可用的资源数量 */
( ( Queue_t * ) xHandle )->uxMessagesWaiting = uxInitialCount;
traceCREATE_COUNTING_SEMAPHORE();
}
else{
traceCREATE_COUNTING_SEMAPHORE_FAILED();
}
return xHandle;
}
2.2 释放和获取计数信号量
计数型信号量的释放与获取与二值信号量相同,可参考二值信号量中的 3.2 和 3.3 章节
3. 计数信号量的应用实例
本实例的功能需求是使用两个按键触发计数信号量的释放和获取,并打印出当前信号量的计数值
使用 STM32CubeMX 将 FreeRTOS移植到工程中,创建一个按键处理任务、一个计数信号量
Keyscan_Task:扫描按键状态,根据不同的键值释放或者获取信号量
3.1 STM32CubeMX 设置
- RCC 设置外接 HSE,时钟设置为 72M
- PC0 设置为 GPIO 推挽输出模式、上拉、高速、默认输出电平为高电平
- USART1 选择为异步通讯方式,波特率设置为 115200Bits/s,传输数据长度为 8Bit,无奇偶校验,1 位停止位;开启串口中断
- 激活 FreeRTOS,添加 Keyscan_Task 任务,设置任务名称、优先级、堆栈大小、函数名称等参数
- 动态创建计数信号量,需要先使能 USE_COUNTING_SEMAPHORES
- 使用 FreeRTOS 操作系统,一定要将 HAL 库的 Timebase Source 从 SysTick 改为其他定时器,选好定时器后,系统会自动配置 TIM
- 输入工程名,选择路径(不要有中文),选择 MDK-ARM V5;勾选 Generated periphera initialization as a pair of ‘.c/.h’ files per IP ;点击 GENERATE CODE,生成工程代码
3.2 MDK-ARM 软件编程
- 创建按键驱动文件 key.c 和 key.h,参考按键输入例程
- 添加 KeyscanTask 任务函数代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
void KeyscanTask(void const * argument){
int8_t key;
int8_t sem_value;
BaseType_t err;
for(;;){
key = KEY_Scan(0);
if(CountingSemHandle != NULL){
switch(key){
case KEY_UP_PRES:
err = xSemaphoreTake(CountingSemHandle,portMAX_DELAY);
if(err == pdFALSE)
printf("Semaphore Take failed!\r\n");
else{
sem_value = uxSemaphoreGetCount(CountingSemHandle);
printf("Semaphore Take successed, Semvalue = %d\r\n",sem_value);
HAL_GPIO_WritePin(GPIOC,GPIO_PIN_0,GPIO_PIN_RESET);
}
break;
case KEY_DOWN_PRES:
err = xSemaphoreGive(CountingSemHandle);
if(err == pdFALSE)
printf("Semaphore Give failed!\r\n");
else{
sem_value = uxSemaphoreGetCount(CountingSemHandle);
printf("Semaphore Give successed, Semvalue = %d\r\n",sem_value);
HAL_GPIO_WritePin(GPIOC,GPIO_PIN_0,GPIO_PIN_SET);
}
break;
}
}
osDelay(10);
}
}
3.3 下载验证
编译无误下载到开发板后,按下 K_UP 按键获取信号,串口打印出当前信号量的计数值,同时点亮 LED0;按下 K_DOWN 按键释放信号量,串口打印出当前信号量的计数值,同时熄灭 LED0;信号量最大计数值设置为 10 了,因此信号量释放到 10 后,无法继续释放
以上转载自博主“安迪西嵌入式”的FreeRTOS专栏,仅作学习记录,如有侵权,请联系删除。
本文由作者按照 CC BY 4.0 进行授权