信号量(Semaphores)
信号量是一种解决同步问题的机制,可以实现对共享资源的有序访问
任务可以通过两种操作来访问信号量:
- 等待:如果信号量的值大于0,表示有可用资源,信号量的值减1,任务继续执行。如果信号量的值为0,表示没有可用资源,任务将被阻塞,直到信号量的值变为大于0。
- 释放:当任务完成对共享资源的访问后,它会释放信号量,即信号量的值加1。如果有任务因为等待该信号量而被阻塞,那么释放操作将唤醒(或称为解阻塞)其中一个等待的任务,使其能够继续执行。
信号量:用于传递状态
队列与信号量的对比
队列 |
信号量 |
可以容纳多个数据; 创建队列有两部分内存:队列结构体+队列项存储空间 |
仅存放计数值,无法存放其他数据; 创建信号量,只需分配信号量结构体 |
写入队列:当队列满时,可阻塞; |
释放信号量:不可阻塞,计数值++, 当计数值为最大值时,返回失败 |
读取队列:当队列为空时,可阻塞; |
获取信号量:计数值--, 当没有资源时,可阻塞 |
二值信号量
二值信号量的本质是一个队列长度为 1 的队列 ,该队列就只有空和满两种情况,这就是二值
二值信号量通常用于互斥访问或任务同步, 与互斥信号量比较类似,但是二值信号量有可能会导致优先级翻转的问题 ,所以二值信号量更适合用于同步!
相关API函数
使用二值信号量的过程:创建二值信号量 释放二值信号量 获取二值信号量
函数 |
描述 |
xSemaphoreCreateBinary () |
使用动态方式创建二值信号量 |
xSemaphoreCreateBinaryStatic () |
使用静态方式创建二值信号量 |
xSemaphoreGive () |
释放信号量 |
xSemaphoreGiveFromISR () |
在中断中释放信号量 |
xSemaphoreTake () |
获取信号量 |
xSemaphoreTakeFromISR () |
在中断中获取信号量 |
创建二值信号量函数:
SemaphoreHandle_t xSemaphoreCreateBinary ( void )
#define xSemaphoreCreateBinary( ) \
QueueGenericCreate ( 1,
semSEMAPHORE_QUEUE_ITEM_LENGTH ,
queueQUEUE_TYPE_BINARY_SEMAPHORE )
#define semSEMAPHORE_QUEUE_ITEM_LENGTH ( ( uint8_t ) 0U )
返回值 |
描述 |
NULL |
创建失败 |
其他值 |
创建成功返回二值信号量的句柄 |
不同信号量创建的本质函数还是 QueueGenericCreate 只是不同信号量的第三个入口参数不同代表了不同信号量。
#define queueQUEUE_TYPE_BASE ( ( uint8_t ) 0U ) /* 队列 */
#define queueQUEUE_TYPE_SET ( ( uint8_t ) 0U ) /* 队列集 */
#define queueQUEUE_TYPE_MUTEX ( ( uint8_t ) 1U ) /* 互斥信号量 */
#define queueQUEUE_TYPE_COUNTING_SEMAPHORE ( ( uint8_t ) 2U ) /* 计数型信号量 */
#define queueQUEUE_TYPE_BINARY_SEMAPHORE ( ( uint8_t ) 3U ) /* 二值信号量 */
#define queueQUEUE_TYPE_RECURSIVE_MUTEX ( ( uint8_t ) 4U ) /* 递归互斥信号量 */
释放二值信号量函数:
BaseType_t xSemaphoreGive( xSemaphore )
#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 |
释放信号量失败 |
#define queueSEND_TO_BACK ( ( BaseType_t ) 0 )
#define queueSEND_TO_FRONT ( ( BaseType_t ) 1 )
#define queueOVERWRITE ( ( BaseType_t ) 2 )
获取二值信号量函数:
BaseType_t xSemaphoreTake( xSemaphore, xBlockTime )
形参 |
描述 |
xSemaphore |
要获取的信号量句柄 |
xBlockTime |
阻塞时间 |
返回值 |
描述 |
pdTRUE |
获取信号量成功 |
pdFALSE |
超时,获取信号量失败 |
二值信号量实验
实验目的:学习 FreeRTOS 的二值信号量相关API函数的使用
实验设计:将设计三个任务:start_task、task1、task2
三个任务的功能如下:
- start_task:用来创建其他的2个任务
- task1:用于按键扫描,当检测到按键KEY0被按下时,释放二值信号量
- task2:获取二值信号量,当成功获取后打印提示信息
void start_task(void *pvParameters)
{
taskENTER_CRITICAL(); /* 进入临界区 */
/* 创建二值信号量 */
BinarySemaphore = xSemaphoreCreateBinary();
if (BinarySemaphore!= NULL)
{
printf("信号量创建成功\r\n");
}
/* 创建任务1 */
xTaskCreate((TaskFunction_t )task1,
(const char* )"task1",
(uint16_t )TASK1_STK_SIZE,
(void* )NULL,
(UBaseType_t )TASK1_PRIO,
(TaskHandle_t* )&Task1Task_Handler);
/* 创建任务2 */
xTaskCreate((TaskFunction_t )task2,
(const char* )"task2",
(uint16_t )TASK2_STK_SIZE,
(void* )NULL,
(UBaseType_t )TASK2_PRIO,
(TaskHandle_t* )&Task2Task_Handler);
vTaskDelete(StartTask_Handler); /* 删除开始任务 */
taskEXIT_CRITICAL(); /* 退出临界区 */
}
void task1(void *pvParameters)
{
uint8_t key = 0;
while (1)
{
key = key_scan(0);
if (key == KEY0_PRES)
{
xSemaphoreGive(BinarySemaphore); /* 释放二值信号量 */
}
vTaskDelay(10);
}
}
void task2(void *pvParameters)
{
uint32_t task_num = 0;
BaseType_t value;
while (1)
{
value = xSemaphoreTake(BinarySemaphore, portMAX_DELAY); /* 获取二值信号量 */
if(value == pdTRUE)
{
task_num++;
printf("成功获取信号量;获取次数:%u\r\n",task_num);
}
}
}
其他部分代码可自行查看之前博文;亦或者查看正点原子相关视频;代码部分相比源码有改编;