移植COSII中英文翻译资料.docx
《移植COSII中英文翻译资料.docx》由会员分享,可在线阅读,更多相关《移植COSII中英文翻译资料.docx(17页珍藏版)》请在冰豆网上搜索。
移植COSII中英文翻译资料
中文4200字
移植μC/OS-II
这篇文章介绍如何将μC/OS-II移植到不同的处理器上。
所谓移植,就是使一个实时内核能在其他的微处理器上运行。
为了方便移植,大部分μC/OS-II的代码是用C语言编写的:
但是,仍需要用C语言和汇编语言编写一些与处理器硬件相关的代码,这是因为μC/OS-II在读/写处理器寄存器时,只能通过汇编语言来实现。
由于μC/OS-II在设计前就已经考虑了可移植性,所以它的移植相对来说是比较容易的。
要使μC/OS-II正常运行,处理器必须满足以下要求:
(1)处理器的C编译器能产生可重入型代码:
(2)处理器支持中断,并且能产生定时中断(通常为10-100Hz);
(3)用C语言就可以开关中断;
(4)处理器能支持一定数量的数据存储器的硬件堆栈;
(5)处理器有将堆栈指针以及其他CPU寄存器的内容读出、并存储到堆栈或内存中去的指令;
如果已经了解处理器和C编译器的技术细节,那么移植的工作是非常容易的,测试一个像μC/OS-II这样的实时内核其实并不复杂,甚至可以在没有任何应用程序下测试,换句话说,就是让内核自己测试自己。
有两种原因要这样做:
第一,避免使本来就复杂的事情变的更加复杂化;第二,如果出现问题可以知道问题出在内核代码中,而不是应用程序中。
刚开始时,可以运行一些简单的任务和时钟节拍中断程序。
一旦多任务调度成功运行了,再添加应用程序的任务就更加容易了。
1.1开发工具
如前所述移植μC/OS-II需要标准的C交叉编译器,并且是针对所使用的CPU的;因为它是一个可剥夺的内核,只能通过C编译器来产生可重入型代码。
同时C编译器还要支持汇编语言程序。
绝大部分为嵌入式系统设计的C编译器都包括汇编器、链接器、定位器。
链接器用来将不同的模块(编译过或汇编过的文件)链接成目标文件;定位器则允许将代码和数据放置在目标处理器的指定内存空间中。
所用的C编译器还提供另一种机制,能在C编译器中开中断和关中断。
一些编译器允许在C语言源代码中直接插入汇编语句,这就使得插入相应的处理器中的指令开中断和关中断变得容易了。
1.2文件
(1)INCLUDES.H文件
INCLUDES.H是一个头文件,它出现在每个.C文件的第一行,如下:
#include“includes.h”
INCLUDES.H文件使得工程项目中的每个.C文件无需分别考虑它实际上需要哪些头文件。
使用INCLUDES.H文件的唯一缺点就是它可能包含一些与当前要编译的.C文件实际上不相干的头文件。
这意味着每个文件的编译时间都会增加;但由于他增加了代码的可移植性,所以还是决定使用这种方法。
也可以通过重新编译INCLUDES.H文件来增加头文件,但是头文件必须添加在文件列表的最后。
(2)OS_CPU.H文件
OS_CPU.H包含了用#define语句定义的、与处理器相关的常数、宏以及类型。
OS_CPU.H文件的大体结构如程序清单T1所列:
#ifdefOS_CPU_GLOBALS
#defineOS_CPU_EXT
#else
#defineOS_CPU_EXTextern
#endif
typedefunsignedcharBOOLEAN;
typedefunsignedcharINT8U;/*无符号8位整数*/
typedefsignedcharINT8S;/*有符号8位整数*/
typedefunsignedintINT16U;/*无符号16位整数*/
typedefsignedintINT16S;/*有符号16位整数*/
typedefunsignedlongINT32U;/*无符号32位整数*/
typedefsignedlongINT32S;/*有符号32位整数*/
typedeffloatFP32;/*单精度浮点数*/
typedefdoubleFP64;/*双精度浮点数*/
typedefunsignedintOS_STK;/*堆栈入口宽度为16位*/
#defineOS_ENTER_CRITICAL()?
?
?
/*关中断*/
#defineOS_EXIT_CRITICAL()?
?
?
/*开中断s*/
#defineOS_STK_GROWTH1/*定义堆栈方向:
1=向下递减,0=向上递增*/
#defineOS_TASK_SW()?
?
?
程序清单T1
①OS_CPU.H,与编译器相关的数据类型
因为不同的微处理器有不同的字长,所以μC/OS-II的移植包括了一系列的数据类型的定义,而确保其可移植性。
尤其是,μC/OS-II代码从不使用C语言中的short,int及long等数据类型,因为它们是与编译器相关的,是不可移植的。
相反,定义的数据结构等既是可移植的,又很直观。
举例来说,INT16U数据类型总是代表16位的无符号整型数。
这样μC/OS-II就可以断定,声明为该数据类型变量的范围都是0~65535。
将μC/OS-II移植到32位的处理器上,就意味着INT16U实际被声明为无符号短整型数,而不是无符号整数,但是,μC/OS-II处理的仍然是INT16U。
你必须将任务堆栈的数据类型告诉μC/OS-II。
这是通过为OS_STK声明恰当的C数据类型来实现的。
如果处理器的堆栈是32位的,那么就应该将OS_STK声明:
为:
unsignedint,所有的任务堆栈都必须声明使用OS_STK作为它的数据类型。
用户需要做的只是查阅编译器文档,找出对应于μC/OS-II的标准的C的相应数据类型。
②OS_CPU.H,OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL()
像其它的实时内核一样,μC/OS-II需要先关中断再处理临界段代码,并且在处理完毕后再重新开中段。
这就能够保证临界段代码免受多任务或中断服务子程序的破坏。
通常每个处理器都会提供一定的汇编指令来开关中断,C编译器必须有一定的机制直接从C语言中执行这些操作。
有些编译器允许在C源代码中直接加入汇编语句,这就使得插入处理器指令来开关中断变的容易,有些其它的编译器提供语言扩展功能,可以直接从C语言中开关中断。
为了隐藏编译器厂商提供的不同实现方法,以增加可移植性,μC/OS-II定义了2个宏,用来关开中断:
OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL(),如T1:
它们都是成对出现的,分别加在临界段代码的前面和后面;
µC/OS-IIServiceFunction
{
OS_ENTER_CRITICAL();
/*临界段代码*/
OS_EXIT_CRITICAL();
}
T1
方法一:
实现OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL()这两个宏的这种方法是最简单的方法,在中调用处理器指令关中断,以及在中调用相应处理器指令开中断;但是这个过程还存在小小的问题:
如果在禁止中断的情况下调用μC/OS-II函数,那
么从μC/OS-II函数返回时,中断可能会变成允许的了。
而实际上如果调用μC/OS-II之前中断是关掉的希望从μC/OS-II函数返回时,希望中断还是关掉的。
在这种情况下,仅靠这种方法是不适宜的。
方法二:
执行OS_ENTER_CRITICAL()时,先将中断状态保存到堆栈中,然后关中断;而当执行OS_EXIT_CRITICAL()时,再从堆栈中恢复原来的中断开关状态。
如果用这种方法,那么不管用户是在中断禁止,还是中断允许的情况下调用μC/OS-II的功能函数,调用后都不会改变中断状态。
应用程序可以调用OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL(),以保护临界段代码;但是,在使用这种方法时需特别小心,因为如果在调用像OSTimeDly()之类的功能函数之前就关掉了中断,应用程序就会崩溃。
发生这种情况的原因是,任务被挂起,知道延迟时间到,而中断是关掉的。
OSTimeDly()实际上是依靠时钟节拍实现中断的,而因为中断是关掉的,程序不可能获得时钟节拍中断。
明显的,所有的PEND调用都会涉及到这个问题,需十分小心。
一个通用的办法是,应该在中断允许的情况下调用μC/OS-II的系统功能函数。
③OS_CPU.H,OS_STK_GROWTH
绝大多数微处理器和微控制器的堆栈都是从上往下递减的,但是也有某些处理器使用的是相反的方式,μC/OS-II被设计成对这两种情况都可以处理,只要再用配置常数OS_STK_GROWTH指定堆栈的方向就可以了:
置OS_STK_GROWTH为0,表示堆栈从下(低地址)往上(高地址)递增:
置OS_STK_GROWTH为1,表示堆栈从上(高地址)往下(低地址)递减。
④OS_CPU.H,OS_TASK_SW()
OS_TASK_SW()是一个宏,是在μC/OS-II从低优先级任务切换到高优先级任务时须用到的。
OS_TASK_SW()总是在任务级代码中被调用。
另一个函数OSIntExit()用在中断服务子程序ISR中。
任务切换只是简单地将处理器的寄存器保存到将被挂起的任务的堆栈中,并且从堆栈中恢复要运行的更高优先级的任务。
在μC/OS-II中,处于就绪态任务的堆栈结构看起来就像刚刚发生过中断一样,所有的寄存器都保存在堆栈中。
换句话说,μC/OS-II要运行处于就绪态的任务必须要做的事是,从任务堆栈中恢复所有的寄存器,并且执行中断返回指令。
为了任务调度,可以通过执行OS_TASK_SW()模仿中断的产生。
绝大多数处理器会提供软中断或指令陷阱来完成这项功能。
中断服务子程序或指令陷阱处理函数(也叫做异常处理函数)的中断向量地址必须指向汇编语言函数OSCtxSw()。
例如,在Intel或者AMD80X86处理器上可以使用INT指令,但是中断向量必须指向OSCtxSw()。
有些处理器如Z80,并不提供软中断机制。
在这种情况下需要想办法将堆栈结构设置成与软中断发生后的堆栈结构一样。
在OS_TASK_SW()函数中调
用OSCtxSw(),而不是将某个中断向量指向OSCtxSw()。
实际上μC/OS-II已经被移植到了Z80处理器上,μC/OS-II也同样是可以的。
(3)OS_CPU_A.ASM
μC/OS-II的移植实例要求用户编写4个简单的汇编程序:
OSStartHighRdy()
OSCtxSw()
OSIntCtxSw()
OSTickISR()
如果编译器支持插入行汇编代码,就可以将所有与处理器相关的代码放到OS_CPU.C文件中,而不必再有单独的汇编语言文件。
1OS_CPU_A.ASM,OSStartHighRdy()
OSStart()函数调用OSStartHighRdy()来使就绪态任务中优先级最高的任务开始运行,在调用它之前,要已经建立了至少一个应用任务。
OSStartHighRdy()假设OSTCBHighRdy指向最高优先级任务的任务控制块。
就像先前提到的,在μC/OS-II中处于就绪态任务的堆栈结构看起来就像刚发生过中断一样,所有的寄存器都保存在堆栈中。
要想运行最高优先级任务,需将所有处理器按顺序从任务堆栈中恢复出来,并且执行中断返回指令。
为简单起见,堆栈指针总是存储字任务控制块的开头。
换句话说,也就是需要恢复的任务堆栈指针总是存储在任务控制块的偏移地址为0的位置。
注意到OSStartHighRdy()必须调用OSTaskSwHook()函数,但OSStartHighRdy()函数只是做了任务切换工作的一半——只是完成了高优先级任务寄存器的恢复,而并没有保存当前任务的寄存器。
OSTaskSwHook()函数必须检查OSRunning位,以确定OSTaskSwHook()函数是被OSStartHighRdy()调用的(OSRunning是FALSE),还是在正常的任务切换之中(OSRunning是TRUE)被调用的。
2OS_CPU_A.ASM,OSCtxSw()
任务级的切换是通过执行软中段指令,或者依据处理器的不同,执行TRAP指令来实现的。
中断服务子程序,陷阱或异常处理的向量地址必须指向OSCtxSw()。
如果当前任务调用μC/OS-II提供的功能函数,并使得更高优先级任务进入了就绪态,在系统服务调用的最后,μC/OS-II会调用OSSched(),并由此推断出当前任务不是需要运行的最重要的任务了,OSSched()先将最高优先级任务的地址装载到OSTCBHighRdy,再通过调用OS_TASK_SW()执行软中断或陷阱指令。
注意变量OSTCBCur已经包含了指向当前任务控制块的指针。
软中断指令会强制将处理器的一些寄存器保存到当前任务的堆栈中并使处理器执行OSCtxSw()。
OSCtxSw()示意性代码如T2,这些代码必须用汇编语言编写,因为用户不能直接在C语言中访问CPU寄存器。
voidOSCtxSw(void)
{
保存处理器寄存器;
在当前任务的任务控制块中保存当前任务的堆栈指针:
OSTCBCur->OSTCBStkPtr=Stackpointer;
CalluserdefinableOSTaskSwHook();
OSTCBCur=OSTCBHighRdy;
OSPrioCur=OSPrioHighRdy;
得到将要重新开始运行的任务的堆栈指针:
Stackpointer=OSTCBHighRdy->OSTCBStkPtr;
从新任务的任务堆栈中恢复处理器所有寄存器的值;
执行中断返回指令;
}
T2OSCtxSw()的示意性代码
3OS_CPU_A.ASM,OSIntCtxSw()
OSIntExit()通过调用OSIntCtxSw(),在ISR中执行任务切换功能。
因为OSIntCtxSw()是在ISR中被调用的,所以假定所有的处理器都被正确地保存到了被中断任务的堆栈之中。
OSIntCtxSw()的示意性代码如T3所示,这些代码必须用汇编语言编写,因为在C语言中不能直接访问CPU寄存器。
如果编译器支持插入汇编语言代码,就可以将OSIntCtxSw()的代码放在OS_CPU_C.C文件中,而不放在OS_CPU_A.ASM文件中。
实际上如果需要,可以跳转到OSCtxSw()中相同的代码,以减少代码量。
voidOSIntCtxSw(void)
{
调用用户定义的OSTaskSwHook();
OSTCBCur=OSTCBHighRdy;
OSPrioCur=OSPrioHighRdy;
得到将要重新执行任务的堆栈指针:
Stackpointer=OSTCBHighRdy->OSTCBStkPtr;
从新任务堆栈中恢复所有处理器寄存器;
执行中断返回指令;
}
T3OSIntCtxSw()的示意性代码
④OS_CPU_A.ASM,OSTickISR()
μC/OS-II要求用户提供一个周期性的时钟源,来实现时间的延迟和超时功能。
时钟节拍应该每秒发生10~100次/秒。
为了完成该任务,可以使用硬件定时器,也可以从交流点中获得50/60Hz的时钟频率。
必须在开始多任务后,即调用OSStart()后,启动时钟节拍中断;然后,可以在OSStart()运行后,μC/OS-II启动运行的第一个任务中初始化节拍中断。
通常容易犯的错误是,在调用OSInit()和OSStart()之间打开了时钟节拍。
(如程序清单T4所列)。
voidmain(void)
{
OSInit();/*初始化μC/OS-II*/
/*应用程序初始化代码*/
/*调用OSTaskCreate()建立至少1个任务*/
开时钟节拍中断;/*千万不要在这里开中断*/
OSStart();/*开始多任务*/
}
T4在不正确的位置启动节拍中断
PortingµC/OS-II
ThisarticledescribesingeneraltermswhatneedstobedoneinordertoadaptµC/OS-IItodifferentprocessors.Adaptingareal-timekerneltoamicroprocessororamicrocontrolleriscalledaport.MostofµC/OS-IIiswritteninCforportability,however,itisstillnecessarytowritesomeprocessorspecificcodeinCandassemblylanguage.Specifically,µC/OS-IImanipulatesprocessorregisterswhichcanonlybedonethroughassemblylanguage.PortingµC/OS-IItodifferentprocessorsisrelativelyeasybecauseµC/OS-IIwasdesignedtobeportable.
AprocessorcanrunµC/OS-IIifitsatisfiesthefollowinggeneralrequirements:
(1)YoumusthaveaCcompilerfortheprocessorandtheCcompilermustbeabletoproducereentrantcode.
(2)YoumustbeabletodisableandenableinterruptsfromC.
(3)Theprocessormustsupportinterruptsandyouneedtoprovideaninterruptthatoccursatregularintervals(typicallybetween10to100Hz).
(4)Theprocessormustsupportahardwarestack,andtheprocessormustbeabletostoreafairamountofdataonthestack(possiblymanyKbytes).
(5)TheprocessormusthaveinstructionstoloadandstorethestackpointerandotherCPUregisterseitheronthestackorinmemory.
PortingµC/OS-IIisactuallyquitestraightforwardonceyouunderstandthesubtletiesofthetargetprocessorandtheCcompileryouwillbeusing.Dependingontheprocessor,aportcanconsistofwritingorchangingbetween50and300linesofcode!
PortingµC/OS-IIcouldtakeyouanywherebetweenafewhourstoaboutaweek.
OnceyouhaveaportofµC/OS-IIforyourprocessor,youwillneedtoverifyitsoperation.Testingamultitaskingreal-timekernelsuchasµC/OS-IIisnotascomplicatedasyoumaythink.Youshouldtestyourportwithoutapplicationcode.Inotherwords,testtheoperationsofthekernelbyitself.Therearetworeasonstodothis.First,youdon’twanttocomplicatethingsanymorethantheyneedtobe.Second,ifsomethingdoesn’twork,youknowthattheproblemliesintheportasopposedtoyourapplication.Startwithacoupleofsimpletasksandonlythetickerinterruptserviceroutine.Onceyougetmultitaskinggoing,it’squitesimpletoaddyourapplicationtasks.
1.1DevelopmentTools
Aspreviouslystated,youneedaCcompilerfortheprocessoryouintendtouseinordertoportµC/OS-II.BecauseµC/OS-IIisapreemptivekernel,youshouldonlyuseaCcompilerthatgeneratesreentrantcode.YourCcompilermustalsobeabletosupportassemblylanguageprogramming.MostCcompilerdesignedforembeddedsystemswill,infact,alsoincludeanassembler,alinker,andalocator.Thelinkerisusedtocombineobjectfiles(compiledandassembledfiles)fromdifferentmoduleswhilethelocatorwillallowyoutoplacethecodeanddataanywhereinthememorymapofthetargetprocessor.YourCcompilermustalsoprovideamechanismtodisableandenableinterruptsfromC.Somecompilerswillallowyoutoinsertin-lineassemblylanguagestatementsinyourCsourcecode.Thismakesitquiteeasytoinsert