μC/OS II针对TMS320C32的移植 03
![Rank: 8](images/default/star_level3.gif) ![Rank: 8](images/default/star_level3.gif)
- UID
- 872238
|
![](http://images.eccn.com/silabs/silicon_chip_980x60_202203.jpg)
μC/OS II针对TMS320C32的移植 03
这几个函数我们这里都处理为空函数,而且还可以通过在文件OS_CFG.H中设置OS_CPU_HOOKS_EN为0而不使用这些函数。我们主要来讨论前三个函数:
堆栈初始化函数OSTaskInit()
堆栈初始化函数OSTaskInit()是由任务创建函数OSTaskCreate()或OSTaskCreateExt()来调用,用来初始化任务堆栈。初始化后的堆栈保存着任务第一次执行时的上下文环境,它和中断后的堆栈神似!这个函数最关键的两个参数就是任务的起始地址void(* task)(void *pd)和任务使用堆栈的栈顶指针void *ptos;需要注意的是,任务第一次执行时的某些全局寄存器的值有特殊要求:1)状态寄存器ST的初始值必须保证中断全局使能位GIE为1,从而保证时钟节拍中断不会长时间被屏蔽,这里我选择初值为0x2000;2)页指针寄存器DP的初始值:如果你选择Small-Memory模式进行编译时,那么它的初始值应该和建立C环境时对DP的初始化值一样;否则就不要对这个DP寄存器进行任何保护处理,这样也可以的,不过这样的话,别的函数也就要做相应的改动了;如果选择了Large-Memory模式的话,那么这个值的初始化就可以不进行了,因为编译系统在编译时会自动插入更新DP的指令的;
中断任务级切换函数OSIntCtxSw()
mC/OS-II中,中断的产生可能会引起任务的切换,在中断服务程序的最后会调用OSIntExit()检查任务就绪状态。如果需要进行任务切换,将调用OSIntCtxSw()。所以OSIntCtxSw()又称为中断级的任务切换函数。需要注意的是,任何中断服务程序ISR前面都要像时钟节拍函数:OSTickISR()流程的第2步那样保存上下文环境。OSIntCtxSw()和OSCtxSw()的后半部分几乎相同,不同之处是:对当前任务的堆栈指针进行调整!其代码如下:
asm(" SUBI 5,SP ");
asm(" LDI @_OSTCBCur,AR0 ");
asm(" STI SP,*AR0 ");
这里我们把堆栈指针SP减去5,就是调整的结果。
下面我们来分析一下这个“5”是怎么得来的(我们针对时钟节拍中断ISR来进行说明):
任务调用函数OSIntExit()之前时,当前任务堆栈的情况如图1所示;调用OSIntExit函数后,当前任务堆栈的情况如图2所示,为什么会这样呢?我们可以看看OSIntExit()函数编译后的汇编文件就明白了,这个函数入口的地方有如下几条语句:
_OSIntExit:
push fp
ldiu sp,fp
push ar4
然后该函数又调用中断切换函数OSIntCtxSw(),这时当前任务堆栈的的情况如图3所示;OSIntCtxSw()编译后的汇编文件入口的地方有如下几条语句:
_OSIntCtxSw:
push fp
由此可见,当别的任务需要调度时,当前任务需要把自己的堆栈指针SP调整到调用OSIntExit()之前的值,从图上可以看出只要把当前任务的堆栈指针的值减去“5”便可。
多任务启动函数中调用的OSStartHighRdy()
这个函数只在多任务启动函数中调用一次,主要用来把多任务启动时优先级最高的就绪任务的上下文环境从堆栈恢复过来。mC/OS-II系统启动时至少创建了一个任务__空闲任务,实际应用当然还要创建别的用户任务。OSStart()首先从这些任务中查找出优先级最高的就绪任务,并把它的任务控制块的地址赋给OSTCBHighRdy,然后调用OSStartHighRdy()来运行OSTCBHighRdy指向的那个任务控制块所对应的任务。其流程是:1)该函数的返回地址压入堆栈,注意,这里提到的堆栈是mC/OS-II系统使用的堆栈,而与其他任何任务使用的堆栈没有任何关系,而且这个地址压不压栈意义已经不大,因为不可能再从返回这里返回回去;2)变量OSRunning赋值为True,标志多任务已经启动;3)从任务初始化过的堆栈中恢复上下文环境,代码和上面的雷同;4)执行RETI指令运行这个任务;
两点补充说明
编译器的编译选项
在移植过程中,除了要熟悉mC/OS-II内核原理和目标芯片之外,还要熟悉C编译器提供编译选项的使用。使用不当,会给移植带来大麻烦。上面在介绍堆栈初始化函数也提及到有关DP寄存器的处理问题上,就是涉及到编译选项的选择:是选用Small-Memory还是Large-Memory模式,这个取决于编译选项-mb的打开和关闭。
中断函数的编写
在分析函数OSIntCtxSw()时,我们提到分析的结果是针对时钟中断服务函数得出的。大家应该注意到这个函数是汇编语言写的,那么我们写别的中断处理程序时,如果用C语言来写的话,要不要注意些什么呢?回答是肯定的。因为编译器在处理C语言写的中断服务函数时会作出一些特殊的处理:在这个ISR入口处插入压栈指令,把部分全局寄存器的值压入堆栈,具体哪些,因函数的不同而有所不同。这就干扰了我们保存上下文环境的工作,如果不进行处理,任务调度时会出现难以想象的问题。解决这个问题的办法就是要让C编译器认为这个中断服务函数是一般的函数,那么它就不会在函数入口处插入一系列的压栈指令。C编译器Code Composer规定,凡是函数名为c_intnm(其中n、m是小于9正整数)的函数都是中断函数,在编译这些函数时都作出特殊的处理,为此,我们避免为中断处理函数取这样的名字就可以了。
但是,编译在处理一般函数时还是要做一定的处理的,譬如:
push fp
ldiu sp,fp
push ar4
为此,我们仔细分析编译器编译生成的汇编代码,并对堆栈作相应的调整。然后在函数结束的地方嵌入asm(" RETI");语句结束。这样中断程序就可以正确执行。
它的框架如下:
void Int0ISR(void)
{
asm(" SUBI N,SP") /*这个N的值要看具体的程序来定,一般等于1或2*/
|
|
|
|
|
|