【FreeRTOS】RTOS任务的同步与互斥:(二)信号量

【FreeRTOS】RTOS任务的同步与互斥:(二)信号量

同步与互斥是学习FreeRTOS的重点内容,同步与互斥的相关的内容可参考:
FreeRTOS入门教程(同步与互斥)
RTOS任务的同步与互斥:(一)队列

同步与互斥的概念:

  • 同步: A等待B做完某件事

  • 互斥: 某一资源同一时间仅能有一个用户访问

  • RTOS同步与互斥的方式: 队列(queue)、信号量(semaphoe)、互斥量(mutex)、事件组(event group)、任务通知(task notification)等

信号量概念

  • 信号量(Semaphore),是在多任务环境下使用的一种机制,是可以用来保证两个或多个关键代码段不被并 发调用。
  • 信号量这个名字,我们可以把它拆分来看,信号可以起到通知信号的作用,然后我们的量还可以用来表示资源的数量,当我们的量只有0和1的时候,它就可以被称作二值信号量,只有两个状态,当我们的那个量没有限制的时候,它就可以被称作为计数型信号量。
  • 信号量也是队列的一种。

二值信号量

二值信号量概念

二值信号量其实就是一个长度为1,大小为零的队列,只有0和1两种状态,通常情况下,我们用它来进行互斥访问或任务同步。
互斥访问: 比如门钥匙,只有获取到钥匙才可以开门
任务同步: 比如采集完成环境数据后才能显示

二值信号量相关API函数

函数 描述
xSemaphoreCreateBinary() 使用动态方式创建二值信号量
xSemaphoreCreateBinaryStatic() 使用静态方式创建二值信号量
xSemaphoreGive() 释放信号量
xSemaphoreGiveFromISR() 在中断中释放信号量
xSemaphoreTake() 获取信号量
xSemaphoreTakeFromISR() 在中断中获取信号量

创建二值信号量

c 复制代码
SemaphoreHandle_t xSemaphoreCreateBinary( void )

返回值:

  • 成功,返回对应二值信号量的句柄;
  • 失败,返回 NULL 。

释放二值信号量

c 复制代码
BaseType_t  xSemaphoreGive( SemaphoreHandle_t xSemaphore )

参数:
xSemaphore:要释放的信号量句柄

返回值:

  • 成功,返回 pdTRUE
  • 失败,返回 errQUEUE_FULL

获取二值信号量

c 复制代码
BaseType_t  xSemaphoreTake( SemaphoreHandle_t xSemaphore, TickType_t xTicksToWait );

参数:

  • xSemaphore:要获取的信号量句柄
  • xTicksToWait:超时时间,0 表示不超时,portMAX_DELAY表示卡死等待;

返回值:

  • 成功,返回 pdTRUE
  • 失败,返回 errQUEUE_FULL 。

二值信号量的案例设计

创建一个二值信号量,按下 KEY1 则释放信号量,按下 KEY2 获取信号量。
注意:

cubeMX配置

创建2个任务

动态方式创建一个二值信号量

软件程序设计

通过CubeMX生成的代码,已经为我们创建好了个任务及其信号量,后续用户只需要写任务逻辑即可

任务一检测到按键一按下放入信号量

c 复制代码
void task1(void const * argument)
{
	uint8_t key;
	while(1)
	{
		key = key_scan();			//扫描按键
		if(key == KEY1_PRES)	//按键一按下
		{
			//printf("key1 按下\r\n");
			if(xSemaphoreGive(binarySem1Handle) == pdTRUE)	//放入信号量
				printf("二值信号量放入成功\r\n");
			else
				printf("二值信号量放入失败\r\n");
		}
		osDelay(10);
	}
}

任务二检测到按键二按下获取信号量(以阻塞的方式获取为例)

c 复制代码
void task2(void const * argument)
{
	uint8_t key;
	while(1)
	{
		key = key_scan();
		if(key == KEY2_PRES)
		{
			//printf("key2 按下\r\n");
			//以阻塞等待的方式获取信号量
			if(xSemaphoreTake(binarySem1Handle,portMAX_DELAY) == pdTRUE)	
				printf("二值信号量获取成功\r\n");
			else
				printf("二值信号量获取失败\r\n");
		}
		osDelay(10);
	}
}

注意: 系统封装的创新信号量函数 osSemaphoreCreate 在创建的时候默认已经放入了一个信号量,因此需要先读取信号量后才能放入成功,当然可以注释系统的默认创建,通过xSemaphoreCreateBinary函数重新创建一个空的信号量,这里采用默认创建。

测试效果:


计数型信号量

计数型信号量概念

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

计数信号量的停车位模拟图

计数型信号量相关API函数

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

