FreeRTOS—二值信号量

发布于:2025-07-25 ⋅ 阅读:(16) ⋅ 点赞:(0)

一、二值信号量简介

二值信号量的本质是一个队列长度为 1 的队列,该队列就只有空和满两种情况,也就是 0 和 1。通常用于互斥访问或任务同步,与互斥信号量比较类似,但是二值信号量有可能会导致优先级反转问题,所以二值信号量更适用于同步。

二、二值信号量相关的API函数

使用二值信号量的过程:创建二值信号量 - 释放信号量 - 获取信号量。下面表格是二值信号量关于任务间的API函数

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

2.1.动态方式创建二值信号量

动态创建的内存由 FreeRTOS 自动分配,静态创建的内存是由用户分配,该函数是一个宏,而且它与队列所调用的函数xQueueGenericCreate( )是相同的,只不过最后一个参数不一样:

#define xSemaphoreCreateBinary()            
		xQueueGenericCreate(( UBaseType_t ) 1, 
							  semSEMAPHORE_QUEUE_ITEM_LENGTH, 
							  queueQUEUE_TYPE_BINARY_SEMAPHORE) 

下面表格是它的返回值:

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

2.2.获取信号量

此函数用于获取信号量,如果信号量处于没有资源的状态,那么此函数可以选择将任务进行阻塞,如果成功获取了信号量,那信号量的资源数将会减1,资源数就是操作xQueueSemaphoreTake( )里面的uxMessagesWaiting变量。该函数实际上是一个宏定义,在 semphr.h 文件中有定义,具体的代码如下所示:

#define xSemaphoreTake(xSemaphore, xBlockTime)       
        xQueueSemaphoreTake((xSemaphore),   
            				(xBlockTime)) 

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

形参 描述
xSemaphore 要获得的信号量句柄
xBlockTime 阻塞时间

下面表格是它的返回值:

返回值 描述
pdTRUE 获取信号量成功
pdFALSE 超时,获取信号量失败

2.3.释放信号量

此函数用于释放信号量,如果信号量处于资源满的状态,那么此函数可续选择将任务进行阻塞,如果成功释放了信号量,那信号量的资源数将会加1。该函数实际上是一个宏定义,在 semphr.h 文件中有定义,具体的代码如下所示:

#define xSemaphoreGive(xSemaphore)         
        xQueueGenericSend(( QueueHandle_t ) ( xSemaphore ),    
           					NULL,       
          				    semGIVE_BLOCK_TIME,       
           					queueSEND_TO_BACK) 
#define semGIVE_BLOCK_TIME  ((TickType_t)0U)           					

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

形参 描述
xSemaphore 要释放的信号量句柄

下面表格是它的返回值:

返回值 描述
pdPASS 释放信号量成功
errQUEUE_FULL 释放信号量失败

三、实验

3.1.实验设计

本实验将设计三个任务:

  • start_task:用来创建 task1 和 task2
  • task1:用于按键扫描,当检测到按键 KEY0 被按下时,释放二值信号量
  • task2:获取二值信号量,当成功获取后打印提示信息

3.2.软件设计

在入口函数里面创建二值信号量:

void freertos_demo(void)
{
	semphr_handle = xSemaphoreCreateBinary();
	if(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:

void task1(void *pvParameters)
{
	uint8_t key = 0;
	BaseType_t err = 0;
	while(1)
	{
		key = key_scan(0);
		if(key == KEY0_PRES)
		{
			err = xSemaphoreGive(semphr_handle);
			if(err == pdPASS)
			{
				printf("信号量释放成功\r\n");
			}
		}
	}
}

接下来是 task2,里面实现了将时间限制在 3s 之内,如果 3s 之内不按下 KEY0 按键来释放信号量,就提示超时多少次,直到按下按键才提示获取信号量成功:

void task2(void *pvParameters)
{
	BaseType_t err = 0;
	uint8_t i = 0;
	while(1)
	{
		err = xSemaphoreTake(semphr_handle, 3000);
		if(err == pdFALSE)
		{
			printf("超时,%d\r\n",++i);
		}
		else if(err == pdTRUE)
		{
			printf("获取信号量成功\r\n");
		}
	}
}

下图是结果图,因为本实验将 task2 的任务优先级设置为 3,task1 的任务优先级设置为 2,所以出现了先获取信号量,再出现信号量释放成功,当程序执行到 task1 的xSemaphoreGive( )函数,task2 就抢占了CPU了,导致还没将 task1 的printf打印出来:
在这里插入图片描述


网站公告

今日签到

点亮在社区的每一天
去签到