UCOS-II的OSCtxSw函数移植

OSCtxSw()函数把被中止任务的断点指针和CPU的寄存器值保存到该任务的堆栈中,并从待运行任务的堆栈中得到其堆栈指针,把待运行任务堆栈中的存储的CPU通用寄存器的内容恢复到CPU的通用寄存器中,最后使CPU获得待运行任务的断点指针。

任务切换函数OSCtxSw()一般有2种方法实现。一种是软中断,周立功的LPC资料上都是这么用的。一种是用子程序调用,我现在做的这个项目就是这么实现的。

在OS_CPU.H中定义

#define OS_TASK_SW() OSCtxSw()

OSCtxSw()函数在OS_CPU_A.S中:

OSCtxSw

STMFD SP!, {LR} ;PC (1)

STMFD SP!, {R0-R12, LR} ;R0-R12 LR (2)

MRS R4, CPSR ;Push CPSR (3)

STMFD SP!, {R4}

LDR R4, =OSTCBCur ; OSTCBCur->OSTCBStkPtr = SP; (4)

LDR R5, [R4] ;(5)

STR SP, [R5] ;(6)

;BL OSTaskSwHook ; OSTaskSwHook(); (7)

LDR R4, =OSPrioCur ; OSPrioCur = OSPrioHighRdy (8)

LDR R5, =OSPrioHighRdy ;(9)

LDRB R6, [R5] ;(10)

STRB R6, [R4] ;(11)

LDR R4, =OSTCBCur ; OSTCBCur = OSTCBHighRdy; ;(12)

LDR R6, =OSTCBHighRdy ;(13)

LDR R6, [R6] ;(14)

STR R6, [R4] ;(15)

LDR SP, [R6] ; SP = OSTCBHighRdy->OSTCBStkPtr; ;(16)

LDMFD SP!, {R4} ;POP CPSR ;(17)

MSR CPSR_cxsf, R4 ;(18)

LDMFD SP!, {R0-R12, LR, PC} ;(19)

OS_Sched (void)函数在OS_CORE.C中:

void OS_Sched (void)

{

#if OS_CRITICAL_METHOD == 3

OS_CPU_SR cpu_sr = 0;

#endif

OS_ENTER_CRITICAL();

if (OSIntNesting == 0) {

if (OSLockNesting == 0) {

OS_SchedNew();

if(OSPrioHighRdy!=OSPrioCur) {

OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy];

#if OS_TASK_PROFILE_EN > 0

OSTCBHighRdy->OSTCBCtxSwCtr++;

#endif

OSCtxSwCtr++; OS_TASK_SW();

}

}

}

OS_EXIT_CRITICAL();

}

在ADS中通过汇编语言可以看到,OS_TASK_SW();语句对应的汇编语句是 BL OSCtxSW

我的程序中BL OSCtxSW下条语句的地址是0x00000CB4,在执行完BL OSCtxSW这条语句后,程序跳到OSCtxSW函数处,同时R14(LR)的值变为0x00000CB4。

BL命令在执行时会自动把下一条指令的地址值拷贝到LR中,但是不会自动保存状态寄存器CPSR。

下面分析OSCtxSW函数:

1、 把LR的值保存到sp – 4后对应得地址中,同时sp = sp – 4。被中止任务再次调度执行时,会把这个值送到PC寄存器中,使被中止任务从断点处继续执行。

2、 把R12-R0寄存器的值压入堆栈,把LR的值再次压入堆栈。就是保存R14(LR)的值,便于恢复。LR的值再次保存我觉得没有什么必要。因为LR的值已经保存过一次,在任务恢复执行时,会把这个值送到PC寄存器。任务切换后,会从待运行任务的堆栈中恢复一个值到PC寄存器。

LR在调用OSCtxSW函数和其它子函数时的作用是不一样的。在调用OSCtxSW函数时,

LR的作用是保存当前任务的断点,并把断点值送到当前任务的堆栈。在执行完OSCtxSW函数后,程序执行的已经是另外的任务,和调用OSCtxSW函数时保存的LR值已经没关系了。

调用其它子函数时,LR保存当前任务断点,执行完子函数后,把LR的值送到PC寄存器,任务继续运行。

这个地方写的很罗嗦,因为这个地方我觉得不好理解,所以不厌其烦,希望已经说清楚了。

3、保存CPSR的值

4-6、把当前的SP值送到当前任务的任务控制块OSTCBCur->OSTCBStkPtr

7、调用OSTaskSwHook函数,这个函数用户自己添加内容,一般为空。可以不调用

8-11、获得待运行任务的控制块地址。

OSPrioHighRdy在OS_SCHED()函数中得到,是一个8位数,指待运行的优先级最高的任务的优先级号。

OSPrioCur是当前任务的优先级号,在8处仍代表被中止任务。

OSPrioCur变为OSPrioHighRdy的值

12-15、改变OSTCBCur的值,使OSTCBCur指向待运行任务的控制块。

16、把待运行任务的堆栈指针值送到SP寄存器。

17-18、从堆栈中得到CPSR的值。

19、从堆栈中得到cpu其它寄存器的值。开始执行新的任务。

发表评论

电子邮件地址不会被公开。 必填项已用*标注