UCOSII V2.86在LPC2378上的移植
( 编译器ADS1.2 )
忙了几天,终于移植成功了。
移植UCOS需要准备一些东西,首先当然是OS源代码,再就是选定编译器,需要弄清一下几点:
1、UCOS需要移植的内容
2、编译器的特性(在这里主要是ATPCS)
3、处理器的特性(这里主要注意ARM7的模式)
以下先简要说一下这几点,为后面的程序说明做准备
首先说说第一点,UCOS需要移植的内容。
1、数据类型的定义,这个根据平台来处理。具体代码如下:
2、任务切换函数,就是两个任务环境之间的切换。
3、处理临界代码前要关闭中断,之后再打开,这个实现的方法与平台关系很大。
4、栈的初始化,栈中保存的是任务的运行环境,不同的处理器中的寄存器自然是不一样的。
5、还有一个和任务切换差不多的,这个函数只运行一次,就是将第一个运行的任务的运行环境复制到CPU中。
再说说编译器的特性,ATPCS规定(想要详细一些的可以看看ADS的帮助文档):R0-R3用来传递参数,R4-R12用来处理局部变量。这个在处理汇编和C的接口时十分重要。
最后是CPU的特性。ARM7有7种模式,但是有些模式对于UCOS在ARM7上的运行用处不大,经过综合考虑,可以只使用四种模式:
SVC:运行OS代码和用户代码
SYS:用于IRQ的嵌套处理,(暂时还未实现)
IRQ:处理中断,处于OS的控制范围内
FIQ: 独立于操作系统,处理快速中断
有了上面的说明,下面可以具体讨论了,因为网页中显示代码及注释不方便,所以把它们放在了附件中。
标题中前面是内容,括号内是涉及到的需要移植的函数
1、任务栈(OSTaskStkInit)
任务栈相当于CPU的一个备份,可以这样认为,当运行一个任务时,将栈中的数据复制到CPU中,由于某种原因停止运行一个任务时将CPU复制到任务栈中。ARM7的CPU需要保存的寄存器如下:
PC | 任务断点 |
LR | |
R12 | |
R11 | |
R10 | |
R9 | |
R8 | |
R7 | |
R6 | |
R5 | |
R4 | |
R3 | |
R2 | |
R1 | |
R0 | |
CPSR |
还有一个栈指针SP,这个保存在任务控制块中。
2、任务开始(OSStartHighRdy)
(1)这个函数很简单,有如下几步
(2)通知OS已经运行
(3)调用OSTaskSwHook,是一个钩子函数
(4)将就绪的优先级最高的任务的数据复制到CPU中,这个过程有个顺序,一般是先将SP恢复,再将CPSR复制到SPSR中,然后使用LDMFD SP!, {R0-R12, LR, PC}^ 一次性复制所有。
3、任务切换(OS_TASK_SW OSIntCtxSw)
这里涉及到两个函数,为什么会有两个呢,这个与任务切换时CPU所处的状态有关。当一个任务正在执行时,需要切换任务(此时处在SVC模式),之需要简单地保存前一个任务的环境,并恢复后一个任务的环境至CPU即可,使用的是OS_TASK_SW()。但是由于中断导致的任务切换就有些麻烦(自己在这块儿耽误了很久),此时需要保存的任务环境一部分保存在IRQ独有的寄存器中( 其中包括任务的断点,任务的CPSR ),一部分保存在SVC的寄存器中,还有一部分保存在IRQ的栈中,此时调用OSIntCtxSw()。OSIntCtxSw()和IRQ的处理方式有关。
OS_TASK_SW()要做的事情是
(1)保存任务的断点,此时还在在LR中
(2)保存R0-R12
(3)保存CPSR
(4)保存SP
(5)调用OSTaskSwHook
(6)OSPrioHighRdy->OSPrioCur
(7)OSTCBHighRdy->OSTCBCur
(8)恢复SP
(9)恢复CPRS至SPRS
(10)使用LDMFD SP!, {R0-R12, LR, PC}^恢复所有
OSIntCtxSw()
此函数被调用前,需要准备一些东西,也就是我们要自己用汇编写IRQ的入口函数IRQ_Handler,此时IRQ的服务程序就不用加__irq了。
IRQ_Handler要做的事情是
(1)LR-4,获得任务的断点,并保存环境 R0-R3, R12, LR
(2)调用OSIntEnter(),通知OS进入中断
(3)执行中断服务程序,用C编写,是普通函数
(4)调用OSIntExit(),判断执行完中断服务程序后是否需要切换任务。这里需要注意一下:OSIntExit()中本来还要调用OSIntCtxSw()来执行切换的,这里改写了OSIntExit()这个函数,将里面的调用OSIntCtxSw()改为OSIntCtxSwFlag = 1。OSIntCtxSwFlag是自己定义的一个标志,置1表示需要切换任务。
(5)判断OSIntCtxSwFlag 是否为1
(6)如果OSIntCtxSwFlag = 1 ,跳转至OSIntCtxSw()
(7)如果OSIntCtxSwFlag = 0 ,接着往下执行
(8)恢复环境R0-R3, R12, LR SPSR至CPSR,回到SVC模式
OSIntCtxSw()要做的事情是
(1)OSIntCtxSwFlag清零
(2)保存任务环境,这个有点复杂,具体可以看一下程序
(3)在执行(2)的同时还用调整IRQ的SP,使得看起来就像没用发生过中断
(4)调用OSTaskSwHook
(5)OSPrioHighRdy->OSPrioCur
(6)OSTCBHighRdy->OSTCBCur
(7)恢复SP
(8)恢复CPRS至SPRS
(9)使用LDMFD SP!, {R0-R12, LR, PC}^恢复所有
4、开、关中断
UCOS中可以使用3中方式。第一种简单,但是会产生问题,一般不用;第二种需要执行CPSR的进栈出栈操作,这样同样会用潜在的问题,任务切换后,PC就改变了,转向了新的任务,后面的代码就可能不会执行,假如不知道任务切换前关中断的次数,那么就不知道压栈的次数,那么任务切换函数就不好写了。第三种比较适合ARM7,关中断时先将CPSR保存在局部变量中,再关中断;开中断则只是简单的将局部变量恢复至CPSR即可,可以和好地解决开关中断的嵌套。
这个代码比较少,可以贴在这里:
#define OS_ENTER_CRITICAL() { cpu_sr = OSSaveCPSR(); }
#define OS_EXIT_CRITICAL() { OSRestoreCPSR(cpu_sr); }
OSSaveCPSR
MRS R0, CPSR
ORR R1, R0, #NoInt
MSR CPSR_c, R1
MOV PC, LR
OSRestoreCPSR
MSR CPSR_c, R0
MOV PC, LR
参数使用R0传递
好了,就先写到这里了,基本问题解决了,剩下的任务就是处理一下中断的嵌套,再看看有没有什么BUG,移植代码就在附件里。
https://static.assets-stash.eet-china.com/album/old-resources/2010/5/9/22b33c66-b004-4efc-9fad-33e366a7c35c.rar
用户251808 2011-4-1 16:57