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

相关推荐
qq_401700415 天前
FreeRtos——9、状态机(FSM)与面向对象在 RTOS 中的使用
freertos
济6175 天前
FreeRTOS基础--堆栈概念与汇编指令实战解析
汇编·嵌入式·freertos
嵌入式×边缘AI:打怪升级日志5 天前
基于ESP32S3的智能终端项目--5.显示时间和天气功能
笔记·esp32·freertos·天气·日历
嵌入式×边缘AI:打怪升级日志5 天前
基于ESP32S3的智能终端项目--4.1 FreeRTOS 任务调度&&设置屏幕亮度
freertos·屏幕亮度
炸膛坦客6 天前
FreeRTOS 学习:(二十九)任务切换的底层逻辑(了解)
单片机·操作系统·freertos
qq_401700416 天前
FreeRtos——1、多任务与“上下文切换”的代价
freertos
螺丝钉的扭矩一瞬间产生高能蛋白6 天前
深入剖析FreeRTOS优先级继承机制:vTaskPriorityInherit与xTaskPriorityDisinherit源码解析
stm32·freertos·嵌入式软件·优先级反转
济6176 天前
FreeRTOS基础知识---为什么使用FreeRTOS以及其核心功能
嵌入式·freertos
炸膛坦客7 天前
FreeRTOS 学习:(二十八)任务调度器 + 启动第一个任务(了解)
stm32·单片机·操作系统·freertos
炸膛坦客7 天前
FreeRTOS 学习:(二十七)死等延时函数会对任务调度产生什么影响
stm32·操作系统·freertos