文章目錄
- 一、二值信號量簡介
- 二、二值信號量相關的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
打印出來: