FreeRTOS - 如何根據FreeRTOS提供的功能(信號量、任務通知、隊列等)設計程序


原文地址:http://www.cnblogs.com/god-of-death/p/6917837.html

 

1、二值信號量

就像一個標志位,事件產生置一,事件處理后直零

用於任務之間的同步,即一個任務 give token,另一個任務 take token

 

特別提醒:

V7.X版本中使用vSemaphoreCreateBinary函數,使用該函數創建的信號量初始值為“滿”
V8.X版本以后版本中使用xSemaphoreCreateBinary函數,使用該函數創建的信號量初始值為“空”

 

2、計數信號量

事件產生加一,事件處理減一,減到零表示事件處理完畢

 

3、中斷推遲處理

把一部分原本放在中斷服務函數的程序放在中斷外,減少中斷服務函數運行時間

由於中斷服務函數越短越好(處理時間越短越好),把關鍵處理放中斷服務函數,其他放到外面,外面可以是一個任務(靈活性大,因為會用到二值信號量或計數信號量,需要為每個信號量創建一個任務,耗用資源多),也可以是定時器守護任務的回調函數xTimerPendFunctionCallFromISR()(定時器守護任務使用到一個命令隊列,只要向隊列發送信號就可以執行相應代碼,所以可以實現“中斷推遲處理”功能;只用到定時器守護任務這一個任務,節省資源,但建議回調函數執行時間短一些,否則影響其他定時器回調函數的執行周期)

 

 4、任務通知

任務通知一定程度上可以替代二值信號量、計數信號量、事件組或隊列。

 

任務通知優點:更快、占用RAM少

任務通知缺點:數據不能從任務發送到ISR(也就是ISR中不能讀取任務通知);接收處理任務通知只能在本任務中;任務通知只能通過32位無符號整數傳遞數據;當任務為“pending”,發送任務通知API不會等待任務變為“not-pending”而阻塞,也就是數據可能丟失

 

任務狀態:接收到通知為處理為“pending”,讀取通知值狀態變為“not-pending”

xTaskNotifyGive() 是xTaskNotify() 的閹割版

ulTaskNotifyTake() 是xTaskNotifyWait() 的閹割版

 

調用發送函數產生的影響:

調用xTaskNotifyGive()后,通知值加一,ucNotifyState變為"pending"

調用xTaskNotify()后,通知值可以不變、指定位置一、加一、強制重寫通知值或者ucNotifyState為"not-pending"狀態時寫通知值,ucNotifyState變為"pending"。第三個參數如果是eSetBits ,是使 notification value 的某一位置一,可以同時對notification value 的某幾位置一,然后同時處理幾件事情;如果第三個參數是eSetValueWithOverwrite ,是設置 notification value 為某個值。

 

接收函數如何判斷是否接收到未讀通知:

ulTaskNotifyTake()判斷是否有未讀通知是根據通知值ulNotifiedValue是否為零,該函數可以實現通知值減一或清零。相對來說,該函數實現主要是針對信號量那種類型(xClearCountOnExit 為pdTRUE替代二值信號量;為pdFALSE替代計數信號量);

xTaskNotifyWait()判斷是否有通知是依據通知狀態ucNotifyState, 該函數可以實現清除指定位。這里,通知值才算真正承載了有用的通知內容。

 

 5、互斥

 當一個資源不能被同時使用時使用互斥,比如打印機。一般使用情況是在某個任務中 take token,並且在這個任務中 give toke

使用互斥可能出現的問題:

1、互斥可能會導致高優先級任務等待低優先級任務,如下圖:

2、也可能導致互鎖,兩個任務都各自持有對方想要的互斥信號,導致兩個任務都無法進行,所以建議任務等待互斥信號的時間不要無限長,超時可以做一些處理,可以發現死鎖情況

3、也可能導致自鎖,比如某個函數對任務進行遞歸調用;可以通過遞歸互斥(recursive mutexes)解決

4、也可能某個任務一直沒執行,如下圖,但是可以通過調用taskYIELD() 解決

 

6、Gatekeeper Tasks

 對資源進行保護,使得只有一個任務能直接訪問資源

互斥量是為了保護“打印機”類型的事件不被破壞,看門人任務和隊列或者計數信號量配合也可以實現這個功能,具體就是只有看門人任務可以直接使用“打印機”,看門人任務大多時間阻塞等待打印數據到來,任何其他任務想使用打印機只能向看門人任務發送打印數據

 

7、掛起調度器、關閉中斷、進入臨界區

使某些關鍵代碼段不被打斷的進行

可以使用的API如下:

1、taskDISABLE_INTERRUPTS() 和 taskENABLE_INTERRUPTS()

             屏蔽可屏蔽中斷,包括調度器也不能工作(即不能任務切換,因為調度器也是基於中斷),保證某個代碼段不被打斷的執行;但是不支持嵌套(即如果調用了兩個DISABLE,調用一個ENABLE也可以使中斷正常工作,嵌套的意思是一對DISABLE和ENABLE里面包含了其他對DISABLE和ENABLE);當調度器掛起,FreeRTOS API不可以被調用

2、taskENTER_CRITICAL() 和 taskEXIT_CRITICAL()

             和1的區別就是支持嵌套,即調用了幾個ENTER,就要調用幾個EXIT才能使能中斷;當調度器掛起,FreeRTOS API不可以被調用
3、vTaskSuspendAll() 和 xTaskResumeAll()
             只是不能進行任務切換,但是沒有屏蔽中斷;1和2不僅不能任務切換,還不能響應中斷;當調度器掛起,FreeRTOS API不可以被調用

 

8、隊列

攜帶信息進行任務間通信

1、不同於二值信號量和計數信號量,隊列的原理是FIFO,可以存放數據

2、創建隊列時隊列的元素個數和元素的類型就已經確定了,只能往隊列存放規定的元素類型

3、可以創建隊列集合,隊列集合里面可以放隊列、計數信號量或二值信號量,使用隊列集合適用於多個任務向某個任務發送數據,而數據類型不相同,不同數據類型的數據存放在不同的隊列集合成員里。

4、隊列可以實現郵箱功能,即多個任務可以讀取長度為1的隊列里的數據,但不會清除數據,即使隊列有數據但沒有更新,調用xQueuePeek()也不會阻塞;任務往此隊列寫數據是覆蓋里面的數據。適用於向多個任務發送數據,即一對多通信,即廣播。

 

9、事件組

任務間、任務與中斷等多個事件的同步

事件組可以用於某個任務等待幾個條件都滿足或某個條件滿足才解除阻塞的情景(多個發送,一個接收,即多對一通信),或者幾個任務互相等待條件滿足才進一步執行任務,比如task A,B,C需要進一步執行各自的任務,需要task A,B,C都滿足條件(多個發送,多個接收,即多對多通信)。

調用這個函數xEventGroupSetBitsFromISR() ,實際是在定時器守護任務中執行,說有configUSE_TIMERS 和 INCLUDE_xTimerPendFunctionCall 需要置一 

10、內存動態申請

如果單片機的RAM比較小,分配給FreeRTOS的heap比較少,可以使用內存動態申請pvPortMalloc定義大數組,從而減小分配給任務棧的大小

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM