FreeRTOS—计数型信号量

文章目录

一、计数型信号量简介

计数型信号量相当于队列长度大于 1 的队列,因此计数型信号量能够容纳多个资源,这在计数型信号量被创建的时候确定的。它的使用场合有:

  • 事件计数:当每次事件发生后,在事件处理函数中释放计数型信号量(计数值+1),其他任务会获取计数型信号量(计数值-1),这种场合一般在创建时将初始计数值设置为 0
  • 资源管理:信号量表示有效的资源数目,任务必须先获取信号量(信号量计数值-1)才能获取资源控制权,当计数值减为零时表示没有的资源。当任务使用完资源后,必须释放信号量(信号量计数值+1),信号量创建时计数值应等于最大资源数目

二、计数型信号量相关API函数

使用计数型信号量的过程:创建计数型信号量 - 释放信号量 - 获取信号量,该方法与使用二值信号量一致:

函数 描述
xSemaphoreCreateCounting( ) 使用动态方法创建计数型信号量
uxSemaphoreGetCount( ) 获取信号量的计数值

2.1.动态方法创建计数型信号量

此函数用于使用动态方式创建计数型信号量,创建计数型信号量所需的内存,由 FreeRTOS 从 FreeRTOS 管理的堆中进行分配。该函数实际上是一个宏定义,在 semphr.h 中有定义,具体的代码如下所示:

c 复制代码
#define xSemaphoreCreateCounting(uxMaxCount, uxInitialCount)      
		xQueueCreateCountingSemaphore((uxMaxCount),       
									  (uxInitialCount)) 

下面表格是它的形参和描述:

形参 描述
uxMaxCount 计数值的最大值限定
uxInitialCount 计数值的初始值

下面表格是它的返回值:

返回值 描述
NULL 创建失败
其他值 创建成功返回计数型信号量的句柄

2.2.获取信号量的计数值

此函数用于获取信号量当前计数值的大小:

c 复制代码
#define uxSemaphoreGetCount(xSemaphore)
		uxQueueMessagesWaiting((QueueHandle_t)(xSemaphore))

下面表格是它的形参和描述:

形参 描述
xSemaphore 信号量的句柄

下面表格是它的返回值:

返回值 描述
整数 当前信号量的计数值大小

三、实验

3.1.实验设计

本实验将设计三个任务:

  • start_task:用来创建 task1 和 task2 任务
  • task1:用于按键扫描,当检测到按键 KEY0 被按下时,释放计数型信号量
  • task2:每过一秒获取一次计数型信号量,当成功获取后打印信号量计数值

3.2.软件设计

和二值信号量创建一样,再入口函数里面创建,定义了最大释放数值为 10,从 0 开始计数:

c 复制代码
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:

c 复制代码
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 按键按下才有资源运行:

c 复制代码
void task2(void *pvParameters)
{
	while(1)
	{
		xSemaphoreTake(count_semphr_handle, portMAX_DELAY);
		printf("还剩%d资源\r\n",uxSemaphoreGetCount(count_semphr_handle));
		vTaskDelay(2000);
	}
}

下图是运行结果,为什么一开始按下按键就显示还剩 0 资源,因为按下按键之后,代码顺序是先消耗,再打印结果出来:

相关推荐
hsjiasb21 小时前
FreeRTOS学习(三十七)——常见错误与工程规范
学习·学习笔记·freertos
捷瑞电子工坊2 天前
FreeRTOS任务管理:从入门到实践(标准库版)
stm32·嵌入式·freertos·实时操作系统·任务管理·标准外设库·任务挂起与恢复
hsjiasb2 天前
FreeRTOS学习(三十五)——系统架构综合项目
学习·学习笔记·freertos
拾知_H3 天前
STM32+FreeRTOS基础配置
stm32·嵌入式硬件·freertos
qq_401700413 天前
RTOS的几种常见调度策略
freertos
圆奋奋3 天前
FreeRTOS学习(十)- 最核心的内容:优先级抢占式调度 + 上下文切换
学习·freertos
hsjiasb6 天前
FreeRTOS学习(二十七)——任务栈与栈溢出检测
开发语言·stm32·学习·学习笔记·freertos
捷瑞电子工坊6 天前
FreeRTOS 空闲任务与阻塞延时
freertos·嵌入式实时操作系统·空闲任务·阻塞延时·vtaskdelay
hsjiasb6 天前
FreeRTOS学习(二十九)——低功耗Tickless Idle
学习·学习笔记·freertos
捷瑞电子工坊6 天前
FreeRTOS 临界段:为什么关中断?怎么用才安全?
嵌入式·freertos·实时操作系统·临界段·临界区