临界段代码,也称作临界域,是一段不可分割的代码。
我们都知道在进入中断函数或者异常的时候代码会被打断而进入中断服务函数。而有些代码是不允许被打断的,比如:一些外围器件的的初始化(LCD)、任务函数的创建等等。所以在 uCOS 中包含了很多临界段代 码来保护临界段。
那么临界段函数是怎么操作的?简单啊——关中断关异常。
临界端代码实现的汇编函数:
CPU_SR_Save
;通过 MRS 指令将特殊寄存器 PRIMASK 寄存器的值读取到通用寄存器 R0。
MRS R0, PRIMASK ; Set prio int mask to mask all (except faults)
CPSID I ;关闭中断
BX LR
CPU_SR_Restore ; See Note #2.
;通过 MSR 指令将通用寄存器 R0 的值写入到特殊寄存器 PRIMASK。
MSR PRIMASK, R0
BX LR
- CPU_SR_Save是关闭中断函数
- CPU_SR_Restore是恢复中断函数(准确来说是恢复PRIMASK 寄存器状态)
这里使用了CPSID I来关闭中断,但是没有用到CPSIDE I来开启中断。
那么中断是如何开启的呢?
答案就在以下这两个语句中,uCOS是通过恢复关中断前PRIMASK的状态来恢复中断的 :
MRS R0, PRIMASK ;通过 MRS 指令将特殊寄存器 PRIMASK 寄存器的值读取到通用寄存器 R0。
MSR PRIMASK, R0;通过 MSR 指令将通用寄存器 R0 的值写入到特殊寄存器 PRIMASK。
在uCOS里面,临界段代码的开关中断就是操控PRIMASK 寄存器来实现的。
一开始在关中断前用R0保存了PRIMASK 的值(MRS R0, PRIMASK),然后用CPSID I语句关掉中断;
在开启中断时(恢复状态),用语句(MSR PRIMASK, R0)把关中断以前PRIMASK 的值恢复到PRIMASK 中。
如果之前PRIMASK的值为1,那么恢复它的值也为1,也就是没有中断还是关着的没有打开,那么可以判断之前肯定发生了临界段代码的嵌套;如果之前PRIMASK为0,那么恢复后也为0,也就是说中断被开启了。
其实这样做是为了处理临界段发生嵌套时发生的情况(如果用CPSIE I来恢复中断就太粗暴了,如果之前已经发生了一个临界段而且还没有处理完,用CPSIE I开了中断,那之前使用了临界段的代码肯定要出错了)。
C语言中函数:
#define CPU_SR_ALLOC() CPU_SR cpu_sr = (CPU_SR)0 //宏函数,相当于定义了一个32位变量cpu_sr ,并赋值为0
/*实现中断打开与关闭的4个函数,其实它们都是调用了上面所说的汇编函数*/
#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. */
#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. */
————————————题外—————————————
开始时,我对C语言那几个函数不是很了解,总是看不懂;其实是由于我对#define宏定义的使用不熟悉,而且uCOS里面的宏定义实在有点多,刚开始看得有点晕。
其实这里是用到了宏函数!我用C语言就做了一个测试:
#include<stdio.h>
typedef unsigned int CPU_INT32U;
typedef CPU_INT32U CPU_SR;
/* 宏函数CPU_SR_ALLOC()的作用相当于定义一个32位变量cpu_sr并赋初值为0*/
#define CPU_SR_ALLOC() CPU_INT32U cpu_sr = (CPU_INT32U)0
/*宏函数修改cpu_sr的值为11*/
#define CPU_INT_DIS() do { cpu_sr = 11; } while (0)
void main ()
{
CPU_SR_ALLOC() ; //相当于定义了一个变量cpu_sr
printf("cpu_sr=%d\n",cpu_sr);
CPU_INT_DIS(); //修改变量cpu_sr=11
printf("cpu_sr=%d",cpu_sr);
getch(); //窗口停留函数,调试时使用
}
其实宏定义很简单,直接把你宏定义的内容直接放进代码代替那个宏里面看就可以了。
代替后的结果是:
#include<stdio.h>
void main ()
{
unsigned int cpu_sr=0;
printf("cpu_sr=%d\n",cpu_sr);
cpu_sr=11; //修改变量cpu_sr=11
printf("cpu_sr=%d",cpu_sr);
getch(); //窗口停留函数,调试时使用
}