下载

2下载券

加入VIP
  • 专属下载特权
  • 现金文档折扣购买
  • VIP免费专区
  • 千万文档免费下载

上传资料

关闭

关闭

关闭

封号提示

内容

首页 第9章

第9章.doc

第9章

linwhwylb
2018-09-05 0人阅读 举报 0 0 0 暂无简介

简介:本文档为《第9章doc》,可适用于IT/计算机领域

µCOSII在x上的移植本章将介绍如何将µCOSII移植到Intelx系列CPU上本章所介绍的移植和代码都是针对x的实模式的且编译器在大模式下编译和连接。本章的内容同样适用于下述CPU:PentiumPentiumII实际上将要介绍的移植过程适用于所有与x兼容的CPU如AMDCyrixNEC(V系列)等等。以Intel的为例只是一种更典型的情况。xCPU每年的产量有数百万大部分用于个人计算机但用于嵌入式系统的数量也在不断增加。最快的处理器(Pentium系列)将在年达到G的工作频率。大部分支持x(实模式)的C编译器都提供了不同的内存使用模式每一种都有不同的内存组织方式适用于不同规模的应用程序。在大模式下应用程序和数据最大寻址空间为Mb程序指针为位。下一节将介绍为什么位指针只用到了其中的位来寻址(Mb)。本章所介绍的内容也适用于处理器但由于没有PUSHA指令移植的时候要用几条PUSH指令来代替。图F显示了工作在实模式下的x处理器的编程模式。所有的寄存器都是位在任务切换时需要保存寄存器内容。图Fx实模式内部寄存器图x提供了一种特殊的机制使得用位寄存器可以寻址Mb地址空间这就是存储器分段的方法。内存的物理地址用段地址寄存器和偏移量寄存器共同表示。计算方法是:段地址寄存器的内容左移位(乘以)再加上偏移量寄存器(其他个寄存器中的一个AXBPSPSIDI或IP)的内容产生可寻址Mb的位物理地址。图F表明了寄存器是如何组合的。段寄存器可以指向一个内存块称为一个段。一个位的段寄存器可以表示,个不同的段因此可以寻址,,字节。由于偏移量寄存器也是位的所以单个段不能超过K。实际操作中应用程序是由许多小于K的段组成的。图F使用段寄存器和偏移量寄存器寻址代码段寄存器(CS)指向当前程序运行的代码段起始堆栈段寄存器(SS)指向程序堆栈段的起始数据段寄存器指向程序数据区的起始附加段寄存器(ES)指向一个附加数据存储区。每次CPU寻址的时候段寄存器中的某一个会被自动选用加上偏移量寄存器的内容作为物理地址。文献中会经常发现用段地址偏移量表示地址的方法例如:FF表示物理地址xFF。开发工具笔者采用的是BorlandCCV和BorlandTurboAssembler汇编器完成程序的移植和测试它可以产生可重入的代码同时支持在C程序中嵌入汇编语句。编译完成后程序可在PC机上运行。本书代码的测试是在一台Pe断。此时任务将处于延时挂起状态等待时钟中断但此时时钟中断是禁止的!则系统可能会崩溃。很明显所有的PEND调用都会涉及到这个问题必须十分小心。所以建议用户调用µCOSⅡ的系统函数之前打开中断。堆栈增长方向x处理器的堆栈是由高地址向低地址方向增长的所以常量OSSTKGROWTH必须设置为程序清单L()。OSTASKSW()在µCOSII中,就绪任务的堆栈初始化应该模拟一次中断发生后的样子堆栈中应该按进栈次序设置好各个寄存器的内容。OSTASKSW()函数模拟一次中断过程在中断返回的时候进行任务切换。x提供了个软中断源可供选用中断服务程序(ISR)(也称为例外处理过程)的入口点必须指向汇编函数OSCtxSw()(请参看文件OSCPUAASM)。由于笔者是在PC机上测试代码的本章的代码用到了中断号(x)因为此中断号是提供给用户使用的程序清单L()(PC和操作系统会占用一部分中断资源译者注)类似的用户可用中断号还有xB到xB,xD到x,或者x到xF。如果用户用的不是PC而是其他嵌入式系统如处理器用户可能有更多的中断资源可供选用。时钟节拍的发生频率实时系统中时钟节拍的发生频率应该设置为到Hz。通常(但不是必须的)为了方便计算设为整数。不幸的是在PC中系统缺省的时钟节拍频率是Hz这对于我们的计算和设置都不方便。本章中笔者将更改PC的时钟节拍频率到Hz(间隔ms)。一方面Hz近似Hz的倍可以经过次延时再调用DOS中断另一方面在DOS中有些操作要求时钟间隔为ms我们设定的间隔ms也可以满足要求。如果您的PC机处理器是时钟节拍最快也只能到Hz而如果是PentiumII处理器则达到Hz以上没有问题。在文件OSCPUH的末尾声明了一个位变量OSTickDOSCtr将保存时钟节拍发生的次数每发生次调用DOS的时钟节拍函数一次从而实现与DOS时钟的同步。OSTickDOSCtr是专门为PC环境而声明的如果在其他非PC的系统中运行µCOSII就不用这种同步方法直接设定时钟节拍发生频率就行了。OSCPUAASMµCOSII的移植需要用户改写OSCPUAASM中的四个函数:OSStartHighRdy()OSCtxSw()OSIntCtxSw()OSTickISR()OSStartHighRdy()该函数由SStart()函数调用功能是运行优先级最高的就绪任务在调用OSStart()之前用户必须先调用OSInit()并且已经至少创建了一个任务(请参考OSTaskCreate()和OSTaskCreateExt()函数)。OSStartHighRdy()默认指针OSTCBHighRdy指向优先级最高就绪任务的任务控制块(OSTCB)(在这之前OSTCBHighRdy已由OSStart()设置好了)。图F给出了由函数OSTaskCreate()或OSTaskCreateExt()创建的任务的堆栈结构。很明显OSTCBHighRdy>OSTCBStkPtr指向的是任务堆栈的顶端。函数OSStartHighRdy()的代码见程序清单L。图F任务创立时的x堆栈结构为了启动任务OSStartHighRdy()从任务控制块(OSTCB)程序清单L()中找到指向堆栈的指针然后运行POPDS程序清单L(),POPES程序清单L(),POPA程序清单L(),和IRET程序清单L()指令。此处笔者将任务堆栈指针保存在任务控制块的开头这样使得堆栈指针的存取在汇编语言中更容易操作。当执行了IRET指令后CPU会从(SS:SP)指向的堆栈中恢复各个寄存器的值并执行中断前的指令。SS:SP指向传递给任务的参数pdata。程序清单LOSStartHighRdy()OSStartHighRdyPROCFARMOVAX,SEGOSTCBHighRdy载入DSMOVDS,AXLESBX,DWORDPTRDS:OSTCBHighRdySS:SP=OSTCBHighRdy>OSTCBStkPtr()MOVSS,ES:BXMOVSP,ES:BXPOPDS恢复任务环境()POPES()POPA()IRET运行任务()OSStartHighRdyENDPOSCtxSw()OSCtxSw()是一个任务级的任务切换函数(在任务中调用区别于在中断程序中调用的OSIntCtxSw())。在x系统上它通过执行一条软中断的指令来实现任务切换。软中断向量指向OSCtxSw()。在µCOSII中如果任务调用了某个函数而该函数的执行结果可能造成系统任务重新调度(例如试图唤醒了一个优先级更高的任务)则在函数的末尾会调用OSSched(),如果OSSched()判断需要进行任务调度会找到该任务控制块OSTCB的地址并将该地址拷贝到OSTCBHighRdy然后通过宏OSTASKSW()执行软中断进行任务切换。注意到在此过程中变量OSTCBCur始终包含一个指向当前运行任务OSTCB的指针。程序清单L为OSCtxSw()的代码。图F是任务被挂起或被唤醒时的堆栈结构。在x处理器上任务调用OSTASKSW()执行软中断指令后图F程序清单L()先向堆栈中压入返回地址(段地址和偏移量)然后是状态字寄存器SW。紧接着用PUSHA图F程序清单L(),PUSHES图F程序清单L(),和PUSHDS图F程序清单L()保存任务运行环境。最后用OSCtxSw()在任务OSTCB中保存SS和SP寄存器。任务环境保存完后将调用用户定义的对外接口函数OSTaskSwHook()程序清单L()。请注意此时OSTCBCur指向当前任务OSTCBOSTCBHighRdy指向新任务的OSTCB。在OSTaskSwHook()中用户可以访问这两个任务的OSTCB。如果不使用对外接口函数请在头文件中把相应的开关选项关闭加快任务切换的速度。程序清单LOSCtxSw()OSCtxSwPROCFAR()PUSHA保存当前任务环境()PUSHES()PUSHDS()MOVAX,SEGOSTCBCur载入DSMOVDS,AXLESBX,DWORDPTRDS:OSTCBCurOSTCBCur>OSTCBStkPtr=SS:S()MOVES:BX,SSMOVES:BX,SPCALLFARPTROSTaskSwHook()MOVAX,WORDPTRDS:OSTCBHighRdyOSTCBCur=OSTCBHighRdy()MOVDX,WORDPTRDS:OSTCBHighRdyMOVWORDPTRDS:OSTCBCur,AXMOVWORDPTRDS:OSTCBCur,DXMOVAL,BYTEPTRDS:OSPrioHighRdyOSPrioCur=OSPrioHighRdy()MOVBYTEPTRDS:OSPrioCur,ALLESBX,DWORDPTRDS:OSTCBHighRdySS:SP=OSTCBHighRdy>OSTCBStkPtr()MOVSS,ES:BXMOVSP,ES:BXPOPDS载入新任务的CPU环境()POPES()POPA()IRET返回新任务()OSCtxSwENDP从对外接口函数OSTaskSwHook()返回后由于任务的更替变量OSTCBHighRdy被拷贝到OSTCBCur中程序清单L()同样OSPrioHighRdy被拷贝到OSPrioCur中程序清单L()。OSCtxSw()将载入新任务的CPU环境首先从新任务OSTCB中取出SS和SP寄存器的值图F()程序清单L()然后运行POPDS图F()程序清单L(),POPES图F()程序清单L(),POPA图F()程序清单L()取出其他寄存器的值,最后用中断返回指令IRET图F()L()完成任务切换。需要注意的是在运行OSCtxSw()和OSTaskSwHook()函数期间中断是禁止的。OSIntCtxSw()在µCOSII中由于中断的产生可能会引起任务切换在中断服务程序的最后会调用OSIntExit()函数检查任务就绪状态如果需要进行任务切换将调用OSIntCtxSw()。所以OSIntCtxSw()又称为中断级的任务切换函数。由于在调用OSIntCtxSw()之前已经发生了中断OSIntCtxSw()将默认CPU寄存器已经保存在被中断任务的堆栈中了。图F任务级任务切换时的x堆栈结构程序清单L给出的代码大部分与OSCtxSw()的代码相同不同之处是第一由于中断已经发生此处不需要再保存CPU寄存器(没有PUSHA,PUSHES,或PUSHDS)第二OSIntCtxSw()需要调整堆栈指针去掉堆栈中一些不需要的内容以使堆栈中只包含任务的运行环境。图F可以帮助读者理解这一过程。程序清单LOSIntCtxSw()OSIntCtxSwPROCFARIgnorecallstoOSIntExitandOSIntCtxSwADDSP,(UncommentifOSCRITICALMETHODis,seeOSCPUH)()ADDSP,(UncommentifOSCRITICALMETHODis,seeOSCPUH)MOVAX,SEGOSTCBCur载入DSMOVDS,AXLESBX,DWORDPTRDS:OSTCBCurOSTCBCur>OSTCBStkPtr=SS:SP()MOVES:BX,SSMOVES:BX,SPCALLFARPTROSTaskSwHook()MOVAX,WORDPTRDS:OSTCBHighRdyOSTCBCur=OSTCBHighRdy()MOVDX,WORDPTRDS:OSTCBHighRdyMOVWORDPTRDS:OSTCBCur,AXMOVWORDPTRDS:OSTCBCur,DXMOVAL,BYTEPTRDS:OSPrioHighRdyOSPrioCur=OSPrioHighRdy()MOVBYTEPTRDS:OSPrioCur,ALLESBX,DWORDPTRDS:OSTCBHighRdySS:SP=OSTCBHighRdy>OSTCBStkPtr()MOVSS,ES:BXMOVSP,ES:BXPOPDS载入新任务的CPU环境()POPES()POPA()IRET返回新任务()OSIntCtxSwENDP图F中断级任务切换时的x堆栈结构当中断发生后CPU在完成当前指令后进入中断处理过程。首先是保存现场将返回地址压入当前任务堆栈然后保存状态寄存器的内容。接下来CPU从中断向量处找到中断服务程序的入口地址运行中断服务程序。在µCOSII中要求用户的中断服务程序在开头保存CPU其他寄存器的内容图F()。此后用户必须调用OSIntEnter()或着把全局变量OSIntNesting加。此时被中断任务的堆栈中保存了任务的全部运行环境。在中断服务程序中有可能引起任务就绪状态的改变而需要任务切换例如调用了OSMboxPost(),OSQPostFront(),OSQPost(),或试图唤醒一个优先级更高的任务(调用OSTaskResume()),还可能调用OSTimeTick()OSTimeDlyResume()等等。µCOSII要求用户在中断服务程序的末尾调用OSInt​Exit()以检查任务就绪状态。在调用OSInt​Exit()后返回地址会压入堆栈中图F()。进入OSIntExit()后由于要访问临界代码区首先关闭中断。由于OSENTERCRITICAL()可能有不同的操作(见节)状态寄存器SW的内容有可能被压入堆栈图F()。如果确实要进行任务切换指针OSTCBHighRdy将指向新的就绪任务的OSTCBOSIntExit()会调用OSIntCtxSw()完成任务切换。注意调用OSIntCtxSw()会在再一次在堆栈中保存返回地址图F()。在进行任务切换的时候我们希望堆栈中只保留一次中断发生的任务环境(如图F())而忽略掉由于函数嵌套调用而压入的一系列返回地址(图F(),(),())。忽略的方法也很简单只要把堆栈指针加一个固定的值就可以了图F()程序清单L()。如果用方法实现OSENTERCRITICAL()这个固定值是如果用方法则是。实际操作中还与编译器以及编译模式有关。例如有些编译器会为OSIntExit()在堆栈中分配临时变量这都会影响具体占用堆栈的大小这一点需要提醒用户注意。一但堆栈指针重新定位后就被保存到将要被挂起的任务OSTCB中图F()程序清单L()。在µCOSII中(包括µCOS)OSIntCtxSw()是唯一一个与编译器相关的函数也是用户问的最多的。如果您的系统移植后运行一段时间后就会死机就应该怀疑是OSIntCtxSw()中堆栈指针重新定位的问题。当当前任务的现场保存完毕后用户定义的对外接口函数OSTaskSwHook()会被调用程序清单L()。注意到OSTCBCur指向当前任务的OSTCBOSTCBHighRdy指向新任务的OSTCB。在函数OSTaskSwHook()中用户可以访问这两个任务的OSTCB。如果不用对外接口函数请在头文件中关闭相应的开关选项提高任务切换的速度。从对外接口函数OSTaskSwHook()返回后由于任务的更替变量OSTCBHighRdy被拷贝到OSTCBCur中程序清单L()同样OSPrioHighRdy被拷贝到OSPrioCur中程序清单L()。此时OSIntCtxSw()将载入新任务的CPU环境首先从新任务OSTCB中取出SS和SP寄存器的值图F()程序清单L()然后运行POPDS图F()程序清单L(),POPES图F()程序清单L(),POPA图F()程序清单L()取出其他寄存器的值,最后用中断返回指令IRET图F()程序清单L()完成任务切换。需要注意的是在运行OSIntCtxSw()和用户定义的OSTaskSwHook()函数期间中断是禁止的。OSTickISR()在节中我们已经提到过实时系统中时钟节拍发生频率的问题应该在到Hz之间。但由于PC环境的特殊性时钟节拍由硬件产生间隔ms(Hz)。我们将时钟节拍频率设为Hz。PC时钟节拍的中断向量为xµCOSII将此向量截取指向了µCOS的中断服务函数OSTickISR()而原先的中断向量保存在中断(x)中。为满足DOS的需要原先的中断服务还是每隔ms(实际上还要短些)调用一次。图F为安装µCOSII前后的中断向量表。在µCOSII中当调用OSStart()启动多任务环境后时钟中断的作用是非常重要的。但在PC环境下启动µCOSII之前就已经有时钟中断发生了实际上我们希望在µCOSII初始化完成之后再发生时钟中断调用OSTickISR()。与此相关的有下述过程:PCDOSSaveReturn()函数(参看PCC):该函数由main()调用任务是取得DOS下时钟中断向量并将其保存在x中。main()函数:设定中断向量x指向任务切换函数OSCtxSw()至少创立一个任务当初始化工作完成后调用OSStart()启动多任务环境第一个运行的任务:设定中断向量x指向函数OSTickISR()将时钟节拍频率从改为Hz图FPC中断向量表(IVT)在程序清单L给出了函数OSTickISR()的伪码。和µCOSII中的其他中断服务程序一样OSTickISR()首先在被中断任务堆栈中保存CPU寄存器的值然后调用OSIntEnter()。µCOSII要求在中断服务程序开头调用OSIntEnter()其作用是将记录中断嵌套层数的全局变量OSIntNesting加。如果不调用OSIntEnter()直接将OSIntNesting加也是允许的。接下来计数器OSTickDOSCtr减程序清单L()每发生次中断OSTickDOSCtr减到则调用DOS的时钟中断处理函数程序清单L()调用间隔大约是ms。如果不调用DOS时钟中断函数则向中断优先级控制器(PIC)发送命令清除中断标志。如果调用了DOS中断则此项操作可免因为在DOS的中断程序中已经完成了。随后OSTickISR()调用OSTimeTick()检查所有处于延时等待状态的任务判断是否有延时结束就绪的任务程序清单L()。在OSTickISR()的最后调用OSIntExit()如果在中断中(或其他嵌套的中断)有更高优先级的任务就绪并且当前中断为中断嵌套的最后一层。OSIntExit()将进行任务调度。注意如果进行了任务调度OSIntExit()将不再返回调用者而是用新任务的堆栈中的寄存器数值恢复CPU现场然后用IRET实现任务切换。如果当前中断不是中断嵌套的最后一层或中断中没有改变任务的就绪状态OSIntExit()将返回调用者OSTickISR()最后OSTickISR()返回被中断的任务。程序清单L给出了OSTickISR()的完整代码。程序清单LOSTickISR()伪码voidOSTickISR(void){Saveprocessorregisters()OSIntNesting()OSTickDOSCtr()if(OSTickDOSCtr==){ChainintoDOSbyexecutingan'INTH'instruction()}else{SendEOIcommandtoPIC(PriorityInterruptController)()}OSTimeTick()()OSIntExit()()Restoreprocessorregisters()Executeareturnfrominterruptinstruction(IRET)()}程序清单LOSTickISR()OSTickISRPROCFARPUSHA保存被中断任务的CPU环境PUSHESPUSHDSMOVAX,SEGOSTickDOSCtr载入DSMOVDS,AXINCBYTEPTROSIntNesting标示uCOSII进入中断DECBYTEPTRDS:OSTickDOSCtrCMPBYTEPTRDS:OSTickDOSCtr,JNESHORTOSTickISR每个时钟节拍(Hz)调用DOS时钟中断MOVBYTEPTRDS:OSTickDOSCtr,INTH调用DOS时钟中断处理过程JMPSHORTOSTickISROSTickISR:MOVAL,H向中断优先级控制器发送命令清除标志位MOVDX,HOUTDX,ALOSTickISR:CALLFARPTROSTimeTick调用OSTimeTick()函数CALLFARPTROSIntExit标示uCOSII退出中断POPDS恢复被中断任务的CPU环境POPESPOPAIRET返回被中断任务OSTickISRENDP如果不更改DOS下的时钟中断频率(保持Hz)OSTickISR()函数还可以简化。程序清单L为Hz的OSTickISR()函数的伪码。同样函数开头要保存所有的CPU寄存器程序清单L()将OSIntNesting加程序清单L()。接下来调用DOS的时钟中断处理过程程序清单L()此处就不需要清除中断优先级控制器的操作了因为DOS的时钟中断处理中包含了这一过程。然后调用OSTimeTick()检查任务的延时是否结束程序清单L()最后调用OSInt​Exit()程序清单L()。结束部分是恢复CPU寄存器的内容程序清单L()执行IRET指令返回被中断的任务。如果采用Hz的OSTickISR()函数系统初始化过程就不用调用PCSetTickRate()同时将文件OSCFGH中的常量OSTICKSPERSEC由改为。程序清单L给出了HzOSTickISR()的完整代码。程序清单LHzOSTickISR()伪码voidOSTickISR(void){Saveprocessorregisters()OSIntNesting()ChainintoDOSbyexecutingan'INTH'instruction()OSTimeTick()()OSIntExit()()Restoreprocessorregisters()Executeareturnfrominterruptinstruction(IRET)()}OSCPUCCµCOSII的移植需要用户改写OSCPUCC中的六个函数:OSTaskStkInit()OSTaskCreateHook()OSTaskDelHook()OSTaskSwHook()OSTaskStatHook()OSTimeTickHook()实际需要修改的只有OSTaskStkInit()函数其他五个函数需要声明但不一定有实际内容。这五个函数都是用户定义的所以OSCPUCC中没有给出代码。如果用户需要使用这些函数请将文件OSCFGH中的#defineconstantOSCPUHOOKSEN设为设为表示不使用这些函数。程序清单LHz的OSTickISR()函数OSTickISRPROCFARPUSHA保存被中断任务的CPU环境PUSHESPUSHDSMOVAX,SEGOSIntNesting载入DSMOVDS,AXINCBYTEPTROSIntNesting标示uCOSII进入中断INTH调用DOS的时钟中断处理函数CALLFARPTROSTimeTick调用OSTimeTick()函数CALLFARPTROSIntExit标示uCOSIIof中断结束POPDS恢复被中断任务的CPU环境POPESPOPAIRET返回被中断任务OSTickISRENDP图F传递参数pdata的堆栈初始化结构OSTaskStkInit()该函数由OSTaskCreate()或OSTaskCreateExt()调用用来初始化任务的堆栈。初始状态的堆栈模拟发生一次中断后的堆栈结构。图F说明了OSTaskStkInit()初始化后的堆栈内容。请注意图中的堆栈结构不是调用OSTaskStkInit()任务的而是新创建任务的。当调用OSTaskCreate()或OSTaskCreateExt()创建一个新任务时需要传递的参数是:任务代码的起使地址参数指针(pdata)任务堆栈顶端的地址任务的优先级。OSTaskCreateExt()还需要一些其他参数但与OSTask​StkInit()没有关系。OSTaskStkInit()(程序清单L)只需要以上提到的个参数(task,pdata,和ptos)。程序清单LOSTaskStkInit()void*OSTaskStkInit(void(*task)(void*pd),void*pdata,void*ptos,INTUopt){INTU*stkopt=opt*'opt'未使用,此处可防止编译器的警告*stk=(INTU*)ptos*载入堆栈指针()**stk=(INTU)FPSEG(pdata)*放置向函数传递的参数()**stk=(INTU)FPOFF(pdata)*stk=(INTU)FPSEG(task)*函数返回地址()**stk=(INTU)FPOFF(task)*stk=(INTU)x*SW设置为中断开启()**stk=(INTU)FPSEG(task)*堆栈顶端放置指向任务代码的指针**stk=(INTU)FPOFF(task)*stk=(INTU)xAAAA*AX=xAAAA()**stk=(INTU)xCCCC*CX=xCCCC**stk=(INTU)xDDDD*DX=xDDDD**stk=(INTU)xBBBB*BX=xBBBB**stk=(INTU)x*SP=x**stk=(INTU)x*BP=x**stk=(INTU)x*SI=x**stk=(INTU)x*DI=x**stk=(INTU)x*ES=x**stk=DS*DS=当前CPU的DS寄存器()*return((void*)stk)}由于x堆栈是位宽的(以字为单位)程序清单L()OSTaskStkInit()将创立一个指向以字为单位内存区域的指针。同时要求堆栈指针指向空堆栈的顶端。笔者使用的BorlandCC编译器配置为用堆栈而不是寄存器来传送参数pdata此时参数pdata的段地址和偏移量都将被保存在堆栈中程序清单L()。堆栈中紧接着是任务函数的起始地址程序清单L()理论上此处应该为任务的返回地址但在µCOSII中任务函数必须为无限循环结构不能有返回点。返回地址下面是状态字(SW)程序清单L()设置状态字也是为了模拟中断发生后的堆栈结构。堆栈中的SW初始化为x这将使任务启动后允许中断发生如果设为x则任务启动后将禁止中断。需要注意的是如果选择任务启动后允许中断发生则所有的任务运行期间中断都允许同样如果选择任务启动后禁止中断则所有的任务都禁止中断发生而不能有所选择。如果确实需要突破上述限制可以通过参数pdata向任务传递希望实现的中断状态。如果某个任务选择启动后禁止中断那么其他的任务在运行的时候需要重新开启中断。同时还要修改OSTaskIdle()和OSTaskStat()函数在运行时开启中断。如果以上任何一个环节出现问题系统就会崩溃。所以笔者还是推荐用户设置SW为x在任务启动时开启中断。堆栈中还要留出各个寄存器的空间注意寄存器在堆栈中的位置要和运行指令PUSHA,PUSHES,和PUSHDS和压入堆栈的次序相同。上述指令在每次进入中断服务程序时都会调用程序清单L()。AX,BX,CX,DX,SP,BP,SI,和DI的次序是和指令PUSHA的压栈次序相同的。如果使用没有PUSHA指令的处理器就要使用多个PUSH指令压入上述寄存器且顺序要与PUSHA相同。在程序清单L中每个寄存器被初始化为不同的值这是为了调试方便。Borland编译器支持伪寄存器变量操作可以用DS关键字取得CPUDS寄存器的值程序清单L中()标记处用DS直接把DS寄存器拷贝到堆栈中。堆栈初始化工作结束后OSTaskStkInit()返回新的堆栈栈顶指针OSTaskCreate()或OSTaskCreateExt()将指针保存在任务的OSTCB中。OSTaskCreateHook()OSCPUCC中未定义此函数为用户定义。OSTaskDelHook()OSCPUCC中未定义此函数为用户定义。OSTaskSwHook()OSCPUCC中未定义此函数为用户定义。其用法请参考例程。OSTaskStatHook()OSCPUCC中未定义此函数为用户定义。其用法请参考例程。OSTimeTickHook()OSCPUCC中未定义此函数为用户定义。内存占用表列出了指定初始化常量的情况下µCOSII占用内存的情况包括数据和程序代码。如果µCOSII用于嵌入式系统则数据指RAM的占用程序代码指ROM的占用。内存占用的说明清单随磁盘一起提供给用户在安装µCOSII后查看SOFTWAREuCOSIIIxLDOC目录下的ROMRAMXLS文件。该文件为MicrosoftExcel文件需要Office或更高版本的Excel打开。表中所列出的内存占用大小都近似为字节的倍数。笔者所用的BorlandCCV设定为编译产生运行速度最快的目标代码所以表中所列的数字并不是绝对的但可以给读者一个总的概念。例如如果不使用消息队列机制在编译前将OSQEN设为则编译后的目标代码长度,字节可减小大约,字节。此外空闲任务(idle)和统计任务(statistics)的堆栈都设为,字节(Kb)。根据您自己的要求可以增减。µCOSII的数据结构最少需要字节的RAM。表说明了如何裁减µCOSII应用在更小规模的系统上。此处的小系统有个任务。并且不采用如下功能:•邮箱功能(OSMBOXEN设为)•内存管理机制(OSMEMEN设为)•动态改变任务优先级(OSTASKCHANGEPRIOEN设为)•旧版本的任务创建函数OSTaskCreate()(OSTASKCREATEEN设为)•任务删除(OSTASKDELEN设为)•挂起和唤醒任务(OSTASKSUSPENDEN设为)采取上述措施后程序代码空间可以减小Kb数据空间可以减小,字节。由于只有个任务运行节省了大量用于任务控制块OSTCB的空间。在x的大模式编译条件下每一个OSTCB将占用字节的RAM。运行时间表到列出了大部分µCOSII函数在处理器上的运行时间。统计的方法是将C原程序编译为汇编代码然后计算每条汇编指令所需的时钟周期根据处理器的时钟频率最后算出运行时间。表中的I栏为函数包含有多少条指令C栏为函数运行需要多少时钟周期µs为运行所需的以微秒为单位的时间。表中有类时间分别是在函数中关闭中断的时间、函数运行的最小时间和最大时间。如果您不使用处理器表中的数据就没有什么实际意义但可以使您理解每个函数运行时间的相对大小。表µCOSII内存占用()配置参数值代码(字节)数据(字节)OSMAXEVENTSOSMAXMEMPARTOSMAXQSOSMAXTASKS,OSLOWESTPRIOOSTASKIDLESTKSIZE,OSTASKSTATENOSTASKSTATSTKSIZE,OSCPUHOOKSENOSMBOXEN(参看OSMAXEVENTS)OSMEMEN(参看OSMAXMEMPART)OSQEN, (参看OSMAXQS)OSSEMEN (参看OSMAXEVENTS)OSTASKCHANGEPRIOENOSTASKCREATEENOSTASKCREATEEXTENOSTASKDELENOSTASKSUSPENDENµCOSII内核,应用程序堆栈应用程序的RAM总计,,表压缩后的µCOSII配置配置参数值代码(字节)数据(字节)OSMAXEVENTSOSMAXMEMPARTOSMAXQSOSMAXTASKSOSLOWESTPRIOOSTASKIDLESTKSIZE,OSTASKSTATENOSTASKSTATSTKSIZE,OSCPUHOOKSENOSMBOXEN(参看OSMAXEVENTS)OSMEMEN(参看OSMAXMEMPART)OSQEN,(参看OSMAXQS)OSSEMEN(参看OSMAXEVENTS)OSTASKCHANGEPRIOENOSTASKCREATEENOSTASKCREATEEXTENOSTASKDELENOSTASKSUSPENDENµCOSII内核,应用程序堆栈应用程序的RAM总计,,以上各表中的时间数据都是假设函数成功运行正常返回同时假定处理器工作在最大总线速度。平均来说处理器的每条指令需要个时钟周期。对于处理器µCOSII中的函数最大的关闭中断时间是µs约,个时钟周期。NA是指该函数的运行时间长短并不重要例如一些只执行一次初始化函数。如果您用的是x系列的其他CPU您可以根据表中每个函数的运行时钟周期项估计当前CPU的执行时间。例如如果用且知的指令平均用个时钟周期或者知道总线频率为MHz(比的MHz快倍)都可以估计出函数在上的执行时间。表µCOSII函数在MHz上的执行时间关闭中断时间最小运行时间最大运行时间函数ICµsICµsICµs杂项OSInit()NANANANANANANANANAOSSchedLock()OSSchedUnlock()OSStart()OSStatInit()NANANANANANANANANAOSVersion()中断管理OSIntEnter()OSIntExit()OSTickISR(),,,邮箱OSMboxAccept()OSMboxCreate()OSMboxPend(),OSMboxPost(),OSMboxQuery(),,表µCOSII函数在MHz上的执行时间(续表)内存管理OSMemCreate()OSMemGet()OSMemPut()OSMemQuery()消息队列OSQAccept()OSQCreate(),,OSQFlush()OSQPend(),OSQPost(),OSQPostFront(),OSQQuery(),,,信号量管理OSSemAccept()OSSemCreate()OSSemPend(),OSSemPost(),OSSemQuery()任务管理OSTaskChangePrio(),OSTaskCreate(),,OSTaskCreateExt(),,OSTaskDel(),,OSTaskDelReq()OSTaskQuery(),,,OSTaskResume()OSTaskStkChk()OSTaskSuspend(),表µCOSII函数在MHz上的执行时间(续表)时钟管理OSTimeDly()OSTimeDlyHMSM(),,OSTimeDlyResume()OSTimeGet()OSTimeSet()OSTimeTick(),,,用户定义函数OSTaskCreateHook()OSTaskDelHook()OSTaskStatHook()OSTaskSwHook()OSTimeTickHook()下面我们将讨论每个函数的关闭中断时间最大、最小运行时间是如何计算的以及这样计算的先决条件。OSSchedUnlock()最小运行时间是当变量OSLockNesting减为且系统中没有更高优先级的任务就绪OSSchedUnlock()正常结束返回调用者。最大运行时间是也是当变量OSLockNesting减为但有更高优先级的任务就绪函数中需要进行任务切换。OSIntExit()最小运行时间是当变量OSLockNesting减为且系统中没有更高优先级的任务就绪OSIntExit()正常结束返回被中断任务。最大运行时间是也是当变量OSLockNesting减为但有更高优先级的任务就绪OSIntExit()将不返回调用者经过任务切换操作后将直接返回就绪的任务。OSTickISR()此函数假定在当前µCOSII中运行有最大数目的任务(个)。最小运行时间是当个任务都不在等待延时状态。也就是说所有的任务都不需要OSTickISR()处理。最大运行时间是当个任务(空闲进程不会延时等待)都处于延时状态此时OSTickISR()需要逐个检查等待中的任务将计数器减并判断是否延时结束。这种情况对于系统是一个很重的负荷。例如在最坏的情况设时钟节拍间隔msOSTickISR()需要µs占了约的CPU利用率。但请注意此时所有的任务都没有执行只是内核的开销。OSMboxPend()最小运行时间是当邮箱中有消息需要处理的时候。最大运行时间是当邮箱中没有消息任务需要等待的时候。此时调用OSMboxPend()的任务将被挂起进行任务切换。最大运行时间是同一任务执行OSMboxPend()的累计时间这个过程包括OSMboxPend()查看邮箱发现没有消息再调用任务切换函数OSSched()切换到新任务。当由于某种原因调用OSMboxPend()的任务又被唤醒执行从OSSched()中返回发现返回的原因是由于延时结束(处理延时结束情况的代码最长译者注)最后返回调用任务。OSMboxPend()的最大运行时间是上述时间的总和。OSMboxPost()最小运行时间是当邮箱是空的没有任务等待消息的时候。最大运行时间是当消息邮箱中有一个或多个任务在等待消息。此时消息将发往等待队列中优先级最高的任务将此任务唤醒执行。最大运行时间是同一任务执行OSMboxPost()的累计时间这个过程包括任务唤醒等待任务发送消息调用任务切换函数OSSched()切换到新任务。当由于某种原因调用OSMboxPost()的任务又被唤醒执行从OSSched()中返回发现返回的原因是由于延时结束(处理延时结束情况的代码最长译者注)最后返回调用任务。OSMboxPost()的最大运行时间是上述时间的总和。OSMemGet()最小运行时间是当系统中已经没有内存块OSMemGet()返回错误码。最大运行时间是OSMemGet()获得了内存块返回调用者。OSMemPut()最小运行时间是当向一个已经排满的内存分区中返回内存块。最大运行时间是当向一个未排满的内存分区中返回内存块OSQPend()最小运行时间是当消息队列中有消息需要处理的时候。最大运行时间是当消息队列中没有消息任务需要等待的时候。此时调用OSQPend()的任务将被挂起进行任务切换。最大运行时间是同一任务执行OSQPend()的累计时间这个过程包括OSQPend()查看消息队列发现没有消息再调用任务切换函数OSSched()切换到新任务。当由于某种原因调用OSQPend()的任务又被唤醒执行从OSSched()中返回发现返回的原因是由于延时结束(处理延时结束情况的代码最长译者注)最后返回调用任务。OSQ`Pend()的最大运行时间是上述时间的总和。OSQPost()最小运行时间是当消息队列是空的没有任务等待消息的时候。最大运行时间是当消息队列中有一个或多个任务在等待消息。此时消息将发往等待队列中优先级最高的任务将此任务唤醒执行。最大运行时间是同一任务执行OSQPost()的累计时间这个过程包括任务唤醒等待任务发送消息调用任务切换函数OSSched()切换到新任务。当由于某种原因调用OSQPost()的任务又被唤醒执行从OSSched()中返回发现返回的原因是由于延时结束(处理延时结束情况的代码最长译者注)最后返回调用任务。OSQPost()的最大运行时间是上述时间的总和。OSQPostFront()此函数与OSQPost()的过程相同。OSSemPend()最小运行时间是当信号量可获取的时候(信号量计数器大于)。最大运行时间是当信号量不可得任务需要等待的时候。此时调用OSSemPend()的任务将被挂起进行任务切换。最大运行时间是同一任务执行OSQPend()的累计时间这个过程包括OSSemPend()查看信号量计数器发现是再调用任务切换函数OSSched()切换到新任务。当由于某种原因调用OSSemPend()的任务又被唤醒执行从OSSched()中返回发现返回的原因是由于延时结束(处理延时结束情况的代码最长译者注)最后返回调用任务。OSSemPend()的最大运行时间是上述时间的总和。OSSemPost()最小运行时间是当没有任务在等待信号量的时候。最大运行时间是当有一个或多个任务在等待信号量。此时等待队列中优先级最高的任务将被唤醒执行。最大运行时间是同一任务执行OSSemPost()的累计时间这个过程包括任务唤醒等待任务调用任务切换函数OSSched()切换到新任务。当由于某种原因调用OSSemPost()的任务又被唤醒执行从OSSched()中返回发现返回的原因是由于延时结束(处理延时结束情况的代码最长译者注)最后返回调用任务。OSSemPost()的最大运行时间是上述时间的总和。OSTaskChangePrio()最小运行时间是当任务被改变的优先级比当前运行任务的低此时不进行任务切换直接返回调用任务。最大运行时间是当任务被改变的优先级比当前运行任务的高此时将进行任务切换。OSTaskCreate()最小运行时间是当调用OSTaskCreate()的任务创建了一个比自己优先级低的任务此时不进行任务切换。最大运行时间是当调用OSTaskCreate()的任务创建了一个比自己优先级高的任务此时将进行任务切换。上述两种情况都是假定OSTaskCreateHook()不进行任何操作。OSTaskCreateExt()最小运行时间是当OSTaskCreateExt()不对堆栈进行清零操作(此项操作是为堆栈检查函数做准备的)。最大运行时间是当OSTaskCreateExt()需要进行堆栈清零操作。但此项操作的时间取决于堆栈的大小。如果设清除每个堆栈单元(堆栈操作以字为单位译者注)需要个时钟周期(µs)字节的堆栈将需要,µs(字节除以再乘以µs每字)。在清除堆栈过程中中断是打开的可以响应中断请求。上述两种情况都是假定OSTaskCreateHook()不进行任何操作。OSTaskDel()最小运行时间是当被删除的任务不是当前任务此时不进行任务切换。最大运行时间是当被删除的任务是当前任务此时将进行任务切换。OSTaskDelReq()该函数很短几乎没有最小和最大运行时间之分。OSTaskResume()最小运行时间是当OSTaskResume()唤醒了一个任务但该任务的优先级比当前任务低此时不进行任务切换。最大运行时间是OSTaskResume()唤醒了一个优先级更高的任务此时将进行任务切换。OSTaskStkChk()OSTaskStkChk()的执行过程是从堆栈的底端开始检查的个数估计堆栈所剩的空间。所以最小运行时间是当OSTaskStkChk()检查一个全部占满的堆栈。但实际上这种情况是不允许发生的这将使系统崩溃。最大运行时间是当OSTaskStkChk()检查一个全空堆栈执行时间取决于堆栈的大小。例如检查每个堆栈单元(堆栈操作以字为单位译者注)需要钟周期(µs)字节的堆栈将需要,µs(字节除以再乘以µs每字)。再加上其他的一些操作总共需要大约,µs。在检查堆栈过程中中断是打开的可以响中断请求。OSTaskSuspend()最小运行时间是当被挂起的任务不是当前任务此时不进行任务切换。最大运行时间是当前任务挂起自己此时将进行任务切换。OSTaskQuery()该函数的运行时间总是一样的。OSTaskQuery()执行的操作是获取任务的任务控制块OSTCB。如果OSTCB中包含所有的操作项需要占用字节(大模式编译)。OSTimeDly()如果延时时间不为则OSTimeDly()运行时间总是相同的。此时将进行任务切换。如果延时时间为OSTimeDly()不清除OSRdyGrp中的任务就绪位不进行延时操作直接返回。OSTimeDlyHMSM()如果延时时间不为则OSTimeDlyHMSM()运行时间总是相同的。此时将进行任务切换。此外OSTimeDlyHMSM()延时时间最好不要超过,个时钟节拍。也就是说如果时钟节拍发生的间隔为ms(频率Hz)延时时间应该限定在分秒毫秒内。如果超过了上述数值该任务就不能用OSTimeDlyResume()函数唤醒。OSTimeDlyResume()最小运行时间是当被唤醒的任务优先级低于当前任务此时不进行任务切换。最大运行时间是当唤醒了一个优先级更高的任务此时将进行任务切换。OSTimeTick()前面我们讨论的OSTickISR()函数其实就是OSTimeTick()与OSIntEnter()、OSIntExit()的组合。OSTickISR()的时间占用情况就是OSTimeTick()的占用情况。以下讨论假定系统中有µCOSII允许的最大数量的任务(个)。最小运行时间是当个任务都不在等待延时状态。也就是说所有的任务都不需要OSTimeTick()处理。最大运行时间是当个任务(空闲进程不会延时等待)都处于延时状态此时OSTimeTick()需要逐个检查等待中的任务将计数器减并判断是否延时结束。例如在最坏的情况设时钟节拍间隔msOSTimeTick()需要约µs占了的CPU利用率表各函数的执行时间(按关闭中断时间排序)关闭中断时间最小运行时间最大运行时间函数ICµsICµsICµsOSVersion()OSStart()OSSchedLock()OSIntEnter()OSTimeGet()OSTimeSet()OSSemAccept()OSSemCreate()OSMboxCreate()OSQCreate(),,OSMboxAccept()OSMemCreate()OSTaskDelReq()OSQFlush()OSTaskResume()OSMemGet()OSMemPut()OSTimeTick(),,,OSTickISR(),,,OSTaskStkChk()OSTaskSuspend(),OSQAccept()OSMemQuery()OSIntExit()OSSchedUnlock()OSTimeDly()OSTimeDlyResume()OSTaskChangePrio(),OSSemPend(),OSMboxPend(),OSTimeDlyHMSM(),,OSTaskCreate(),,OSTaskCreateExt(),,OSTaskDel(),,OSQPend(),OSMboxPost(),OSSemPost(),OSQPostFront(),OSQPost(),OSSemQuery()OSMboxQuery(),,OSTaskQuery(),,,OSQQuery(),,,OSStatInit()NANANANANANANANANAOSInit()NANANANANANANANANA表各函数的执行时间(按最大运行时间排序)关闭中断时间最大运行时间最小运行时间ServiceICµsICµsICµsOSVersion()OSIntEnter()OSSchedLock()OSTimeSet()OSTimeGet()OSSemAccept()OSQFlush()OSMboxAccept()OSStart()OSMemPut()OSTaskDelReq()OSMemGet()OSMemQuery()OSQAccept()OSIntExit()OSTaskStkChk()OSMemCreate()OSSemCreate()OSSchedUnlock()OSTimeDly()OSSemQuery()OSMboxCreate()OSTaskResume()OSTimeDlyResume()OSTaskQuery(),,,OSTaskSuspend(),OSQQuery(),,,OSMboxQuery(),,OSQCreate(),,OSSemPost(),OSQPostFront(),OSMboxPost(),OSQPost(),OSTaskChangePrio(),OSSemPend(),OSTaskDel(),,OSMboxPend(),OSQPend(),OSTimeDlyHMSM(),,OSTaskCreate(),,OSTaskCreateExt(),,OSTimeTicK(),,,OSTickISR(),,,OSInit()NANANANANANANANANAOSStatInit()NANANANANANANANAN

用户评价(0)

关闭

新课改视野下建构高中语文教学实验成果报告(32KB)

抱歉,积分不足下载失败,请稍后再试!

提示

试读已结束,如需要继续阅读或者下载,敬请购买!

评分:

/31

VIP

在线
客服

免费
邮箱

爱问共享资料服务号

扫描关注领取更多福利