一、概述
代碼的臨界段也稱為臨界區,指處理時不可分割的代碼。一旦這部分代碼開始執行,則不允許執行被打斷。
大多數系統為確保臨界段代碼的執行不被中斷,在進入臨界段之前必須關中斷,而臨界段代碼執行完后,要立即開中斷。常見案例為喂狗、FLASH的寫入、獲取當前時鍾節拍計數器等保護操作。
在UCOSIII中存在大量的臨界區代碼,分以下情況:
- 情況1:中斷處理程序和任務都會訪問的臨界段代碼,需要用關中斷的方法加以保護。一旦使用關中斷的方法,臨界區代碼必須快速完成,否則會導致中斷延遲影響嚴重,例如串口數據丟包現象。
- 情況2:僅由任務訪問的臨界區代碼,可以通過給調度器上鎖的方法來保護。一旦使用調度器上鎖,內核會禁止用戶進行阻塞型調用。如果某些應用能夠進行阻塞調用,則該應用很可能已經崩潰了。
在os.h頭文件有以下函數:
#if OS_CFG_ISR_POST_DEFERRED_EN > 0u /* Deferred ISR Posts ------------------------------ */
/* Lock the scheduler */
#define OS_CRITICAL_ENTER() \
do { \
CPU_CRITICAL_ENTER(); \
OSSchedLockNestingCtr++; \
if (OSSchedLockNestingCtr == 1u) { \
OS_SCHED_LOCK_TIME_MEAS_START(); \
} \
CPU_CRITICAL_EXIT(); \
} while (0)
#define OS_CRITICAL_EXIT() \
do { \
CPU_CRITICAL_ENTER(); \
OSSchedLockNestingCtr--; \
if (OSSchedLockNestingCtr == (OS_NESTING_CTR)0) { \
OS_SCHED_LOCK_TIME_MEAS_STOP(); \
if (OSIntQNbrEntries > (OS_OBJ_QTY)0) { \
CPU_CRITICAL_EXIT(); \
OS_Sched0(); \
} else { \
CPU_CRITICAL_EXIT(); \
} \
} else { \
CPU_CRITICAL_EXIT(); \
} \
} while (0)
#else
#define OS_CRITICAL_ENTER() CPU_CRITICAL_ENTER()
#define OS_CRITICAL_EXIT() CPU_CRITICAL_EXIT()
#endif
#define CPU_CRITICAL_ENTER() do { CPU_INT_DIS(); } while (0) /* Disable interrupts. */
#define CPU_CRITICAL_EXIT() do { CPU_INT_EN(); } while (0) /* Re-enable interrupts. */
#define CPU_INT_DIS() do { cpu_sr = CPU_SR_Save(); } while (0) /* Save CPU status word & disable interrupts.*/
#define CPU_INT_EN() do { CPU_SR_Restore(cpu_sr); } while (0) /* Restore CPU status word. */
上述代碼:OS_CFG_ISR_POST_DEFERRED_EN該宏定義決定進入臨界區時的操作
- OS_CFG_ISR_POST_DEFERRED_EN的值為0:要屏蔽除NMI和fault以外的所有異常和中斷
- OS_CFG_ISR_POST_DEFERRED_EN的值為1:是否要對調度器上鎖,默認值為1。這種在情況2特別合適,特別注意的是,若調度器上鎖,禁止在臨界區調用各種引起任務調度的函數,如睡眠延時、阻塞等待信號量/互斥型信號量/事件標志組/消息隊列/多個內核對象等,否則引起內核運行異常。
注:
OS_CFG_ISR_POST_DEFERRED_EN使能的延遲中斷提交已經不被推薦使用了。在uC/OS-III v3.06.00發布的更新記錄中,已經將延遲中斷提交列為了過時的方法並且去除了,推測可能是出於實時性和穩定性的考慮。
Deferred interrupt processing is now obsolete and has been removed。
二、如何使用
情況1:若該臨界區不希望受到異常與中斷的影響,直接使用CPU_CRITICAL_ENTER與CPU_CRITICAL_EXIT函數,步驟如下:
CPU_SR_ALLOC();
CPU_CRITICAL_ENTER(); //屏蔽除NMI和fault以外的所有異常和中斷,即將進入臨界區
臨界區代碼,該代碼涉及中斷訪問
CPU_CRITICAL_EXIT() ; //開啟除NMI和fault以外的所有異常和中斷,退出臨界區
情況2:OS_CFG_ISR_POST_DEFERRED_EN為1,若臨界區即使被中斷打斷,也不會影響結果,只是用於任務之間的互斥訪問,步驟如下:
CPU_SR_ALLOC();
OS_CRITICAL_ENTER(); //鎖定調度器,即將進入臨界區
臨界區代碼,該代碼不涉及中斷訪問
OS_CRITICAL_EXIT() ; //解鎖調度器,退出臨界區
使用示例:獲取當前時鍾節拍計數器
OS_TICK OSTimeGet (OS_ERR *p_err)
{
OS_TICK ticks;
CPU_SR_ALLOC();
:
:
CPU_CRITICAL_ENTER();
ticks = OSTickCtr;
CPU_CRITICAL_EXIT();
*p_err = OS_ERR_NONE;
return (ticks);
}
三、關所有異常和中斷
1.屏蔽除NMI和fault以外的所有異常和中斷,最后會調用以下代碼:
#define CPU_INT_DIS() do { cpu_sr = CPU_SR_Save(); } while (0) /* Save CPU status word & disable interrupts.*/
#define CPU_INT_EN() do { CPU_SR_Restore(cpu_sr); } while (0) /* Restore CPU status word. */
CPU_SR_Save
MRS R0, PRIMASK ; Set prio int mask to mask all (except hard faults)
CPSID I
BX LR
CPU_SR_Restore ; See Note #2.
MSR PRIMASK, R0
BX LR
2.Cortex-M 處理器的異常中,編號 1~15 的為系統異常,16及以上的則為中斷異常。

3.PRIMASK用於屏蔽除NMI和fault以外的所有異常和中斷。該寄存器可以通過 MRS和MSR以下例方式訪問:
《Cortex M3與M4權威指南》章節7.10.1 P265
In many applications you might need to temporarily disable all interrupts to carry out some timing critical tasks. You can use the PRIMASK register for this purpose. The PRIMASK register can only be accessed in privileged state.
The PRIMASK register is used to disable all exceptions except NMI and Hard Fault. It effectively changes the current priority level to 0 (highest programmable level). In C programming, you can use the functions provided in CMSIS-Core to set and clear PRIMASK:
1)關中斷
MOV R0,#1
MSR PRIMASK,R0
或者:
CPSID I;等價上面語句
2)開中斷
MOV R0,#0 MSR PRIMASK,R0
或者:
CPSIE I;等價上面語句