您的位置:首页 > 其它

移植UCOS-II到ARM7TDMI

2007-12-13 15:19 871 查看
UCOS-II移植到ARM需要做的工作如下:
<!--[if !supportLists]-->1) <!--[endif]-->编写一个的与CPU相关的文件OS_CPU_A.S,此文件包含四个函数,这四个函数需用汇编编写。
<!--[if !supportLists]-->a) OSStartHighRdy( ),此函数作用为启动最高优先级任务,由OSStart( )调用。
<!--[if !supportLists]-->b) OSCtxSw( ),任务级别的切换函数,例如当前运行的任务不再是优先级最高时,这时就需要任务的切换(比如当前任务申请一个信号量而被挂起,或由于任务释放资源时,这些时候系统会进行任务的切换)。OSCtxSw( )由OSSche( )调用。
<!--[if !supportLists]-->c)<!--[endif]--> OSIntCtxSw( ),此函数用于由中断产生的任务切换。比如系统时钟使得一个更高优先级的任务就绪时,或者中断使得更高优先级就绪时。由于发生中断时,系统的堆栈已经保存了当前任务的上下文,而后中断处理程序又调用OSIntExit( ),最后才由OSIntExit( )调用OSIntCtxSw( )进行任务的切换,这时堆栈中就多了一些内容,需要将这些多余的内容去掉,然后调用OSCtxSw( )。
<!--[if !supportLists]-->d) <!--[endif]-->OSTickISR( ),为UCOS-II提供一个时钟资源来实现时间的延时和期满的功能。时钟节拍应该每秒发生10—100次。这与具体的处理器有关。
<!--[if !supportLists]-->2) <!--[endif]-->编写OS_CPU_C.C文件
要求编写六个简单的函数:
OSTaskStkInit( );
OSTaskCreateHook( );
OSTaskDelHook( );
OSTaskSwHook( );
OSTaskStatHook( );
OSTimeTickHook( );
在这六个函数中最关键的是编写OSTaskStkInit( ),此函数是在任务被创建的时候,由OSTaskCreate( )调用。主要用于初始化任务堆栈,任务堆栈结构与CPU的体系结构、编译器密切相关。本次移植的任务堆栈结构如下图(一)



1、OS_CPU.H
本移植是将UCOS-II移植到ARMTDMI内核,ARMTDMI有7个工作模式,两个指令系统ARM和THUMB。
在7个工作模式中只选用用户和系统模式。本次移植将实现在用户和系统模式转换,ARM和THUMB指令系统的转换。
当系统初始化完成后,将进入用户模式,在用户模式下进行任务的创建和运行,然后调用相应的软中断来实现用户和系统模式转换,ARM和THUMB指令系统的转换,以及任务切换等工作。也就是说任务切换是通过软中断来实现的。各个软中断(SWI)服务如下:(OS_TASK_SW( )和OSStartHighRdy( )在OS_CPU_A.S中实现,其他各功能实现在OS_CPU_C.C中)
__swi(0x0) void OS_TASK_SW(void); /*任务级切换函数 */
__swi(0x1) void OSStartHighRdy(void); /*运行优先级最高的任务*/
__swi(0x2) void OS_ENTER_CRITICAL(void); /*进入临界区,关中断 */
__swi(0x3) void OS_EXIT_CRITICAL(void); /*推出临界区,开中断 */
__swi(0x80) void ChangToSYSMode(void); /*任务切换到系统模式 */
__swi(0x81) void ChangToUserMode(void); /*任务切换到用户模式 */
__swi(0x82) void TaskIsARM(INT8U prio); /*任务代码是ARM指令 */
__swi(0x83) void TaskIsTHUMB(INT8U prio); /*任务代码为THUMB指令*/
TaskIsARM( ) 和TaskIsTHUMB( )使用时应该注意,他们必须在任务建立后但还没运行时被调用。有以下三中解决方法:
<!--[if !supportLists]-->A、 <!--[endif]-->高优先级任务使用默认的指令集;
<!--[if !supportLists]-->B、 <!--[endif]-->改变OSTaskCreateHook( )使任务不处于就绪状态,建立任务后调用函数OSTaskResume( )来使任务进入就绪状态;
建立任务时禁止任务切换,调用TaskIsARM( ) 或TaskIsTHUMB( )后再允许任务切换。

