文章目录
中断
中断是指任务在运行过程中应外部或者内部异步事件的请求中止当前任务转而执行异步事件的过程。应中断请求而运行的程序叫做中断服务子程序(ISR)。
中断的基本流程
在uCOS-II中,如果任务A执行的时候来一个中断,如果这时候cpu的中断允许位是打开的,那么cpu执行完当前正在执行的指令之后就会跳到中断向量表里里面找到对应中断服务程序的入口地址(即中断向量),找到中断向量之后,在执行ISR之前会先保留现场,也就是将目前cpu各个寄存器的值保存在任务A的私有堆栈中,并且将下一条指令的地址也保存在任务A的私有堆栈中,以便于下次执行。执行完ISR之后会返回任务A继续执行,同时将堆栈里面的内容出栈。
可剥夺型内核
对于uCOS-II这种可剥夺型内核来说,ISR执行完之后并不一定会跳到原来的任务,而是会进行一次任务调度,执行当前优先级最高的任务。
中断嵌套
在uCOS-II中允许高优先级的中断源中断请求可以打断低优先级中断服务程序的执行。OSIntNesting就是记录中断嵌套层次的变量。
中断常用函数
OSIntEnter()进入中断服务函数
OSIntEnter()的作用就是将OSIntNesting加一。
源码如下:
void OSIntEnter (void)
{
if (OSRunning == OS_TRUE) {
if (OSIntNesting < 255u) {
OSIntNesting++; /* Increment ISR nesting level */
}
}
}
这个函数发生在中断服务程序保护了被中断任务的断点数据之后,运行用户代码之前。
OSIntExit()退出中断服务函数
这个函数执行之后有两种可能:
- 返回被中断的函数
- 执行OSSched(),选择优先级最高的任务调度
执行调度只有当:OSIntNesting为0、调度器未被锁定且任务就绪表中查到的最高优先级任务又不是被中断任务 时才进行任务切换。
基本流程图如下:
OSIntExit()源码如下:
void OSIntExit (void)
{
#if OS_CRITICAL_METHOD == 3u /* Allocate storage for CPU status register */
OS_CPU_SR cpu_sr = 0u;
#endif
if (OSRunning == OS_TRUE) {
OS_ENTER_CRITICAL();
if (OSIntNesting > 0u) { /* Prevent OSIntNesting from wrapping */
OSIntNesting--;
}
if (OSIntNesting == 0u) { /* Reschedule only if all ISRs complete ... */
if (OSLockNesting == 0u) { /* ... and not locked. */
OS_SchedNew();
OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy];
if (OSPrioHighRdy != OSPrioCur) { /* No Ctx Sw if current task is highest rdy */
#if OS_TASK_PROFILE_EN > 0u
OSTCBHighRdy->OSTCBCtxSwCtr++; /* Inc. # of context switches to this task */
#endif
OSCtxSwCtr++; /* Keep track of the number of ctx switches */
OSIntCtxSw(); /* Perform interrupt level ctx switch */
}
}
}
OS_EXIT_CRITICAL();
}
}
OSIntCtwSw()中断切换函数
这个函数功能就是OSIntExit()函数运行完之后,如果 "OSIntNesting为0、调度器未被锁定且任务就绪表中查到的最高优先级任务又不是被中断任务 "那么就调用OSIntCtwSw()完成切换工作
中断服务子程序的流程图
临界区
在操作系统那个专栏里面也提到过 临界区 的概念,在uCOS-II中都是一样的,临界区指的是:不能被中断的部分。
uCOS-II中实现临界区的三种方式
在uCOS-II中,有三种可选的临界区实现方式
直接调用CPU的开关中断指令
选择这种方式需要将:OS_CRITICAL_METHOD = 1
将CPU中断允许标志位入栈,再调用CPU的开关中断指令
这种方式可以改变CPU中断允许标志位的值,只是CPU不响应而已。临界区结束之后再将内容出栈就行了。
OS_CRITICAL_METHOD = 2
将CPU中断允许标志位的值存在一个变量里面
其实和上面那种方式的思想是一样的,但是这种方式必须要求用户使用的c编译器具有扩展功能。用户可获得程序状态字的值。
OS_CRTITCAL_METHOD = 3
进入以及退出临界区的方式
在临界区代码前后调用两个宏定义:
进入:OS_ENTER_CRITICAL()
退出:OS_EXIT_CRITICAL()
参考资料
嵌入式实时操作系统uC/OS-II原理及应用 – 任哲