FreeRTOS訊號量
FreeRTOS中訊號量又分為二值訊號量、計數型訊號量、互斥訊號量和遞迴互斥訊號量
1:二值訊號量
二值訊號量通常用於互斥訪問或同步,二值訊號量和互斥訊號量非常類似,但是還是有一
些細微的差別,互斥訊號量擁有優先順序繼承機制,二值訊號量沒有優先順序繼承。因此二值訊號
另更適合用於同步(任務與任務或任務與中斷的同步),而互斥訊號量適合用於簡單的互斥訪問.
建立:
函式名 | 原型 | 功能 |
---|---|---|
vSemaphoreCreateBinary () | void vSemaphoreCreateBinary( SemaphoreHandle_t xSemaphore ) | 動 態 創 建 二 值 信 號 量(舊) |
xSemaphoreCreateBinary() | SemaphoreHandle_t xSemaphoreCreateBinary( void ) | 動 態 創 建 二 值 信 號 量 |
xSemaphoreCreateBinaryStatic() | SemaphoreHandle_t xSemaphoreCreateBinaryStatic( StaticSemaphore_t *pxSemaphoreBuffer ) | 靜態建立二值訊號量 |
*新版函式建立的二值訊號量預設是無效的,而老版本是有效的
釋放:
函式名 | 原型 | 功能 |
---|---|---|
xSemaphoreGive() | BaseType_t xSemaphoreGive( xSemaphore ) | 任務級訊號量釋放函式 |
xSemaphoreGiveFromISR() | BaseType_t xSemaphoreGiveFromISR( SemaphoreHandle_t xSemaphore,BaseType_t * pxHigherPriorityTaskWoken) | 中斷級訊號量釋放函式 |
獲取:
“帶走(Taking)”一個訊號量意為”獲取(Obtain)”或”接收(Receive)”訊號量。只有當訊號量有效的時候才可以被獲取。
函式名 | 原型 | 功能 |
---|---|---|
xSemaphoreTake() | BaseType_t xSemaphoreTake(SemaphoreHandle_t xSemaphore,TickType_t xBlockTime) | 任務級獲取訊號量函式 |
xSemaphoreTakeFromISR() | BaseType_t xSemaphoreTakeFromISR(SemaphoreHandle_t xSemaphore,BaseType_t * pxHigherPriorityTaskWoken) | 中斷級獲取訊號量函式 |
應用:
taskENTER_CRITICAL();
SemaphoreHandle_t BinarySemaphore; //二值訊號量控制代碼
BinarySemaphore=xSemaphoreCreateBinary();
taskEXIT_CRITICAL(); //退出臨界區
...........................
void task (void )
{
BaseType_t err=pdFALSE;
while(1)
{
if(BinarySemaphore !=NULL) //檢查訊號量是否成功建立,進入處理
{
err=xSemaphoreTake(BinarySemaphore,portMAX_DELAY);//獲取訊號量
if(err==pdTRUE)//獲取成功
{
............
}
}
}
}
...........................
void XXX_IRQ(void)
{
//釋放二值訊號量
if(BinarySemaphore!=NULL)
{
xSemaphoreGiveFromISR(BinarySemaphore,&xHigherPriorityTaskWoken);
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);//如果需要的話進行一次任務切換
}
}
2:計數型訊號量
計數型訊號量叫做數值訊號量,二值訊號量相當於長度為 1 的佇列,那麼計數型訊號量就是長度大於 1 的佇列。同二值訊號量一樣,使用者不需要關心佇列中儲存了什麼資料,只需要關心佇列是否為空即可。計數型訊號量通常用於如下兩個場合:
1 、事件計數
在這個場合中,每次事件發生的時候就在事件處理函式中釋放訊號量(增加訊號量的計數值),其他任務會獲取訊號量(訊號量計數值減一,訊號量值就是佇列結構體成員變數uxMessagesWaiting)來處理事件。在這種場合中建立的計數型訊號量初始計數值為 0。
2 、資源管理
在這個場合中,訊號量值代表當前資源的可用數量,比如停車場當前剩餘的停車位數量。一個任務要想獲得資源的使用權,首先必須獲取訊號量,訊號量獲取成功以後訊號量值就會減一。當訊號量值為 0 的時候說明沒有資源了。當一個任務使用完資源以後一定要釋放訊號量,釋放訊號量以後訊號量值會加一。在這個場合中建立的計數型訊號量初始值應該是資源的數量,比如停車場一共有 100 個停車位,那麼建立訊號量的時候訊號量值就應該初始化為 100。
建立:
函式名 | 原型 | 功能 |
---|---|---|
xSemaphoreCreateCounting() | SemaphoreHandle_t xSemaphoreCreateCounting(UBaseType_t uxMaxCount,UBaseType_t uxInitialCount ) | 使用動態方法建立計數型訊號量。 |
xSemaphoreCreateCountingStatic() | SemaphoreHandle_t xSemaphoreCreateCountingStatic( UBaseType_t uxMaxCount,UBaseType_t uxInitialCount,StaticSemaphore_t * pxSemaphoreBuffer ) | 使用靜態方法建立計數型訊號量 |
釋放和獲取: 計數型訊號量的釋放和獲取與二值訊號量相同
應用:
//計數型訊號量控制代碼
SemaphoreHandle_t CountSemaphore;//計數型訊號量
taskENTER_CRITICAL(); //進入臨界區
CountSemaphore=xSemaphoreCreateCounting(255,0);
taskEXIT_CRITICAL(); //退出臨界區
.............................................
u8 semavalue;
BaseType_t err;
if(CountSemaphore!=NULL) //檢查訊號量是否成功建立,計數型訊號量建立成功
{
err=xSemaphoreGive(CountSemaphore);//釋放計數型訊號量
if(err==pdFALSE){printf("訊號量釋放失敗!!!\r\n");}
semavalue=uxSemaphoreGetCount(CountSemaphore); //獲取計數型訊號量值
}
.............................................
{
u8 semavalue;
xSemaphoreTake(CountSemaphore,portMAX_DELAY); //等待數值訊號量
semavalue=uxSemaphoreGetCount(CountSemaphore); //獲取數值訊號量值
}
3:互斥訊號量
在使用二值訊號量時候會遇到優先順序翻轉這一情況,引申出來互斥訊號量這一概念。
互斥訊號量其實就是一個擁有優先順序繼承的二值訊號量,在同步的應用中(任務與任務或中斷與任務之間的同步)二值訊號量最適合。
互斥訊號量不能用於中斷服務函式中.
建立:
函式名 | 原型 | 功能 |
---|---|---|
xSemaphoreCreateMutex() | SemaphoreHandle_t xSemaphoreCreateMutex( void ) | 使用動態方法建立互斥訊號量。 |
xSemaphoreCreateMutexStatic() | SemaphoreHandle_t xSemaphoreCreateMutexStatic( StaticSemaphore_t *pxMutexBuffer ) | 使用靜態方法建立互斥訊號量 |
釋放和獲取: 計數型訊號量的釋放和獲取與二值訊號量相同
應用:
taskENTER_CRITICAL(); //進入臨界區
MutexSemaphore=xSemaphoreCreateMutex();
taskEXIT_CRITICAL(); //退出臨界區
void task1 (void )
{
xSemaphoreTake(MutexSemaphore,portMAX_DELAY); //獲取互斥訊號量
...............
xSemaphoreGive(MutexSemaphore); //釋放訊號量
}
void task2 (void )
{
xSemaphoreTake(MutexSemaphore,portMAX_DELAY); //獲取互斥訊號量
...............
xSemaphoreGive(MutexSemaphore); //釋放訊號量
}
4:遞迴互斥訊號量
遞迴互斥訊號量可以看作是一個特殊的互斥訊號量,已經獲取了互斥訊號量的任務就不能
再次獲取這個互斥訊號量,但是遞迴互斥訊號量不同,已經獲取了遞迴互斥訊號量的任務可以
再次獲取這個遞迴互斥訊號量,而且次數不限!
要使用遞迴互斥訊號量的話巨集 configUSE_RECURSIVE_MUTEXES 必須為 1!
建立:
函式名 | 原型 | 功能 |
---|---|---|
xSemaphoreCreateRecursiveMutex() | SemaphoreHandle_t xSemaphoreCreateRecursiveMutex( void ) | 使用動態方法建立遞迴互斥訊號量。 |
xSemaphoreCreateRecursiveMutexStatic() | SemaphoreHandle_t xSemaphoreCreateRecursiveMutexStatic( StaticSemaphore_t *pxMutexBuffer ) | 使用靜態方法建立遞迴互斥訊號量 |
釋放和獲取: 計數型訊號量的釋放和獲取與二值訊號量相同
應用:
SemaphoreHandle_t RecursiveMutex; //遞迴互斥訊號量控制代碼
//某個任務中建立一個遞迴互斥訊號量
void vATask( void * pvParameters )
{
//沒有建立建立遞迴互斥訊號量之前不要使用!
RecursiveMutex = xSemaphoreCreateRecursiveMutex(); //建立遞迴互斥訊號量
for( ;; )
{
/************任務程式碼**************/
}
}
//任務呼叫的使用遞迴互斥訊號量的功能函式。
void vAFunction( void )
{
/**********其他處理程式碼*****************/
if( xMutex != NULL )
{
//獲取遞迴互斥訊號量,阻塞時間為 10 個節拍
if( xSemaphoreTakeRecursive( RecursiveMutex, 10 ) == pdTRUE )
{
/***********其他處理過程*************/
//這裡為了演示,所以是順序的獲取遞迴互斥訊號量,但是在實際的程式碼中肯定
//不是這麼順序的獲取的,真正的程式碼中是混合著其他程式呼叫的。
xSemaphoreTakeRecursive( RecursiveMutex, ( TickType_t ) 10 );
xSemaphoreTakeRecursive( RecursiveMutex, ( TickType_t ) 10 );
//任務獲取了三次遞迴互斥訊號量,所以就得釋放三次!
xSemaphoreGiveRecursive( RecursiveMutex);
xSemaphoreGiveRecursive( RecursiveMutex);
xSemaphoreGiveRecursive( RecursiveMutex);
//遞迴互斥訊號量釋放完成,可以被其他任務獲取了
}
else
{
/**********遞迴互斥訊號量獲取失敗***********/
}
}
}