<!--[if !supportLists]-->1、 <!--[endif]-->OS_CPU_C.C
在此文件中主要是编写OSTaskStkInit( )由于任务堆栈都已经确定好了,如图(一)。很容易写出代码,其代码如下:
void *OSTaskStkInit (void (*task)(void *pd), void *pdata, void *ptos, INT16U opt){

OS_STK *stk;

opt = opt; /* ''opt'' is not used, prevent warning */

stk=(OS_STK *)ptos; /* Load stack pointer */

*stk = (OS_STK)task; /* PC (High address) */

*--stk = (OS_STK)task;/* LR */

*--stk = 0; /* R12 */

*--stk = 0; /* R11 */

*--stk = 0; /* R10 */

*--stk = 0; /* R9 */

*--stk = 0; /* R8 */

*--stk = 0; /* R7 */

*--stk = 0; /* R6 */

*--stk = 0; /* R5 */

*--stk = 0; /* R4 */

*--stk = 0; /* R3 */

*--stk = 0; /* R2 */

*--stk = 0; /* R1 */

*--stk = (unsigned int) pdata; /* R0, the first parameter is transfered by R0 */

*--stk = 0x10; /* CPSR ,enable IRQ and FIQ interrupt (Low address)*/

return ((void *)stk);
}
其余的勾挂函数,在以后具体的应用中根据具体的情况编写。

<!--[if !supportLists]-->1、 <!--[endif]-->OS_CPU_A.S
对于所有的任务级的切换,都是使用SWI软中断来实现。因此首先编写如下的汇编代码,此为SWI的汇编与C
的接口代码。软中断发生后PC指向0x08处,因此可以在0x08处放一条跳转语句指向处理程序SwiFirstHandler,然后通过SwiFirstHandler调用SwiSecondHandler( )来实现其他的功能。SwiFirstHandler如程序清单1
程序清单1 软中断代码的汇编部分
SwiFirstHandler
LDR SP, =SVCStack ; ①
STMFD SP!, {R0-R3, R12, LR} ;保护任务环境

MOV R1, SP ; Set pointer to parameters
MRS R3, SPSR ; 将 SPSR保存到R3,其实SPSR就是任务运行时的CPSR

TST R3,#T_BIT ;判断是否是THUNMB态
LDRNEH R0, [LR, #-2] ; THUMB
BICNE R0, R0, #0xFF00

LDREQ R0, [LR, #-4] ; ARM
BICEQ R0, R0, #0xFF000000

;R0 存放 SWI 功能码

CMP R0, #1
LDRLO PC,=OS_TASK_SW ; SWI 码是 0,进行任务的切换,在OS_CPU_A.S中实现
LDREQ PC,=OSStartHighRdy ; SWI 码是 1,启动任务,在OS_CPU_A.S中实现

BL SwiSecondHandler ; 在在OS_CPU_C.C中实现
LDMFD SP!, {R0-R3,R12,PC}^ ; 没有进行任务切换返回到当前任务
注意①,我们知道在处理器处理软中断时,处理器的工作模式本来就是SVC模式,自然SP也是指向SVC模式的指针,这里为什么还要从新把SVC模式指针赋给SP呢?这里主要是因为在启动代码程序中,将SVC模式指针修改了,所以这里要从新将SVC模式指针赋给SP。这部分启动任务程序在OS_CPU_A.S中。
还需注意的是本段代码并不在OS_CPU_A.S实现,而被放到启动代码文件InitTarget.s实现的。

2.1、OSStartHighRdy()的实现
用伪码描述OSStartHighRdy()如下:
void OSStartHighRdy (void)
{
Call user definable OSTaskSwHook();
Get the stack pointer of the task to resume:
Stack pointer = OSTCBHighRdy->OSTCBStkPtr;
OSRunning = TRUE;
Restore all processor registers from the new task''s stack;
Execute a return from interrupt instruction;
}
OSStartHighRdy( )被OSStart()调用,用于启动高优先级的任务。OSStartHighRdy( )所要做的工作是将图(一)中任务堆栈的初始值直接拷贝到相应寄存器中即可完成。

2.2、OS_TASK_SW( )和OSIntCtxSw( )
由于arm处理器和所用编译器的特点,OS_TASK_SW( )和OSIntCtxSw( )可用相同的代码编写。
虽然OS_TASK_SW( )和OSIntCtxSw( )是在两个不同的模式下被调用,OS_TASK_SW( )是在SVC模式下,而OSIntCtxSw( )是在中断模式下被调用,但是还是可以用相同代码来处理。关键是两种模式堆栈所保存的任务环境要相同,所以对于IRQ模式,也需要做一些保存任务环境的工作,具体采用以下代码来实现,如程序清单2。当发生中断时PC指向0x18处,在此处放一条跳转到IsrIRQ指令。

程序清单2
IsrIRQ ; using I_ISPR register.
sub lr,lr,#4 ; reserved for return
stmfd sp!,{r0-r3,r12,lr} ; restore the contexts of task to be ;interrupted
; IMPORTANT CAUTION
; if I_ISPC isn't used properly, I_ISPR can be 0 in this routine.
ldr r0,=OSIntNesting ; OSIntNesting+1
ldrb r1,[r0]
add r1,r1,#1
strb r1,[r0]
ldr r1,= I_ISPR
ldr r1,[r1]

cmp r1,#0x0 ; if the idel mode work_around is used r9 may 0 sometimes
beq %F2
mov r0,#0x0
0
movs r1,r1,lsr #1
bcs %F1
add r0,r0,#4
b %B0
1
ldr r1,=ADC ; 从ADC开始是对于44B0芯片的全部中断表,这个表如程序清单3
add r1,r1,r0 ; r1+r0就是找到是什么发生了中断请求
ldr pc,[r1] ; 调用中断处理程序
mrs r3,spsr
bl OSIntExit
2
ldmfd sp!,{r0-r3,r12,pc} ; 无任务切换返回

程序清单3
ADC dcd 0
RTC dcd 0
UTXD1 dcd 0
UTXD0 dcd 0
SIO dcd 0
IIC dcd 0
URXD1 dcd 0
URXD0 dcd 0
TIMER5 dcd 0
TIMER4 dcd 0
TIMER3 dcd 0
TIMER2 dcd 0
TIMER1 dcd 0
TIMER0 dcd OSTickISR
UERR01 dcd 0
WDT dcd 0
BDMA1 dcd 0
BDMA0 dcd 0
ZDMA1 dcd 0
ZDMA0 dcd 0
TICK dcd 0
EINT4567 dcd 0
EINT3 dcd 0
EINT2 dcd 0
EINT1 dcd 0
EINT0 dcd 0

需要注意的是这段代码和SwiFirstHandler一样在InitTarget.s中编写,这段代码是按照S3C44B0来编写的,采用的是非向量中断模式。在具体的应用时,中断服务程序的地址自行安排。TIMER0指向OSTickISR也就是说用TIMER0来做操作系统的时钟。
经过IsrIRQ和SwiFirstHandler的处理,不管是在SVC模式还是在IRQ模式,任务都以图(二)的形式保存到各自的堆栈中,这样就可以用相同的程序来处理OS_TASK_SW( )和OSIntCtxSw( )了。



程序清单4将给出如何保存被切换任务的环境

程序清单<!--[if !vml]-->

<!--[endif]-->4
LDR R2,[SP,#20] ;得到 PC
LDR R12,[SP,#16] ;得到R12
MRS R0,CPSR ;将当前的状态保存到R0,这样就不管是SVC还
;是IRQ模式

MSR CPSR_c,#SYSMODE ;切换到系统模式
MOV R1,LR
STMFD SP!,{R1-R2} ;//保存PC,LR,R12-R4
STMFD SP!,{R4-R12}

MSR CPSR_c,R0 ;//返回先前的模式
LDMFD SP!,{R4-R7}
MSR R3,SPSR ;//被切换任务的CPSR
ADD SP,SP,#8 ;// 因为刚才PC和R12还没弹出栈

MSR CPSR_c,#SYSMODE
STMFD SP!,{R4-R7} ;//保存 PC,LR,R3-R0
STMFD SP!,{R3} ;//保存 CPSR,

LDR R1,=OSTCBCur ;//OSTCBCur->OSTCBStkPtr = Stack pointer
LDR R1,[R1]
STR SP,[R1]
以上的代码就将被挂起任务的上下文保存到了自己的堆栈中。下面的的工作就是将更高优先级的就绪任务从任务堆栈中复制出来执行。以下代码略。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: