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 资源,因为按下按键之后,代码顺序是先消耗,再打印结果出来:

相关推荐
TangDuoduo00052 天前
【FreeRTOS空闲钩子函数、优先级函数、删除函数及调度器算法】
freertos
小曹要微笑2 天前
队列集详解
freertos·队列·队列集
wzfj123453 天前
FreeRTOS 学习方法
freertos
Zeku5 天前
20260103 - Linux平台总线LED驱动架构深度解析
stm32·freertos·linux驱动开发·linux应用开发
Zeku5 天前
20260102 - Linux驱动设计的思想
stm32·freertos·linux驱动开发·linux应用开发
Zeku5 天前
20260103 - Linux总线设备驱动模型学习笔记
stm32·freertos·linux驱动开发·linux应用开发
brave and determined6 天前
ESP32 FreeRTOS (day1)入门教程 (ESP-IDF版):从超级循环到多任务的系统化思维
操作系统·esp32·freertos·任务·任务调度器·任务控制块·嵌入式设计
森旺电子7 天前
STM32 上下文详解
stm32·单片机·嵌入式硬件·freertos
Zeku7 天前
20251231 - Linux 字符设备驱动开发笔记:分层设计
stm32·freertos·linux驱动开发·linux应用开发
Zeku7 天前
20251230 - 为什么Linux驱动开发中必须要用到ioremap来访问硬件?
stm32·freertos·linux驱动开发·linux应用开发