中断管理
先看一下最常用的临界段进入的函数:进入临界段 OS_CRITICAL_ENTER() 退出临界段OS_CRITICAL_EXIT()他们两个的宏是这样的.在使能中断延迟提交时:#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) /* Lock the scheduler but re-enable interrupts */#define OS_CRITICAL_ENTER_CPU_CRITICAL_EXIT() \ do { \ OSSchedLockNestingCtr++; \ \ if (OSSchedLockNestingCtr == 1u) { \ OS_SCHED_LOCK_TIME_MEAS_START(); \ } \ CPU_CRITICAL_EXIT(); \ } while (0) /* Scheduling occurs only if an interrupt occurs */#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)#define OS_CRITICAL_EXIT_NO_SCHED() \ do { \ CPU_CRITICAL_ENTER(); \ OSSchedLockNestingCtr--; \ if (OSSchedLockNestingCtr == (OS_NESTING_CTR)0) { \ OS_SCHED_LOCK_TIME_MEAS_STOP(); \ } \ CPU_CRITICAL_EXIT(); \ } while (0)#else /* Direct ISR Posts -------------------------------- */在没有使能中断延迟提交时:#define OS_CRITICAL_ENTER() CPU_CRITICAL_ENTER()#define OS_CRITICAL_ENTER_CPU_CRITICAL_EXIT()#define OS_CRITICAL_EXIT() CPU_CRITICAL_EXIT()#define OS_CRITICAL_EXIT_NO_SCHED() CPU_CRITICAL_EXIT()#endif
先看一下在没有使能中断延迟提交时的临界段进入的函数:进入临界段 CPU_CRITICAL_ENTER() 退出临界段CPU_CRITICAL_EXIT()
他们两个的宏是这样的#define CPU_CRITICAL_ENTER() do { CPU_INT_DIS(); } while (0) #define CPU_CRITICAL_EXIT() do { CPU_INT_EN(); } while (0)
另一层宏是
#define CPU_INT_DIS() do { cpu_sr = CPU_SR_Save(); } while (0) #define CPU_INT_EN() do { CPU_SR_Restore(cpu_sr); } while (0)
接着另一层宏是
CPU_SR_SaveMRS R0, PRIMASK ; Set prio int mask to mask all (except faults)CPSID IBX LR
这里关于C语言调用汇编,规定32位的返回值存放到R0,64位的R0存放第32为,R1存放高32位。所以上边的汇编代码第二行实际就是将PRIMASK保存到R0最后返回给变量cpu_sr ,第三行是一个CM3关全局中断的命令,在LR保存了调用CPU_SR_Save()之前的下一条指令PC指针,BX就返回了。
CPU_SR_Restore ; MSR PRIMASK, R0BX LR
这里注意调用这个函数时传入了cpu_sr,根据C调用汇编的规定这个变量被存放到R0里,所以第二句就是将cpu_sr 恢复到PRIMASK,由CM3权威指南可以知道,这是一个中断管理的寄存器,写1屏蔽所有中断,只剩下faul和NMI,写0则开启中断;c语言调用汇编时规定,当传递参数少于4个时,从右往左依次存放到R0~R3中,当传入参数多于4个时,多出来的参数会先入栈;
注意在调用进入临界段时常常有这个函数CPU_SR_ALLOC()它的宏是
#define CPU_SR_ALLOC() CPU_SR cpu_sr = (CPU_SR)0
即定义的一个CPU_SR的变量初始化为0;
对于延迟提交使能下的进入临界段代码,就是加了一个调度器嵌套层数OSSchedLockNestingCtr++的操作,它代表了调度器被锁住的嵌套层数,OS_CRITICAL_ENTER直接锁住调度器,OS_CRITICAL_ENTER_CPU_CRITICAL_EXIT()这个宏调用之前一般先关闭中断,接着宏锁住调度器然后锁住调度器,再恢锁调度器前的中断状态(这里不是开中断),在没有使能中断延迟时,会增加中断关闭的时间。解开调度嵌套也有两种
- OS_CRITICAL_EXIT()
- 解开调度时进行任务调度(延迟);
- OS_CRITICAL_EXIT_NO_SCHED()
- 解开调度时不进行任务调度;
这两个函数后应该都根据选项选择是否调用OSSchedule()进行任务调度。且进入调度嵌套和退出嵌套要成对使用负责会导致调度器被一直锁住。锁住调度器是用户无法进行任务切换,也无法将当前任务挂起。
中断在系统中相当于优先级最高的任务,因此对于中断的处理是十分紧急的从CM3的内核中断响应机制就能知道cpu对于中断的相应是追求极致的快速,所以UCOS对于这里也有对应的措施,系统级的一些处理时间比较长的任务不允许在中断中调用,对于一些中断中的提交函数会放到中断延迟提交里完成,把中断级的任务放到任务级从而减少关中断时间,减少对中断的实时性的影响。
中断延迟提交:
当用户在把宏OS_CFG_ISR_POST_DEFERRED使能,使用延迟提交在系统初始化时会自动初始化延迟提交队列然后创建一个延迟提交任务。
OSInit()函数内的延迟提交任务初始化
#if OS_CFG_ISR_POST_DEFERRED_EN > 0u OS_IntQTaskInit(p_err); /* Initialize the Interrupt Queue Handler Task */ if (*p_err != OS_ERR_NONE) { return; }#endif
中断延迟提交的过程就是把要提交的内容先保存起来,然后把系统初始化时创建一个优先级 为0(最高)的延迟提交任务就绪,在中断函数执行完成后就会执行延迟的提交。对于OS_IntQTaskInit(p_err)主要作用就是初始化延迟提交队列,并创建任务。在之前的任务管理章节我们知道一个任务被创建就会加如就绪列表等待调用时就会执行.
void OS_IntQTaskInit (OS_ERR *p_err){ OS_INT_Q *p_int_q; OS_INT_Q *p_int_q_next; OS_OBJ_QTY i;#ifdef OS_SAFETY_CRITICAL if (p_err == (OS_ERR *)0) { OS_SAFETY_CRITICAL_EXCEPTION(); return; }#endif //延迟提交溢出计数清零 OSIntQOvfCtr = (OS_QTY)0u; /* Clear the ISR queue overflow counter */ //延迟提交信息队列的内存不能是空 if (OSCfg_IntQBasePtr == (OS_INT_Q *)0) { *p_err = OS_ERR_INT_Q; return; } //延迟信息队列的长最小是1 if (OSCfg_IntQSize < (OS_OBJ_QTY)2u) { *p_err = OS_ERR_INT_Q_SIZE; return; } //延迟提交任务每次运行的最长时间 OSIntQTaskTimeMax = (CPU_TS)0; p_int_q = OSCfg_IntQBasePtr; /* Initialize the circular ISR queue */ p_int_q_next = p_int_q; p_int_q_next++; //将信息对垒串成单项链表 for (i = 0u; i < OSCfg_IntQSize; i++) { p_int_q->Type = OS_OBJ_TYPE_NONE; p_int_q->ObjPtr = (void *)0; p_int_q->MsgPtr = (void *)0; p_int_q->MsgSize = (OS_MSG_SIZE)0u; p_int_q->Flags = (OS_FLAGS )0u; p_int_q->Opt = (OS_OPT )0u; p_int_q->NextPtr = p_int_q_next; p_int_q++; p_int_q_next++; } //单项链表“圈”操作方便延时提交队列内存的使用 p_int_q--; p_int_q_next = OSCfg_IntQBasePtr; p_int_q->NextPtr = p_int_q_next; OSIntQInPtr = p_int_q_next; OSIntQOutPtr = p_int_q_next; //延迟提交队列中要进行提交的内核对象个数 OSIntQNbrEntries = (OS_OBJ_QTY)0u; //延迟提交队列中要进行提交的内核对象的最大个数 OSIntQNbrEntriesMax = (OS_OBJ_QTY)0u; /* -------------- CREATE THE ISR QUEUE TASK ------------- */ if (OSCfg_IntQTaskStkBasePtr == (CPU_STK *)0) { *p_err = OS_ERR_INT_Q_STK_INVALID; return; } if (OSCfg_IntQTaskStkSize < OSCfg_StkSizeMin) { *p_err = OS_ERR_INT_Q_STK_SIZE_INVALID; return; } //创建延迟提交任务 OSTaskCreate((OS_TCB *)&OSIntQTaskTCB, (CPU_CHAR *)((void *)"uC/OS-III ISR Queue Task"), (OS_TASK_PTR )OS_IntQTask, (void *)0, (OS_PRIO )0u, /* This task is ALWAYS at priority '0' (i.e. highest) */ (CPU_STK *)OSCfg_IntQTaskStkBasePtr, (CPU_STK_SIZE)OSCfg_IntQTaskStkLimit, (CPU_STK_SIZE)OSCfg_IntQTaskStkSize, (OS_MSG_QTY )0u, (OS_TICK )0u, (void *)0, (OS_OPT )(OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR), (OS_ERR *)p_err);}#endif
函数内创建的延迟提交任务
void OS_IntQTask (void *p_arg){ CPU_BOOLEAN done; CPU_TS ts_start; CPU_TS ts_end; CPU_SR_ALLOC(); p_arg = p_arg; /* Not using 'p_arg', prevent compiler warning */ while (DEF_ON) { done = DEF_FALSE; while (done == DEF_FALSE) { CPU_CRITICAL_ENTER(); //还有内核对象等待提交? //否 if (OSIntQNbrEntries == (OS_OBJ_QTY)0u) { //任务删除 OSRdyList[0].NbrEntries = (OS_OBJ_QTY)0u; /* Remove from ready list */ OSRdyList[0].HeadPtr = (OS_TCB *)0; OSRdyList[0].TailPtr = (OS_TCB *)0; OS_PrioRemove(0u); /* Remove from the priority table */ CPU_CRITICAL_EXIT(); OSSched(); done = DEF_TRUE; /* No more entries in the queue, we are done */ } //是 else { CPU_CRITICAL_EXIT(); ts_start = OS_TS_GET(); //提交信息 OS_IntQRePost(); ts_end = OS_TS_GET() - ts_start; /* Measure execution time of tick task */ if (OSIntQTaskTimeMax < ts_end) { OSIntQTaskTimeMax = ts_end; } CPU_CRITICAL_ENTER(); //提交信息队列出口指向更新 OSIntQOutPtr = OSIntQOutPtr->NextPtr; /* Point to next item in the ISR queue */ OSIntQNbrEntries--; CPU_CRITICAL_EXIT(); } } }}
函数内先检查提交信息队列是否为空了也就是提交完了,如果 提交完成了就将延迟提交任务从对应优先级的就绪任务列表移除,如果还有信息队列等待提交就继续调用OS_IntQRePost();进行内核对象提交。
void OS_IntQRePost (void){ CPU_TS ts; OS_ERR err; //根据提交对象的类型进行选择内核对象提交函数 switch (OSIntQOutPtr->Type) { /* Re-post to task */ case OS_OBJ_TYPE_FLAG:#if OS_CFG_FLAG_EN > 0u (void)OS_FlagPost((OS_FLAG_GRP *) OSIntQOutPtr->ObjPtr, (OS_FLAGS ) OSIntQOutPtr->Flags, (OS_OPT ) OSIntQOutPtr->Opt, (CPU_TS ) OSIntQOutPtr->TS, (OS_ERR *)&err);#endif break; case OS_OBJ_TYPE_Q:#if OS_CFG_Q_EN > 0u OS_QPost((OS_Q *) OSIntQOutPtr->ObjPtr, (void *) OSIntQOutPtr->MsgPtr, (OS_MSG_SIZE) OSIntQOutPtr->MsgSize, (OS_OPT ) OSIntQOutPtr->Opt, (CPU_TS ) OSIntQOutPtr->TS, (OS_ERR *)&err);#endif break; case OS_OBJ_TYPE_SEM:#if OS_CFG_SEM_EN > 0u (void)OS_SemPost((OS_SEM *) OSIntQOutPtr->ObjPtr, (OS_OPT ) OSIntQOutPtr->Opt, (CPU_TS ) OSIntQOutPtr->TS, (OS_ERR *)&err);#endif break; case OS_OBJ_TYPE_TASK_MSG:#if OS_CFG_TASK_Q_EN > 0u OS_TaskQPost((OS_TCB *) OSIntQOutPtr->ObjPtr, (void *) OSIntQOutPtr->MsgPtr, (OS_MSG_SIZE) OSIntQOutPtr->MsgSize, (OS_OPT ) OSIntQOutPtr->Opt, (CPU_TS ) OSIntQOutPtr->TS, (OS_ERR *)&err);#endif break; case OS_OBJ_TYPE_TASK_RESUME:#if OS_CFG_TASK_SUSPEND_EN > 0u (void)OS_TaskResume((OS_TCB *) OSIntQOutPtr->ObjPtr, (OS_ERR *)&err);#endif break; case OS_OBJ_TYPE_TASK_SIGNAL: (void)OS_TaskSemPost((OS_TCB *) OSIntQOutPtr->ObjPtr, (OS_OPT ) OSIntQOutPtr->Opt, (CPU_TS ) OSIntQOutPtr->TS, (OS_ERR *)&err); break; case OS_OBJ_TYPE_TASK_SUSPEND:#if OS_CFG_TASK_SUSPEND_EN > 0u (void)OS_TaskSuspend((OS_TCB *) OSIntQOutPtr->ObjPtr, (OS_ERR *)&err);#endif break; case OS_OBJ_TYPE_TICK:#if OS_CFG_SCHED_ROUND_ROBIN_EN > 0u //任务时间片轮换调度任务 OS_SchedRoundRobin(&OSRdyList[OSPrioSaved]);#endif //时间任务管理 (void)OS_TaskSemPost((OS_TCB *)&OSTickTaskTCB, /* Signal tick task */ (OS_OPT ) OS_OPT_POST_NONE, (CPU_TS ) OSIntQOutPtr->TS, (OS_ERR *)&err);#if OS_CFG_TMR_EN > 0u //定时器任务提交 OSTmrUpdateCtr--; if (OSTmrUpdateCtr == (OS_CTR)0u) { OSTmrUpdateCtr = OSTmrUpdateCnt; ts = OS_TS_GET(); /* Get timestamp */ (void)OS_TaskSemPost((OS_TCB *)&OSTmrTaskTCB, /* Signal timer task */ (OS_OPT ) OS_OPT_POST_NONE, (CPU_TS ) ts, (OS_ERR *)&err); }#endif break; default: break; }}
这个函数内就是根据延迟提交的内核对象的类型然后分别调用内核对应的Post函数对内核对象进行提交。从而完成内核对象的延迟提交。