1. 程式人生 > >FreeRTOS訊號量

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
{
/**********遞迴互斥訊號量獲取失敗***********/
}
}
}