@目錄0、思考與回答0.1、思考一0.2、思考二0.3、思考三1、關中斷1.1、帶返回值1.2、不帶返回值2、開中斷3、臨界段4、應用 0、思考與回答 0.1、思考一 為什麼需要臨界段? 有時候我們需要部分代碼一旦這開始執行,則不允許任何中斷打斷,這段代碼稱為臨界段 0.2、思考二 如何實現臨界段? ...
@
目錄0、思考與回答
0.1、思考一
為什麼需要臨界段?
有時候我們需要部分代碼一旦這開始執行,則不允許任何中斷打斷,這段代碼稱為臨界段
0.2、思考二
如何實現臨界段?
- 關中斷
- 執行臨界區代碼
- 開中斷
0.3、思考三
對於 Cotex-M4 內核的處理器如何方便的控制其中斷開關?
使用 BASEPRI 寄存器,當該寄存器中的值不為 0 時,處理器將不會處理優先順序值大於或等於 BASEPRI 的任何異常,該寄存器相關信息可以在 Cortex-M4 Devices Generic User Guide 手冊中找到,具體如下圖所示
值得註意的是 STM32 的 BASEPRI 寄存器做了一些修改,只使用了其高 4 位,低 4 位的數據沒有使用,所以對於 STM32 在使用 BASEPRI 寄存器對中斷進行屏蔽時,需要考慮到寫入的高 4 位數據才是正確的數據,感興趣的可以閱讀 為何修改BASEPRI寄存器無效? 這篇文章
1、關中斷
1.1、帶返回值
Keil 版本
/* portMacro.h */
#define portSET_INTERRUPT_MASK_FROM_ISR() ulPortRaiseBASEPRI()
// 帶返回值關中斷,將當前中斷狀態作為返回值返回
static __inline uint32_t ulPortRaiseBASEPRI(void)
{
uint32_t ulReturn,ulNewBASEPRI = configMAX_SYSCALL_INTERRUPT_PRIORITY;
__asm
{
mrs ulReturn,basepri // 保存中斷時 BASEPRI 寄存器的值
msr basepri,ulNewBASEPRI // 屏蔽 優先順序值 大於等於 11 的中斷
dsb
isb
}
return ulReturn;
}
CLion 版本
static __inline uint32_t ulPortRaiseBASEPRI(void)
{
uint32_t ulReturn,ulNewBASEPRI = configMAX_SYSCALL_INTERRUPT_PRIORITY;
__asm volatile
(
"mrs ulReturn,basepri \n"
"msr basepri,ulNewBASEPRI \n"
"dsb \n"
"isb \n"
);
return ulReturn;
}
1.2、不帶返回值
Keil 版本
/* portMacro.h */
#define portDISABLE_INTERRUPTS() vPortRaiseBASEPRI()
// 不帶返回值關中斷
static __inline void vPortRaiseBASEPRI(void)
{
uint32_t ulNewBASEPRI = configMAX_SYSCALL_INTERRUPT_PRIORITY;
__asm
{
msr basepri,ulNewBASEPRI
dsb
isb
}
}
CLion 版本
static __inline void vPortRaiseBASEPRI(void)
{
uint32_t ulNewBASEPRI = configMAX_SYSCALL_INTERRUPT_PRIORITY;
__asm volatile
(
"msr basepri, %0 \n"
"isb \n"
:
: "r" (ulNewBASEPRI)
: "memory"
);
}
2、開中斷
Keil 版本
/* portMacro.h */
// 設置 BASEPRI 為 0 開所有中斷
#define portENABLE_INTERRUPTS() vPortSetBASEPRI(0)
// 設置 BASEPRI 為進入中斷時的值則恢複原來的中斷狀態
#define portCLEAR_INTERRUPT_MASK_FROM_ISR(x) vPortSetBASEPRI(x)
// 開中斷
static __inline void vPortSetBASEPRI(uint32_t ulBASEPRI)
{
__asm volatile
{
msr basepri,ulBASEPRI
}
}
CLion 版本
static __inline void vPortSetBASEPRI(uint32_t ulBASEPRI)
{
__asm volatile
(
"msr basepri, %0 \n"
: "r" (ulBASEPRI)
: "memory"
);
}
3、臨界段
/* task.h */
#define taskENTER_CRITICAL() portENTER_CRITICAL()
#define taskEXIT_CRITICAL() portEXIT_CRITICAL()
#define taskENTER_CRITICAL_FROM_ISR() portSET_INTERRUPT_MASK_FROM_ISR()
#define taskEXIT_CRITICAL_FROM_ISR(x) portCLEAR_INTERRUPT_MASK_FROM_ISR(x)
/* portMacro.h */
extern void vPortEnterCritical(void);
extern void vPortExitCritical(void);
#define portENTER_CRITICAL() vPortEnterCritical()
#define portEXIT_CRITICAL() vPortExitCritical()
/* port.c */
// 中斷嵌套計數器
static UBaseType_t uxCriticalNesting = 0xAAAAAAAA;
// 進入臨界區
void vPortEnterCritical(void)
{
portDISABLE_INTERRUPTS();
uxCriticalNesting++;
if(uxCriticalNesting==1)
{
// configASSERT((portNVIC_INT_CTRL_REG & portVECTACTIVE_MASK) == 0);
}
}
// 退出臨界區
void vPortExitCritical(void)
{
// configASSERT(uxCriticalNesting);
uxCriticalNesting--;
if(uxCriticalNesting == 0)
{
portENABLE_INTERRUPTS();
}
}
4、應用
普通場合
// 進入臨界區直接屏蔽優先順序號大於 11 的中斷
taskENTER_CRITICAL();
// 退出時直接設置 BASEPRI 寄存器的值為 0
taskEXIT_CRITICAL();
中斷場合
uint32_t ulReturn;
// 進入臨界區前保存 BASEPRI 寄存器的值
ulReturn = taskENTER_CRITICAL_FROM_ISR();
// 退出臨界區時恢復 BASEPRI 寄存器的值
taskEXIT_CRITICAL_FROM_ISR(ulReturn);