创建计数型信号量函数原型

cpp 复制代码
SemaphoreHandle_t xSemaphoreCreateCounting( UBaseType_t uxMaxCount, UBaseType_t uxInitialCount);
参数 描述
uxMaxCount 可以达到的最大计数值
uxInitialCount 创建信号量时分配给信号量的计数值

返回值:

  • 成功,返回对应计数型信号量的句柄;
  • 失败,返回 NULL 。

计数型信号量的释放和获取与二值信号量完全相同 !!!
释放二值信号量

c 复制代码
BaseType_t  xSemaphoreGive( SemaphoreHandle_t xSemaphore )

参数:
xSemaphore:要释放的信号量句柄

返回值:

  • 成功,返回 pdTRUE
  • 失败,返回 errQUEUE_FULL

获取二值信号量

c 复制代码
BaseType_t  xSemaphoreTake( SemaphoreHandle_t xSemaphore, TickType_t xTicksToWait );

参数:

  • xSemaphore:要获取的信号量句柄
  • xTicksToWait:超时时间,0 表示不超时,portMAX_DELAY表示卡死等待;

返回值:

  • 成功,返回 pdTRUE
  • 失败,返回 errQUEUE_FULL 。

二值信号量的案例设计

创建一个计数型信号量,按下 KEY1 则释放信号量,按下 KEY2 获取信号量。

cubeMX配置

创建2个任务 :同二值信号量及其任务的管理一样,创建两个任务用于检测按键并实现逻辑业务控制程序

使能计数型信号量

创建一个计数型信号量 :以默认的动态方式创建即可,核心需要修改信号量的名称和计数型信号量的长度

软件程序设计

通过CubeMX生成的代码,已经为我们创建好了个计数型信号量,同二值信号量一样,默认创建的初值量为最大值,这里使用创建,使得信号量初始值为0。

任务一检测到按键一按下放入计数型信号量

cpp 复制代码
void task1(void const * argument)
{
	uint8_t key;
	while(1)
	{
		key = key_scan();			//扫描按键
		if(key == KEY1_PRES)	//按键一按下
		{
			//printf("key1 按下\r\n");
			if(xSemaphoreGive(countingSem1Handle) == pdTRUE)	//放入信号量
				printf("计数型信号量放入成功\r\n\r\n");
			else
				printf("计数型信号量放入失败\r\n\r\n");
		}
		osDelay(10);
	}
}

任务二检测到按键二按下读取计数型信号量

这里获取信号量采用非阻塞方式,或信号量为空,则返回获取失败。

cpp 复制代码
void task2(void const * argument)
{
	uint8_t key;
	while(1)
	{
 
		key = key_scan();
		if(key == KEY2_PRES)
		{
			//printf("key2 按下\r\n");
			//以阻塞等待的方式获取信号量
			if(xSemaphoreTake(countingSem1Handle,portMAX_DELAY) == pdTRUE)	
				printf("计数型信号量获取成功\r\n\r\n");
			else
				printf("计数型信号量获取失败\r\n\r\n");
		}
		osDelay(10);
	}
}

测试:

相关推荐
远望创客学堂8 小时前
【单片机毕业设计选题24047】-基于阿里云的工地环境监测系统
stm32·单片机·嵌入式硬件·阿里云·云计算·课程设计·arduino
极客小张8 小时前
利用 STM32 实现多协议物联网网关:Modbus/Zigbee 到以太网/Wi-Fi 的数据桥接
stm32·单片机·嵌入式硬件·物联网·网络协议·https·硬件工程
@一二三四五10 小时前
STM32 看门狗 HAL
stm32·单片机·嵌入式硬件
快秃头的码农15 小时前
STM32-HAL-FATFS(文件系统)(没做完,stm32f103zet6(有大佬的可以在评论区说一下次板子为什么挂载失败了))
stm32·单片机·嵌入式硬件
嵌入式详谈20 小时前
STM32智能家居安防系统教程
stm32·嵌入式硬件·智能家居
YONYON-R&D20 小时前
STM32中的I2S(Inter-IC Sound)接口和SA接口(Serial Audio Interface)的区别
stm32·单片机·嵌入式硬件
heater4041 天前
【STM32标准库】USART+DMA
stm32·单片机·嵌入式硬件
小小怪下士的编程小屋2 天前
stm32中断
c语言·stm32·单片机·嵌入式硬件
ooolmf2 天前
蓝牙模块的使用01,OOOLMF蓝牙模块HC05调试使用01AT设置从机,手机用软件对接
stm32·单片机·51单片机
岁月磨吾少年志2 天前
【STM32】温湿度采集与OLED显示
stm32·单片机·嵌入式硬件