文章目录
- 一、二值信号量简介
- 二、二值信号量相关的API函数
- 2.1.动态方式创建二值信号量
- 2.2.获取信号量
- 2.3.释放信号量
- 三、实验
- 3.1.实验设计
- 3.2.软件设计
一、二值信号量简介
二值信号量的本质是一个队列长度为 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
打印出来: