一、概述
代码的临界段也称为临界区,指处理时不可分割的代码。一旦这部分代码开始执行,则不允许执行被打断。
大多数系统为确保临界段代码的执行不被中断,在进入临界段之前必须关中断,而临界段代码执行完后,要立即开中断。常见案例为喂狗、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;等价上面语句