μC/OS中高优先级任务一旦处于就绪态总能获得CPU控制权的认识
发布时间:2021-11-22 13:51:23 所属栏目:教程 来源:互联网
导读:COS的任务切换时间:1.任务创建时 2.任务挂起时 3.任务恢复 4.任务延时时 6.任务释放信号量时 7.任务释放互斥信号量时 8.任务请求消息邮箱时 9.任务释放消息队列时 10 中断退出时(OSINTEXIT()函数中) 任务之前的切换应该就是利用时钟中断来实现,当OS运行
μCOS的任务切换时间:1.任务创建时 2.任务挂起时 3.任务恢复 4.任务延时时 6.任务释放信号量时 7.任务释放互斥信号量时 8.任务请求消息邮箱时 9.任务释放消息队列时 10 中断退出时(OSINTEXIT()函数中) 任务之前的切换应该就是利用时钟中断来实现,当OS运行完一个时钟片后会产生一个中断(定时器的中断)异常,PC指针立即跳转到异常向量表执行处理异常的代码,随后会导致OS执行一次任务调度。整个过程分析如下: 产生异常后执行的第一条指令 b HandlerIRQ 跳转到HandlerIRQ,而HandlerIRQ是一条宏命令,定义如下: HandlerIRQ HANDLER HandleIRQ 宏体如下: MACRO $HandlerLabel HANDLER $HandleLabel $HandlerLabel sub sp,sp,#4 stmfd sp!,{r0} //后面代码需要使用R0寄存器,所以先将其入栈。 ldr r0,=$HandleLabel //此处是将HandeIRQ的地址赋给寄存器R0,Handler中存放是中断服务函数汇编部分的入口地址,此汇编用来计算中断服务函数的入口地址,所有IRQ中断通用。HandeIRQ的赋值操作在后面。 ldr r0,[r0] //此处读取R0中的内容,即中断服务函数汇编部分的入口地址 str r0,[sp,#4] ldmfd sp!,{r0,pc} //出栈,栈中R0的值重新赋给R0,中断服务函数汇编入口地址复制给PC。相当于跳转到汇编入口地址处。 MEND HandeIRQ中存放汇编入口地址的代码如下。 ldr r0,=HandleIRQ //取HandeIRQ地址 ldr r1, =OS_CPU_IRQ_ISR //取OS_CPU_IRQ_ISR地址 str r1,[r0] //将OS_CPU_IRQ_ISR地址赋值给HandeIRQ 执行完宏体的结果应该是跳转到OS_CPU_IRQ_ISR OS_CPU_IRQ_ISR STMFD SP!, {R1-R3} //后面会皆用这三个寄存器,所以先将其入栈 MOV R1, SP //把IRQ模式下的当前SP指针保存到R1 ADD SP, SP, #12 //调整SP,使其指向R1~R3入栈前的地址 SUB R2, LR, #4 //LR保存的是返回值的地址,但是不同的异常产生的LR不同。IRQ异常时,LR保存的是下一条将被执行的指令+4。所以此处需要LR-4。来调整。 MRS R3, SPSR ; Copy SPSR (Task CPSR) MSR CPSR_cxsf, #SVCMODE|NOINT //切换处理器运行状态,SP也会更改为特权模式的堆栈指针 下面代码的作用是将被中断的任务的相关寄存器保存到特权模式的堆栈下。 STMFD SP!, {R2} //R2中存放的是被中断任务的下一条将被执行的指令,将其入栈。 STMFD SP!, {R4-R12, LR} ; Push task''s LR,R12-R4 LDMFD R1!, {R4-R6} //R1此时是IRQ堆栈的栈顶指针,和下一条指令一起将被中断任务的R1~R3寄存器保存到特权模式的堆栈中 STMFD SP!, {R4-R6} ; Push Task''s R3-R1 to SVC stack STMFD SP!, {R0} ; Push Task''s R0 to SVC stack STMFD SP!, {R3} ; Push task''s CPSR 此时的特权模式堆栈结构应该是: LR R12 R11 R10 R9 R8 R7 R6 R5 R4 R3 R2 R1 R0 CPSR LDR R0,=OSIntNesting ;OSIntNesting++ LDRB R1,[R0] ADD R1,R1,#1 STRB R1,[R0] //μC/OS中OSInrNesting变量+1 CMP R1,#1 ;if(OSIntNesting==1){ BNE %F1 LDR R4,=OSTCBCur // ;OSTCBHighRdy->OSTCBStkPtr=SP; LDR R5,[R4] //将当前的TCB指针赋值R5 STR SP,[R5] //将被特权模式的栈顶指针保存到北中断任务的OSTCBStkPtr中 1 MSR CPSR_c,#IRQMODE|NOINT ;Change to IRQ mode to use IRQ stack to handle interrupt LDR R0, =INTOFFSET LDR R0, [R0] LDR R1, IRQIsrVect //IRQ已经通过指令 //IRQIsrVect DCD HandleEINT0被赋值为HandleEINT0的地址 MOV LR, PC //此时的PC应该指向MSR那条指令,LDR这条恰好是跳转指令,这样跳转之后刚好返回MSR。 LDR PC, [R1, R0, LSL #2] //此处计算C语言的中断服务函数入口地址,然后跳转执行。 MSR CPSR_c,#SVCMODE|NOINT //中断服务函数执行完后返回 BL OSIntExit //跳转OSIntExit执行,先插入分析OSIntExit void OSIntExit (void) { #if OS_CRITICAL_METHOD == 3 OS_CPU_SR cpu_sr = 0; #endif if (OSRunning == OS_TRUE) { OS_ENTER_CRITICAL(); if (OSIntNesting > 0) { OSIntNesting--; } if (OSIntNesting == 0) { if (OSLockNesting == 0) { OS_SchedNew(); //计算处于最高优先级的任务 if (OSPrioHighRdy != OSPrioCur) { //假如就绪态的任务优先级高于当前运行的任务优先级着将其调度执行 OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy]; #if OS_TASK_PROFILE_EN > 0 OSTCBHighRdy->OSTCBCtxSwCtr++; #endif OSCtxSwCtr++; OSIntCtxSw(); //任务切换 } } } OS_EXIT_CRITICAL(); } } LDMFD SP!,{R4} //将当前堆栈保存的数据弹出一个到R4,即将被中断的任务的CPSR保存到R4 MSR SPSR_cxsf,R4 LDMFD SP!,{R0-R12,LR,PC}^ 出栈,继续执行被中断的任务。 OSInt和OS_Sched类似但是不能互换使用。OSIntExit含有中断嵌套数的计算。在OSIntExit做任务切换使用的是OSIntCtxSw,而OS_Sched中使用的是OS_TASK_SW。根据如上分析,每个时钟片刻结束时候OS都会根据优先级重新调度一遍,所以说在μC/OS,只要高优先级的任务处于就绪态,就会立即抢占CPU。以为在这一个时钟片结束后,OS会执行一次调度。 (编辑:济南站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |