FreeRTOS(4)信号量

信号量API函数实际上都是宏,它使用现有的队列机制。这些宏定义在semphr.h文件中。如果使用信号量或者互斥量,需要包含semphr.h头文件。

img

一、创建信号量

  1. ==二进制信号量==创建实际上是直接使用通用队列创建函数xQueueGenericCreate()。创建二进制信号量API接口实际上是一个宏,定义如下:
1
2
3
4
5
6
7
8
9
#define xSemaphoreCreateBinary()         \
xQueueGenericCreate( \
( UBaseType_t ) 1, \
semSEMAPHORE_QUEUE_ITEM_LENGTH, \
NULL, \
NULL, \
queueQUEUE_TYPE_BINARY_SEMAPHORE\
)

2.==计数信号量==的创建间接使用通用队列创建函数xQueueGenericCreate()。创建计数信号量API接口同样是个宏定义:

1
2
3
#define xSemaphoreCreateCounting(uxMaxCount, uxInitialCount )             \
xQueueCreateCountingSemaphore( ( uxMaxCount ), ( uxInitialCount ), (NULL ) )

创建计数信号量API接口有两个参数,含义如下:

  • uxMaxCount:最大计数值,当信号到达这个值后,就不再增长了。
  • uxInitialCount:创建信号量时的初始值。

二、释放信号量

  1. xSemaphoreGive()

用于释放一个信号量,不带中断保护。被释放的信号量可以是二进制信号量、计数信号量和互斥量。注意递归互斥量并不能使用这个API函数释放。其实信号量释放是一个宏,真正调用的函数是xQueueGenericSend(),宏定义如下:

1
2
3
4
5
6
7
#define xSemaphoreGive( xSemaphore )                    \
xQueueGenericSend( \
( QueueHandle_t ) ( xSemaphore ), \
NULL, \
semGIVE_BLOCK_TIME, \
queueSEND_TO_BACK )

可以看出释放信号量实际上是一次入队操作,并且阻塞时间为0(由宏semGIVE_BLOCK_TIME定义)。

     对于二进制信号量和计数信号量,根据上一章的内容可以总结出,释放一个信号量的过程实际上可以简化为两种情况:第一,如果队列未满,队列结构体成员uxMessageWaiting加1,判断是否有阻塞的任务,有的话解除阻塞,然后返回成功信息(pdPASS);第二,如果队列满,返回错误代码(err_QUEUE_FULL),表示队列满。
  1. xSemaphoreGiveFromISR()

         用于释放一个信号量,带中断保护。被释放的信号量可以是**二进制信号量**和**计数信号量**。和普通版本的释放信号量API函数不同,它不能释放互斥量,这是因为互斥量不可以在中断中使用!互斥量的优先级继承机制只能在任务中起作用,在中断中毫无意义。带中断保护的信号量释放其实也是一个宏,真正调用的函数是xQueueGiveFromISR (),宏定义如下:
    
1
2
3
4
definexSemaphoreGiveFromISR( xSemaphore, pxHigherPriorityTaskWoken )     \
xQueueGiveFromISR( \
( QueueHandle_t ) ( xSemaphore), \
( pxHigherPriorityTaskWoken ) )

三、获取信号量

​ 无论二进制信号量、计数信号量还是互斥量,它们都使用相同的获取和释放API函数

在收到信号量之前,任务一直处于阻塞态并开始计时。

  • 若时间到了还没收到信号,会自动执行后边的语句并返回pdFALSE,如果收到了信号返回PDTRUE
  • 阻塞时间是xBlockTime

如果为0,则阻塞0ms,相当于一直在while循环,==如果有N个信号量,会被一瞬间消耗完==

如果阻塞HAL_MAXDELAY,则任务一直在阻塞态,if后边的语句将不会执行。

获取信号量分为不带中断保护和带中断保护两个版本。

  1. xSemaphoreTake()

         用于获取信号量,==该函数运行后,指定的信号量的队列长度-1==.不带中断保护。获取的信号量可以是二进制信号量、计数信号量和互斥量。注意递归互斥量并不能使用这个API函数获取。其实获取信号量是一个宏,真正调用的函数是xQueueGenericReceive (),宏定义如下:
    
1
2
3
4
5
6
definexSemaphoreTake( xSemaphore, xBlockTime )        \
xQueueGenericReceive( \
( QueueHandle_t ) ( xSemaphore ), \
NULL, \
( xBlockTime ), \
pdFALSE )
  1. xSemaphoreTakeFromISR()

         用于获取信号量,带中断保护。获取的信号量可以是二进制信号量和计数信号量。和普通版本的获取信号量API函数不同,它不能获取互斥量,这是因为互斥量不可以在中断中使用!互斥量的优先级继承机制只能在任务中起作用,在中断中毫无意义。带中断保护的获取信号量其实也是一个宏,真正调用的函数是xQueueReceiveFromISR (),宏定义如下:
    
1
2
3
4
5
definexSemaphoreTakeFromISR( xSemaphore, pxHigherPriorityTaskWoken )   \
xQueueReceiveFromISR( \
( QueueHandle_t ) ( xSemaphore ), \
NULL, \
( pxHigherPriorityTaskWoken ) )

四、得到指定信号量中队列长度

1
#define uxSemaphoreGetCount (xSemaphore  uxQueueMessagesWaiting( ( QueueHandle_t ) ( xSemaphore ) )
  • 如果xSemaphore是一个计数信号量,则返回它当前总的信号量个数
  • 如果xSemaphore是一个二值信号量,则它只会返回0或1

五、例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/* .h信号量全局定义 */
extern SemaphoreHandle_t KEY0_Semphr_Handler;
extern SemaphoreHandle_t KEY1_Semphr_Handler;

/* .c信号量声明 */
SemaphoreHandle_t KEY0_Semphr_Handler;
SemaphoreHandle_t KEY1_Semphr_Handler;

void Start_task(void *pvParameters)
{
taskENTER_CRITICAL();//代码进入临界区
/* 信号量句柄要在临界区赋值 */
KEY0_Semphr_Handler = xSemaphoreCreateCounting(255,0);//创建二值信号量
KEY1_Semphr_Handler = xSemaphoreCreateBinary();
taskEXIT_CRITICAL();//代码退出临界区
}