實戰技能分享,減小開關中斷對系統實時性的影響,提升系統響應速度
一、背景知識:
(1)中斷延遲:從中斷觸發到執行中斷服務程式的第一條指令這段時間就是中斷延遲時間。
對於Cortex-M核心晶片,典型的中斷延遲時間是12-16個時鐘週期
以Cortex-M3/M4核心為例,中斷觸發後,執行時序效果如下,其中xPSR,PC,R0,R1,R2,R3,R12和LR是硬體自動入棧的:
(2)那麼問題來了,什麼是零中斷延遲:
零中斷延遲並不是說中斷延遲時間是0,而是中斷觸發後,延遲時間接近晶片特性的延遲時間。
這個裡面影響中斷延遲的關鍵就是開關中斷,關的時間越長,中斷延遲就越長,也是最影響系統實時性的。本帖就是針對這個問題,給大家提供一些實用的解決思路。
二、中斷延遲的影響:
如果原始碼裡面有較多的開關中斷,且關閉時間較長,會給系統帶來什麼問題呢?比如某個任務正在呼叫系統API函式,此時中斷正好也關閉了,如果有一個緊急的中斷事件被觸發,這個中斷就不能得到及時執行,必須等到中斷開啟才可以得到執行,如果關中斷時間超過了緊急中斷能夠容忍的限度,危害是可想而知的。
正是基於此,中斷延遲時間也是衡量RTOS實時作業系統的一項重要指標。
三、實戰應用場景一:儘量不要使用全域性開關中斷,使用區域性中斷
(1)一些外設驅動中,如果僅需開關自己的中斷就能完成效果,建議僅開關自己,不要做全域性中斷的開關。以我們的8個串列埠FIFO驅動為例:
我們可以修改下,僅對相應串列埠做開關中斷,這就大大降低了開關中斷影響:
(2)使用__set_PRIMASK(操作PRIMASK暫存器)做全域性開關中斷的地方,改用__set_BASEPRI(操作BASEPRI暫存器)
Basepri暫存器僅對指定優先順序及其以下優先順序中斷做開關處理,其它高於此優先順序的中斷不受影響。
現在各種RTOS基本都是採用的BASEPRI做開發中斷,這樣可以讓一些需要高實時性的中斷完全不受RTOS核心中斷API影響。
如果我們自己的程式裡面想呼叫BASEPRI暫存器,可以採用如下方法:
巨集定義MAX_PRIORITY為0x10,表示呼叫函式TX_DISABLE關閉中斷的時候,僅關閉搶佔優先順序1到15,搶佔優先順序0未不關閉(NVIC的優先順序分組為4,STM32僅使用高4bit),大家可以根據自己的情況做修改調整。
四、實戰應用場景二:儘量不使用全域性排程鎖,而使用排程閥值
當前ThreadX帶了這麼個功能,大家有需要,可以借鑑下,這種方式的優勢是我們可以僅關閉指定範圍內的任務排程,而不是一刀切關閉所有任務排程。
這種功能在實際專案中還是非常實用的。比如中斷裡面將掛起的高優先順序任務加入到就緒列表了,退出中斷後,由於全域性排程鎖的問題,無法得到執行。
五、實戰應用場景三:RTOS核心原始碼帶的各種開關中斷
現在常用的RTOS核心原始碼裡面都有各種開關中斷,僅僅這一項的存在就很難做的零延遲,所以各家都搞了一些解決方案
(1)uCOS-III早期原始碼搞了箇中斷延遲提交功能:
從uCOS-III V3.07.XX已經將其刪除了,不過大家使用可以借鑑下。
(2)採用BASEPRI暫存器做開關中斷處理
也就是應用場景一里的用法,這種方式也有缺點,不受控的中斷服務程式裡面不能RTOS裡面的API。
像uCOS-II,uCOS-III,FreeRTOS和ThreadX都是採用的方式了。
(3) 零中斷延遲RTOS,典型代表是RTX4和RTX5
能實現主要得益於RTX充分發揮了M核心特性,缺點是RTX主要面向ARM自家核心晶片。
那麼問題來了,為什麼可以做到零中斷延遲?
a、任務級API通過SVC軟中斷呼叫,這樣就無需做開關中斷操作了(這麼做的本意是為了將RTX核心程式碼和使用者應用層程式碼隔離)
b、中斷級API最終實現都會整到ISR FIFO統一處理。
c、還有一個是需要互斥的地方使用CM核心指令 LDREX 和 STREX。
關於RTX,我們可以借鑑的是檔案rtx_core_cm.h裡面提供了一批原子操作API,這樣就不需要開關中斷了, 支援MDK,IAR和GCC:
部分截圖:
六、實戰應用場景四:降低中斷服務程式執行時間
如果中斷服務程式執行時間過長,會影響影響低優先順序中斷的執行,反過來還會影響任務的響應速度。
(1) 使用者自己的程式碼比較好控制,在中斷裡面要執行的程式碼,最好傳送訊息給任務,在任務裡面跑實際功能。像LwIP,RTX的各種中間和ThreadX的各種中介軟體底層驅動基本都是這種玩法。
(2) 一些C庫函式執行時間比較長,中斷裡面慎用,比如sprintf。
(3)中斷裡面最好也不要呼叫uint64_t型別變數,uint64_t除法執行時間賊長,如果帶硬體雙精度浮點,推薦使用硬體雙精度浮點,速度能差10倍出來,這差距太大了。
不過使用時注意雙精度浮點的精度,雙精度浮點不能覆蓋所有64bit整數,精度到15個小數位左右。
MATLAB:
比如整數9223372036854775807用雙精度浮點來表示。
下面是利用H7-TOOL的LUA小程式在H750上的硬體雙精度執行效果:
總結:
程式做的龐大且複雜時,建議中斷越少越好,中斷頻率越低越好,任務之間耦合度越低越好
1、很多地方,其實可以完全用不到中斷,中斷太多會大大增加程式的不可預測性,以及各種中斷優先順序配置造成的奇葩問題
比如QSPI Flash字型檔,相簿儲存載入,如果用QSPI MDMA方式就必須整個中斷(因為要查詢執行是否完畢),此時就可以上記憶體對映方式,簡單方便,一下子省去兩個中斷。
2、另外就是中斷不要搞得太頻繁,造成僅僅進出中斷時間就給系統增加很大的負擔。
3、使用了RTOS的話,任務之間的耦合問題也相當重要,能夠獨立的最好獨立,不要跟太任務有訊息的管理,否則出了問題,後期維護非常辛苦。