S3C2410平台UCOS移植笔记.docx
《S3C2410平台UCOS移植笔记.docx》由会员分享,可在线阅读,更多相关《S3C2410平台UCOS移植笔记.docx(8页珍藏版)》请在冰豆网上搜索。
S3C2410平台UCOS移植笔记
S3C2410之UCOS移植笔记
2011-10-0120:
10
μC/OS-II大部分代码是用移植性很高的ANSIC语言编写的,只包含一小部分汇编语言代码,因此可以很方便地将它移植到各种不同构架的微处理器上。
移植μC/OS-II,所用处理器和该处理器所用的C语言编译器必须满足以下条件:
1.处理器的C编译器能产生可重入代码;
2.处理器支持中断,并且能产生定时中断;
3.用C语言就可以开/关中断;
4.处理器能支持一定数量的数据存储硬件堆栈;
5.处理器有将堆栈指针以及其他CPU寄存器的内容读出、并存储到堆栈或内存中去的指令。
首先,由于处理器字长不同为了方便,我们通常要修改下字长定义,s3c2410是32位处理器所以定义如下:
#defineU32unsignedint
#defineU16unsignedshort
#defineS32int
#defineS16shortint
#defineU8unsignedchar
#defineS8signedchar
下面我对以上的移植要求一次进行移植分析:
1.修改ucos源代码中OS_CPU.S中的代码如下,主要是用开关中断实现代码的可重入要求:
EXPORTOSCPUSaveSR
OSCPUSaveSR
mrsr0,CPSR
orrr1,r0,#NOINT ;屏蔽irq,fiq
msrCPSR_c,r1
movpc,lr ;跳回
EXPORTOSCPURestoreSR
OSCPURestoreSR
msrCPSR_c,r0
movpc,lr
2. s2c2410支持中断,选择一个定时器作为时钟滴答,来对任务做时间片的调度,有关s3c2410的中断介绍可以参考我得另一片文章。
产生中断后,代码自动跳转到0x0的中断向量表,然后在从中断向量表中跳到下面的程序,进行中断号的分析,然后利用ucos的中断任务切换到中断服务子程序中。
UCOS_IRQHandler
stmfdsp!
{r0-r3,r12,lr};保存现场
blOSIntEnter ;跳到下面程序,实际上是中断嵌套
blC_IRQHandler ;计算出中断号
blOSIntExit ;中断减1,切换最高优先级任务
ldrr0,=OSIntCtxSwFlag;判断是否需要中断切换
ldrr1,[r0]
cmpr1,#1
beq_IntCtxSw ;调用中断任务切换函数,后面分析
ldmfdsp!
{r0-r3,r12,lr};恢复现场
subspc,lr,#4
voidOSIntEnter(void)
{
if(OSRunning==OS_TRUE){
if(OSIntNesting<255u){
OSIntNesting++; /*中断记数+1 */
}
}
}
voidOSIntExit(void)
{
#ifOS_CRITICAL_METHOD==3
OS_CPU_SRcpu_sr=0;
#endif
if(OSRunning==OS_TRUE){
OS_ENTER_CRITICAL();
if(OSIntNesting>0){ 有中断,中断数-1
OSIntNesting--;
}
if(OSIntNesting==0){ 如果没有中断的话
if(OSLockNesting==0){
OS_SchedNew();
if(OSPrioHighRdy!
=OSPrioCur){
OSTCBHighRdy=OSTCBPrioTbl[OSPrioHighRdy];
#ifOS_TASK_PROFILE_EN>0
OSTCBHighRdy->OSTCBCtxSwCtr++;
#endif
OSCtxSwCtr++; 将最高优先级的任务调入
OSIntCtxSw();
}
}
}
OS_EXIT_CRITICAL();
}
}
voidC_IRQHandler(void)
{
U32wTemp;
wTemp=rINTOFFSET<<2; 根据中断偏移量判断是什么中断
((void(*)(void))(*((U32*)(aISR_EINT0+wTemp))))();关键!
跳到相应中断服务程序
}
其中的UCOS_IRQHandler就是实现中断跳转的关键代码,C语言中将irq中断函数的入口
都指向它实现中断跳转到C中对应的服务函数,代码如下:
pISR_IRQ=(U32)UCOS_IRQHandler;中断入口挂接
然后初始化一个定时器作为时钟滴答,一般选择100ms左右,产生一次中断溢出,进行
一次任务调度。
3.有关堆栈的设置。
因为ucos进行任务切换时,就是相当于模拟的做中断操作。
所以在切换任务之前必须先保存任务的现场:
包括R0~R12,sp,lr,pc,cprs,sprs。
然后用sp从旧的任务堆栈指向新的任务的堆栈。
(这个堆栈在任务创建的时候已经分配好了)。
在初始化任务堆栈之前,首先要设置两个参数,在CONFIG文件中:
大小端编译模式,堆栈得生长方向。
代码如下:
#defineOS_STK_GROWTH1;堆栈向上生长
然后在任务创建时的堆栈初始化函数,在OS_CPU_C.c文件中:
OS_STK*OSTaskStkInit(void(*task)(void*pd),void*pdata,OS_STK*ptos,INT16Uopt)
{
OS_STK*stk;
opt =opt; /*'opt'isnotused,preventwarning */
stk =ptos; /*Loadstackpointer */
*(stk)=(OS_STK)task; /*EntryPoint */
*(--stk)=(INT32U)0; /*lr */
*(--stk)=(INT32U)0; /*r12 */
*(--stk)=(INT32U)0; /*r11 */
*(--stk)=(INT32U)0; /*r10 */
*(--stk)=(INT32U)0; /*r9 */
*(--stk)=(INT32U)0; /*r8 */
*(--stk)=(INT32U)0; /*r7 */
*(--stk)=(INT32U)0; /*r6 */
*(--stk)=(INT32U)0; /*r5 */
*(--stk)=(INT32U)0; /*r4 */
*(--stk)=(INT32U)0; /*r3 */
*(--stk)=(INT32U)0; /*r2 */
*(--stk)=(INT32U)0; /*r1 */
*(--stk)=(INT32U)pdata; /*r0:
argument */
*(--stk)=(INT32U)(SVCMODE|0x0); /*PSR */
*(--stk)=(INT32U)(SVCMODE|0x0); /*SPSR */
return(stk);
}
4.有关任务调度方面的移植:
首先就是当运行OSSTART()后,系统要进入优先级最高的任务中,即第一个任务,这短代码的移植如下:
OSStartHighRdy
blOSTaskSwHook ;
ldrr4,=OSRunning ;运行多任务
movr5,#1
strbr5,[r4]
ldrr4,=OSTCBHighRdy ;得到最高优先级任务的TCB地址
ldrr4,[r4] ;得到其堆栈地址
ldrsp,[r4] ;切换到新任务的堆栈
ldmfdsp!
{r4} ;把新任务堆栈的内容环境载进当片CPU
msrSPSR_cxsf,r4
ldmfdsp!
{r4} ;popnewtask'spsr
msrCPSR_cxsf,r4
ldmfdsp!
{r0-r12,lr,pc} ;popnewtask'sr0-r12,lr&pc
其次时对任务调度函数的移植,主要是在时间片到时,读取就绪表中优先级最高的任务,并切换到该任务的环境中:
OSCtxSw
;保存要被切换出去的任务环境
stmfdsp!
{lr} ;pushpc(lrshouldbepushedinplaceofPC)
stmfdsp!
{r0-r12,lr} ;pushlr®isterfile
mrsr4,cpsr
stmfdsp!
{r4} ;pushcurrentpsr
mrsr4,spsr
stmfdsp!
{r4} ;pushcurrentspsr
;把就绪表中优先级最高的任务载入到当前任务指针指向
;OSPrioCur=OSPrioHighRdy
ldrr4,=OSPrioCur
ldrr5,=OSPrioHighRdy
ldrbr6,[r5]
strbr6,[r4]
;得到当前任务的TCB地址
;GetcurrenttaskTCBaddress
ldrr4,=OSTCBCur
ldrr5,[r4]
strsp,[r5] ;storespinpreemptedtasks'sTCB
blOSTaskSwHook ;callTaskSwitchHook
;得到最高优先级任务的地址,并把堆栈指针指向它的堆栈地址
;GethighestprioritytaskTCBaddress
ldrr6,=OSTCBHighRdy
ldrr6,[r6]
ldrsp,[r6] ;getnewtask'sstackpointer
;调出新任务的环境实现切换
;OSTCBCur=OSTCBHighRdy
strr6,[r4] ;setnewcurrenttaskTCBaddress
ldmfdsp!
{r4} ;popnewtask'sspsr
msrSPSR_cxsf,r4
ldmfdsp!
{r4} ;popnewtask'spsr
msrCPSR_cxsf,r4
ldmfdsp!
{r0-r12,lr,pc} ;popnewtask'sr0-r12,lr&pc
最后是中断级的任务切换,这种情况发生在,在中断服务程序中报告了一个或多个事件的发生,而这些事件的发生使一些更高优先级的任务进入就绪状态。
因此在中断退出时不应该返回到被中断的任务,而应该返回到就绪态中优先级最高的任务。
函数OSIntCtxSw()的主要功能是完成中断级的任务切换。
与任务级的任务切换不同的时,在进入中断后,所有寄存器的值都被入栈了。
因此在进入任务级切换时寄存器的入栈操作应该再有。
另外,在中断服务程序中调用了函数OSIntExit()和OSIntCtxSw(),因此应该调整当前任务的堆栈指针,让任务下次再被执行时返回中断服务程序而不是返回函数OSIntExit()和OSIntCtxSw()。
剩下的任务就是进行任务切换了,这些代码同函数OSCtxSw()的大部分代码都相同。
_IntCtxSw
;对堆栈指针的调整
movr1,#0
strr1,[r0]
ldmfdsp!
{r0-r3,r12,lr}
stmfdsp!
{r0-r3}
movr1,sp
addsp,sp,#16
subr2,lr,#4
mrsr3,spsr
orrr0,r3,#NOINT
msrspsr_c,r0
ldrr0,=.+8
movspc,r0
;以下代码和正常的任务切换过程完全一样
stmfdsp!
{r2} ;pusholdtask'spc
stmfdsp!
{r4-r12,lr} ;pusholdtask'slr,r12-r4
movr4,r1 ;Specialoptimisedcodebelow
movr5,r3
ldmfdr4!
{r0-r3}
stmfdsp!
{r0-r3} ;pusholdtask'sr3-r0
stmfdsp!
{r5} ;pusholdtask'spsr
mrsr4,spsr
stmfdsp!
{r4} ;pusholdtask'sspsr
;OSPrioCur=OSPrioHighRdy
ldrr4,=OSPrioCur
ldrr5,=OSPrioHighRdy
ldrbr5,[r5]
strbr5,[r4]
;GetcurrenttaskTCBaddress
ldrr4,=OSTCBCur
ldrr5,[r4]
strsp,[r5] ;storespinpreemptedtasks'sTCB
blOSTaskSwHook ;callTaskSwitchHook
;GethighestprioritytaskTCBaddress
ldrr6,=OSTCBHighRdy
ldrr6,[r6]
ldrsp,[r6] ;getnewtask'sstackpointer
;OSTCBCur=OSTCBHighRdy
strr6,[r4] ;setnewcurrenttaskTCBaddress
ldmfdsp!
{r4} ;popnewtask'sspsr
msrSPSR_cxsf,r4
ldmfdsp!
{r4} ;popnewtask'spsr
msrCPSR_cxsf,r4
ldmfdsp!
{r0-r12,lr,pc} ;popnewtask'sr0-r12,lr&pc
这样我们就可以实现ucos在s3c2410的移植了。
注意如果要是在ram中仿真时,必须把中断向量表预先烧录到0x0处才能正常实现中断的跳转。