3.4 開中斷
3.5 進入臨界段
3.6 退出臨界段
3.7 案例
[野火®]《freertos 核心實現與應用開發實戰—基於stm32》臨界段用一句話概括就是一段在執行的時候不能被中斷的**段。
在 freertos 裡面,這個臨界段最常出現的就是對全域性變數的操作,全域性變數就好像是乙個槍把子,誰都可以對他開槍,但是我開槍的時候,你就不能開槍,否則就不知道是誰命中了靶子。
在進行系統排程和處理外部中斷時,臨界段會被打斷。系統排程,最終也是產生 pendsv 中斷,在 pendsv handler 裡面實現任務的切換,所以還是可以歸結為中斷。所以,freertos 對臨界段的保護可以理解為對中斷的開和關的控制。
為了快速地開關中斷, cortex-m 核心專門設定了一條 cps 指令,有 4 種用法。
cpsid i ;primask=
1;關中斷
cpsie i ;primask=
0;開中斷
cpsid f ;faultmask=
1;關異常
cpsie f ;faultmask=
0;開異常
cortex-m 核心 裡面三個中斷遮蔽暫存器描述如下。
暫存器名稱
描述primask
這是個只有單一位元的暫存器。 在它被置 1 後,就關掉所有可遮蔽的異常,只剩下 nmi 和硬 fault 可以響應。它的預設值是 0,表示沒有關中斷。
faultmask
這是個只有 1 個位的暫存器。當它置 1 時,只有 nmi 才能響應,所有其它的異常,甚至是硬 fault 也被關閉。它的預設值也是 0,表示沒有關異常。
basepri
這個暫存器最多有 9 位(由表達優先順序的位數決定)。它定義了被遮蔽優先順序的閾值。當它被設成某個值後,所有優先順序號大於等於此值的中斷都被關(優先順序號越大,優先順序越低)。但若被設成 0,則不關閉任何中斷, 0 也是預設值。
在 freertos 中,對中斷的開和關是通過操作 basepri 暫存器來實現的,即大於等於 basepri 的值的中斷會被遮蔽,小於 basepri 的值的中斷則不會被遮蔽,不受freertos 管理。
freertos 關中斷的函式在 portmacro.h 中定義,分無返回值和有返回值兩種。
在往 basepri 寫入新的值的時候,不用先將 basepri 的值儲存起來,即不用管當前的中斷狀態是怎麼樣的,既然不用管當前的中斷狀態,也就意味著這樣的函式不能在中斷裡面呼叫。
/* 不帶返回值的關中斷函式,不能巢狀,不能在中斷裡面使用;*/
#define portdisable_interrupts() vportraisebasepri()
static portforce_inline void
vportraisebasepri
(void
)}
在往 basepri 寫入新的值的時候,先將 basepri 的值儲存起來,在更新完 basepri 的值的時候,將之前儲存好的 basepri 的值返回,返回的值作為形參傳入開中斷函式。
/* 帶返回值的關中斷函式,可以巢狀,可以在中斷裡面使用;*/
#define portset_interrupt_mask_from_isr() ulportraisebasepri()
static portforce_inline uint32_t ulportraisebasepri
(void
)return ulreturn;
/* 返回原來 basepri 的值。*/
}
freertos 開中斷的函式在 portmacro.h 中定義。
開中斷函式,具體是將傳進來的形參更新到 basepri 暫存器。
根據傳進來形參的不同,分為中斷保護版本與非中斷保護版本。
直接將 basepri 的值設定為 0,與 portdisable_interrupts() 成對使用。
/* 不帶中斷保護的開中斷函式 */
#define portenable_interrupts() vportsetbasepri( 0 )
將上一次關中斷時儲存的 basepri 的值作為形參,與 portset_interrupt_mask_from_isr() 成對使用。
/* 帶中斷保護的開中斷函式 */
#define portclear_interrupt_mask_from_isr(x) vportsetbasepri(x)
static portforce_inline void
vportsetbasepri
( uint32_t ulbasepri )
}
#define taskenter_critical() portenter_critical()
#define taskenter_critical_from_isr() portset_interrupt_mask_from_isr()
進入臨界段,無中斷保護版本,不能巢狀。
/* 在 task.h 中定義 */
#define taskenter_critical() portenter_critical()
/* 在 portmacro.h 中定義 */
#define portenter_critical() vportentercritical()
/* 在 port.c 中定義 */
void
vportentercritical
(void)}
/* 在 portmacro.h 中定義 */
#define portdisable_interrupts() vportraisebasepri()
/* 在 portmacro.h 中定義 */
static portforce_inline void
vportraisebasepri
(void
)}
進入臨界段,有中斷保護版本,可以巢狀。
/* 在 task.h 中定義 */
#define taskenter_critical_from_isr() portset_interrupt_mask_from_isr()
/* 在 portmacro.h 中定義 */
#define portset_interrupt_mask_from_isr() ulportraisebasepri()
/* 在 portmacro.h 中定義 */
static portforce_inline uint32_t ulportraisebasepri
(void
)return ulreturn;
}
#define taskexit_critical() portexit_critical()
#define taskexit_critical_from_isr( x ) portclear_interrupt_mask_from_isr( x )
退出臨界段,無中斷保護版本,不能巢狀。
/* 在 task.h 中定義 */
#define taskexit_critical() portexit_critical()
/* 在 portmacro.h 中定義 */
#define portexit_critical() vportexitcritical()
/* 在 port.c 中定義 */
void
vportexitcritical
(void)}
/* 在 portmacro.h 中定義 */
#define portenable_interrupts() vportsetbasepri( 0 )
static portforce_inline void
vportsetbasepri
( uint32_t ulbasepri )
}
退出臨界段,有中斷保護版本,可以巢狀。
/* 在 task.h 中定義 */
#define taskexit_critical_from_isr( x ) portclear_interrupt_mask_from_isr( x )
/* 在 portmacro.h 中定義 */
#define portclear_interrupt_mask_from_isr(x) vportsetbasepri(x)
static portforce_inline void
vportsetbasepri
( uint32_t ulbasepri )
}
在 freertos 中,對臨界段的保護出現在兩種場合,一種是在中斷場合一種是在非中斷場合。
/* 在中斷場,臨界段可以巢狀 */
/* 在非中斷場合,臨界段不能巢狀 */
FreeRTOS臨界資源保護(臨界區保護)
臨界區未保護出現的異常 今天我們說說其中之一的原因 臨界資源未保護。我們先看個例子,假如有乙個5個節點單向鍊錶,如下結構 head 1 2 3 4 5 null 有一任務a在乙個單向鍊錶的2 3節點之間插入乙個新的2a節點,已經將2 2a,還未將2a 3,此時,鍊錶變為兩個未完整的部分,如下結構 h...
FreeRTOS 中斷配置和臨界段
中斷遮蔽暫存器 primask faultmask和basepri 1.primask 這是個只有1個位的暫存器。當它置1時,就關掉所有可遮蔽的異常,只剩下 nmi和硬fault可以響應。它的預設值是0,表示沒有關中斷 2.faultmask 這是個只有1個位的暫存器。當它置1時,只有nmi才能響應...
四 FreeRTOS 中斷配置和臨界段
freertos 的中斷配置是乙個很重要的內容,我們需要根據所使用的 mcu 來具體配置。因此要先了解 mcu 架構中有關中斷的知識。中斷由硬體產生,當中斷產生以後 cpu 就會中斷當前的流程轉而去處理中斷服務,待中斷服務函式執行完後再回來執行之前被中斷的任務。cortex m 核心的 mcu 提供...