§4.3.3.2 OSIntCtxSw的编写
在μC/OS-Ⅱ中,任务切换只是简单的将处理器寄存器保存到将被挂起的任务的堆栈中,并且将更高优先级的任务从堆栈中恢复出来。处于就绪状态的任务的堆栈结构看起来就像刚发生过中断并将所有的寄存器保存到堆栈中的情形一样。换句话说,μC/OS-Ⅱ要运行处于就绪状态的任务必须要做的事就是将所有处理器寄存器从任务堆栈中恢复出来,并且执行中断的返回。
在μC/OS-Ⅱ中,用户级任务调度时会调用宏(或者函数)OS_TASK_SW(),它是在μC/OS-Ⅱ从低优先级任务切换到最高优先级任务时被调用的,μC/OS-Ⅱ建议OS_TASK_SW()通过某种途径最终调用函数OSCtxSw()。函数OSCtxSw()是与系统相关的,μC/OS-Ⅱ提供的OSCtxSw()函数原型如下:
OSCtxSw()原型的程序清单
void OSCtxSw(void)
{
保存处理器寄存器;
将当前任务的堆栈指针保存到当前任务的OS_TCB中;
OSTCBCur->OSTCBStkPtr = Stack pointer;
调用用户定义的OSTaskSwHook();
OSTCBCur = OSTCBHighRdy;
OSPrioCur = OSPrioHighRdy;
得到需要恢复的任务的堆栈指针;
Stack pointer = OSTCBHighRdy->OSTCBStkPtr;
将所有处理器寄存器从新任务的堆栈中恢复出来;
执行中断返回指令;
}
在μC/OS-Ⅱ中,函数OSIntExit( )被用来在ISR使得更高优先级任务处于就绪状态时,执行任务切换功能。中断退出函数通过调OSIntCtxSw()来从ISR中执行切换功能。函数OSIntCtxSw()是与系统相关的,μC/OS-Ⅱ提供的OSIntCtxSw()函数原型如下:
OSIntCtxSw( )原型的程序清单
void OSIntCtxSw(void)
{
调用用户定义的OSTaskSwHook( ) ;
OSTCBCur = OSTCBHighRdy ;
OSPrioCur = OSPrioHighRdy ;
得到需要恢复的任务的堆栈指针 ;
堆栈指针 = OSTCBHighRdy->OSTCBStkPtr ;
将所有处理器寄存器从新任务的堆栈中恢复出来;
执行中断返回指令;
}
对比两个函数原型,除OSCtxSw()原型的程序清单比OSIntCtxSw( )原型的程序清单多了两句外,其它都是一样的。由异常处理代码与C语言的接口程序可知,OS_TASK_SW()实质是软件中断的功能号0,在软件中断中已经把除变量OsEntersum外的所有寄存器保存到管理模式的堆栈中。而由程序可知,当程序执行函数 OSIntCtxsw()时,变量 OsEnterSum也没有保存到 IRQ模式的堆栈中。也就是说,两种情况需要做的工作一样,都可用同一段代码实现。
OSIntCtxSw
;下面为保存任务环境
LDR R2, [SP, #20] ;获取PC (1)
LDR R12, [SP, #16] ;获取R12 (2)
MRS R0, CPSR (3)
MSR CPSR_c, #(NoInt | SYS32Mode) (4)
MOV R1, LR (5)
STMFD SP!, {R1-R2} ;保存LR,PC (6)
STMFD SP!, {R4-R12} ;保存R4-R12 (7)
MSR CPSR_c, R0 (8)
LDMFD SP!, {R4-R7} ;获取R0-R3 (9)
ADD SP, SP, #8 ;出栈R12,PC (10)
MSR CPSR_c, #(NoInt | SYS32Mode) (11)
STMFD SP!, {R4-R7} ;保存R0-R3 (12)
LDR R1, =OsEnterSum ;获取OsEnterSum (13)
LDR R2, [R1] (14)
STMFD SP!, {R2, R3} ;保存CPSR,OsEnterSum (15)
;保存当前任务堆栈指针到当前任务的TCB
LDR R1, =OSTCBCur (16)
LDR R1, [R1] (17)
STR SP, [R1] (18)
BL OSTaskSwHook ;调用子函数 (19)
LDR R4, =OSPrioCur (20)
LDR R5, =OSPrioHighRdy (21)
LDRB R6, [R5] (22)
STRB R6, [R4] (23)
LDR R6, =OSTCBHighRdy (24)
LDR R6, [R6] (25)
LDR R4, =OSTCBCur (26)
STR R6, [R4] (27)
OSIntCtxSw_1
;获取新任务堆栈指针
LDR R4, [R6] (28)
ADD SP, R4, #68 (29)
LDR LR, [SP, #-8] (30)
MSR CPSR_c, #(NoInt | SVC32Mode) ;进入管理模式(31)
MOV SP, R4 ;设置堆栈指针(32)
LDMFD SP!, {R4, R5} ;CPSR,OsEnterSum (33)
;恢复新任务的OsEnterSum
LDR R3, =OsEnterSum (34)
STR R4, [R3] (35)
MSR SPSR_cxsf, R5 ;恢复CPSR (36)
LDMFD SP!, {R0-R12, LR, PC }^ ;运行新任务(37)
这部分代码基本按照μC/OS-Ⅱ提供的函数原型编写的,其中程序清单(1)—(18)部分与OSCtxSw()和OSIntCtxSw( )的原型是没有对应语句的,寄存器应当保存到任务的堆栈中,但为了节省CPU的时间和RAM的空间,仅在必要的时候才将寄存器保存到任务堆栈。OSTCBCur->OSTCBStkPtr=SP也是在必要的时候才执行的。这部分正是在处理这两件事情,其流程图见图4-4。
图4-4 OSIntCtxSw部分代码流程图
由软中断的汇编与C接口程序可知,在调用OS_TASK_SW( )(即软件中断的功能号0)时,寄存器R0—R3、R12、PC已经保存到当前模式的堆栈中,任务的R4—R11、SP、LR没有发生变化。也就是说寄存器已经保存,但不是保存到任务的堆栈中。同样由异常处理代码与C语言的接口程序可知,在调用OSIntCtxSw( )时,寄存器R0—R3、R12、PC已经保存到当前模式的堆栈中,任务的R4—R11、SP、LR没有发生变化。也就是说寄存器已经保存,但不是保存到任务的堆栈中。当前处理器模式的堆栈结构如图4-5所示。
图4-5 当前处理器模式堆栈结构图
在执行(1)—(18)这部分程序时的寄存器和存储器之间的具体关系如图4-6所示,图中的标号为对应的程序段,这里是以调用OS_TASK_SW( )为例子来说明,调用OSIntCtxSw( )的工作过程和调用OS_TASK_SW( ) 的工作过程是一样的,只不过调用OS_TASK_SW( )时处理器当前模式为管理模式,而调用OSIntCtxSw( )时处理器当前模式为IRQ模式。
图4-6-1
此时处理器处于管理模式,因为本移植是使用软中断指令SWI使处理器进入管理模式和ARM指令状态,并使用功能0来实现OS_TASK_SW( )的功能。具体参见软中断的汇编与C接口程序代码。
图4-6-2
此时是通过执行程序段(4),利用MSR指令直接设置状态寄存器CPSR的模式位,使处理器进入用户/系统模式此时需要注意的是处理器的可见寄存器与管理模式时是有区别的。在保存之后同样是通过执行程序段(8),利用MSR指令返回到管理模式。
图4-6-3
进入管理模式以后继续保存数据,当数据保存好以后,要注意调整当前模式堆栈指针因为当前模式的入栈比出栈的数据多,而在堆栈中剩余的数据(R12、PC)已经没有用处,所以要进行调整。完成指针调整的为程序段(10)。
图4-6-4
此时又使处理器进入用户/系统模式,继续把未保存的寄存器内容保存到任务堆栈,这里要注意的是R3保存着任务的CPSR(既当前模式的SPSR),这部分在异常处理代码与C语言的接口程序中完成的。至此,寄存器内容已经完全保存到任务堆栈里了。
从标号OSIntCtxSw_1处开始至程序的最后,是将所有处理器寄存器从新任务的堆栈中恢复出来。处理器执行这段代码时处于ARM状态,但用户任务可能处于Thumb状态。而由ARM的相关资料可知,程序不可以直接改变程序状态寄存器的CPSR的T位来改变处理器的状态。但“LDMFD SP!, {R0-R12, LR, PC }^ ” 异常返回指令是可以使用的(具体参见ARM的多寄存器加载指令),所以在恢复寄存器时,必须使处理器处于某种异常模式。由于FIQ模式和IRQ模式对应中断,SP指针不能随意改变,而未定义模式和中止模式以后可能会用到,所以本移植选择为管理模式。
在执行(28)—(37)这部分程序时的寄存器和存储器之间的具体关系如图4-7所示,图中的标号为对应的程序段。
图4-7-1
由于 OSTCBHirtRdy->OSTCBStkPtr保存的是任务堆栈位置,而寄存器恢复后堆栈指针并不指向这里,所以要调整新任务堆栈指针,这是由程序段(29)完成的。
图4-7-2
然后通过执行程序段(31),利用MSR指令直接设置状态寄存器CPSR的模式位,使处理器进入管理模式,先恢复新任务OsEntersum,这是由程序段(34)(35)完成的,然后把新任务的 CPSR恢复到 SPSR这是由程序段(36)实现的。
图4-7-3
最后通过中断返回指令恢复R0—R12,把SPSR拷贝到CPSR(恢复用户任务的处理器模式和指令集)和执行用户任务(恢复PC指针),这是由程序段(37)实现的。这里需要注意的是程序段(29)和(32)中的SP是不同的处理器寄存器分别为R13和R13_SVC。
§4.3.3.3 OSStartHighRdy的编写
μC/OS-Ⅱ启动多任务环境的函数叫做OSStart()。用户在调用OSStart()之前,必须已经建立了一个或更多任务。OSStart()最终调用OSStartHighRdy()函数运行多任务启动前优先级最高的任务。由软中断的汇编与C接口程序SoftwareInterrupt可知,这是调用软中断的1号功能。这是因为ARM处理器核具有两个指令集,在执行Thumb指令的状态时不是所有寄存器都可见(参考ARM的相关资料),而且任务可能不在特权模式。为了兼容任意一种模式,本移植使用软中断指令SWI使处理器进入管理模式和ARM指令状态,并使用功能1实现OSStartHighRdy的功能。μC/OS-Ⅱ中调用函数 OSStartHighRdy()之前,OSTCBHighRdy指向的是优先级最高的任务的任务控制块。μC/OS-Ⅱ要求处于就绪状态的任务的堆栈结构看起来就像刚发生过中断,并将所有寄存器保存到堆栈中的情形一样。要想运行最高优先级任务,用户要做的是将所有处理器寄存器按顺序从任务堆栈中恢复出来,并且执行中断的返回。μC/OS-Ⅱ提供的OSStartHighRdy函数原型如下:
OSStartHighRdy原型的程序清单
void OSStartHighRdy (void)
{
调用用户函数OSTaskSwHook();
OSRunning=True;
获取堆栈指针SP=OSTCBHighRdy->OSTCBStkPtr:
从新任务堆栈中恢复所有寄存器;
执行中断返回指令;
}
OSStartHighRdy的程序代码如下:
OSStartHighRdy
MSR CPSR_c, #(NoInt | SYS32Mode)
;告诉uC/OS-II自身已经运行
LDR R4, =OSRunning
MOV R5, #1
STRB R5, [R4]
BL OSTaskSwHook ;调用子函数
LDR R6, =OSTCBHighRdy
LDR R6, [R6]
B OSIntCtxSw_1
这部分代码是比较严格的按照μC/OS-Ⅱ提供的原型编写的,其中OSIntCtxSw_1的代码在上一小节已经详细介绍过。
这里需要对“MSR CPSR_c, #(NoInt | SYS32Mode)”作以说明,在μC/OS -Ⅱ中,需要用户提供周期性信号源,用于实现时间延时和确认超时。必须在多任务系统启动以后再开启时钟节拍器,也就是在调用OSStart()之后。换句话说,在调用OSStart()之后做的第一件事是初始化定时器中断。通常,容易犯的错误是将允许时钟节拍器中断放在系统初始化函数OSInit()之后,在调启动多任务系统启动函数OSStart()之前允许时钟节拍中断。μC/OS-Ⅱ的启动多任务函数OSStart()会在最后调用OSStartHighRdy,而SStartHighRdy的第一条语句就允许中断。这里是将CPSR的第8位置1,允许IRQ中断。
至此已经将μC/OS-Ⅱ移植到了LPC2106,下面可以进行瞬时电压有效值检测的程序的编写。
此文转自:http://hi.baidu.com/goby2004/blog/item/352b7d13dbac49c4c3fd78f3.html