《嵌入式实时操作系统uC OS-2教程》课件第4章_第1页
《嵌入式实时操作系统uC OS-2教程》课件第4章_第2页
《嵌入式实时操作系统uC OS-2教程》课件第4章_第3页
《嵌入式实时操作系统uC OS-2教程》课件第4章_第4页
《嵌入式实时操作系统uC OS-2教程》课件第4章_第5页
已阅读5页,还剩81页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

第4章中断处理与时间管理4.1中断处理的基本概念

4.2μC/OS-Ⅱ的中断处理

4.3μC/OS-Ⅱ的时钟节拍

4.4μC/OS-Ⅱ的时间管理

习题 4.1中断处理的基本概念

4.1.1中断

中断定义为CPU对系统内外发生的异步事件的响应。异步事件是指没有一定时序关系的、随机发生的事件。当中断产生时,由硬件向CPU发送一个异步事件请求,CPU接收到请求后,中止当前工作,保存当前运行环境,转去处理相应的异步事件任务,这个过程称为中断。事件处理完毕后,在前后台系统中,程序回到后台程序;在不可剥夺型内核中,程序回到被中断了的任务;在可剥夺型内核中,让进入就绪态的优先级最高的任务开始运行,若没有高优先级任务准备就绪,则回到被中断了的任务。使用中断机制的优点在于:CPU无需连续不断地查询是否有新的事件发生,只需在有事件发生时作出响应。CPU可以通过两条特殊指令关中断(DisableInterrupt)和开中断(EnableInterrupt)来响应和不响应中断。关中断会影响中断延迟时间,时间太长可能会引起中断丢失。所以在实时环境中,关中断的时间应尽量短。在中断服务期间,CPU一般允许中断嵌套(如图4.1所示),允许新的中断打入,识别中断优先级别更高的事件。图4.1中断嵌套4.1.2中断延迟

中断延迟定义为从硬件中断发生到开始执行中断处理程序第一条指令所用的时间,也就是说,中断延迟是从中断发生到中断跳转指令执行完毕之间的这段时间,它是实时内核最重要的指标。由于实时操作系统考虑得更多的是最坏的情况,而不是平均的情况,因此指令执行的时间必须按照最长的指令执行时间来计算。所以,中断延迟时间通常是由关中断的最长时间来决定的。关中断的时间越长,中断延迟就越长。中断延迟由下式给出:在前后台系统中:

在不可剥夺型和不可剥夺内核中:4.1.3中断响应

中断响应定义为从中断发生起到开始执行中断用户处理程序的第一条指令所用的时间,换句话说,中断响应是从中断发生到刚刚开始处理异步事件之间的这段时间,它包括开始处理这个中断前的全部开销。一般地,执行用户代码之前要保护现场,将CPU的各个寄存器推入堆栈。

在前后台系统和不可剥夺型内核中,保存寄存器以后立即执行用户代码,中断响应由下式给出:

中断响应 = 中断延迟 + 保存CPU内部寄存器的时间在可剥夺型内核中,则要先调用一个特定的函数,通知内核即将进行中断服务,使得内核可以跟踪中断的嵌套。对μC/OS-Ⅱ说来,这个函数是OSIntEnter(),可剥夺型内核的中断响应由下式给出:

中断响应 = 中断延迟 + 保存CPU内部寄存器的时间 + 内核进入中断服务函数的执行时间

中断响应考虑的是系统在最坏情况下的响应中断时间,而不是平均时间。如某系统100次中有99次在100μs之内响应中断,只有一次响应中断的时间是250μs,只能认为中断响应时间是250μs。4.1.4中断恢复时间

中断恢复时间(InterruptRecovery)定义为CPU返回到被中断了的程序代码所需要的时间。

在前后台系统和不可剥夺型内核中,中断恢复时间只包括恢复CPU内部寄存器值的时间和执行中断返回指令的时间。中断恢复时间由下式给出:对于可剥夺型内核,中断的恢复要复杂一些。一般地,可剥夺型内核在中断服务子程序的末尾都要调用一个由实时内核提供的中断脱离函数。在μC/OS-Ⅱ中,这个函数叫做OSIntExit(),它首先判断是否脱离了所有的中断嵌套,然后再判断是否有更高优先级的任务准备就绪。若还处于中断嵌套中,那么程序返回到前一级中断服务子程序继续执行;若已经脱离了所有的中断嵌套,则检查当前是否有优先级更高的任务准备就绪。若有更高优先级任务准备就绪,则返回到这个优先级更高的任务,被中断了的任务只有重新成为优先级最高的就绪态任务时才能恢复运行;如果没有,则返回到被中断的任务继续执行。在这种情况下,可剥夺型内核的中断恢复时间由下式给出:4.1.5中断延迟、响应和恢复时间的比较

前后台系统、不可剥夺型内核以及可剥夺型内核的中断延迟、中断响应和中断恢复时间的比较如图4.2和图4.3所示。图4.2前后台系统和不可剥夺型内核的中断延迟、响应和恢复时间的比较图4.3可剥夺型内核的中断延迟、响应和恢复时间的比较4.1.6非屏蔽中断

非屏蔽中断(NMI)是指不能用系统指令来关闭的中断,其特点是中断优先级高、延迟时间短、响应快、不能被嵌套、不能忍受内核的延迟,一般常应用于紧急事件处理,如掉电保护等。非屏蔽中断的规则如下:

(1)在非屏蔽中断处理程序中,不能处理临界区代码、不能使用内核提供的服务。

(2)在非屏蔽中断处理程序中,参数的传递必须用全程变量,且全程变量的字节长度必须能够一次读完。

若一定要在非屏蔽中断产生时使用内核服务,则可以通过用非屏蔽中断产生普通可屏蔽中断的方法来实现。 4.2μC/OS-Ⅱ的中断处理

4.2.1中断处理程序

在μC/OS-Ⅱ中,中断处理程序可用汇编语言编写,也可以用C语言编写。一个标准的μC/OS-Ⅱ中断处理程序应该按图4.4所示流程图进行编写。特别需要注意的是:与前后台系统中的中断服务子程序不同,μC/OS-Ⅱ要知道当前内核是否正在处理中断、是否脱离中断。对此,μC/OS-Ⅱ是通过在中断服务子程序中调用两个系统服务函数来实现的:一个是中断进入函数OSIntEnter(),其功能是通知内核系统已经进入了中断处理服务子程序;另一个是中断脱离函数OSIntExit(),其功能是通知内核系统已经退出了当前的中断服务子程序。μC/OS-Ⅱ是通过检查中断嵌套层数跟踪计数器OSIntNesting来识别是否处于中断处理程序中的,OSIntEnter()的功能只是对OSIntNesting加1。因此,中断服务子程序也可以不调用OSIntEnter(),而直接对OSIntNesting加1,这样做的好处是可以使程序运行的时间更短。因为OSIntNesting是一个单字节整型全局变量,所以μC/OS-Ⅱ的最大中断嵌套层数是255。图4.4标准的μC/OS-Ⅱ中断处理程序流程图如果中断服务子程序执行得很快,且在执行过程中禁止新的中断打入,那么就没有必要通知内核,已经进入了中断服务也无需调用OSIntEnter()或OSIntNesting加1,相应的在程序最后也无需调用中断脱离函数OSIntExit()。

OSIntEnter()函数源代码如程序清单4.1所示。

程序清单4.1OSIntEnter()函数源代码

voidOSIntEnter(void){

if(OSRunning= =TRUE){ /*在多任务启动后,方可通知内核,否则,直接退出 */

if(OSIntNesting<255)OSIntNesting++;/*中断嵌套计数器加1 */

}

}

OSIntExit()函数程序流程如图4.5所示。图4.5OSIntExit()函数程序流程图

OSIntExit()函数源代码如程序清单4.2所示。

程序清单4.2OSIntExit()函数源代码

voidOSIntExit(void){

#ifOS_CRITICAL_METHOD==3

OS_CPU_SRcpu_sr;

#endif

OS_ENTER_CRITICAL();

if(OSRunning==TRUE){

if(OSIntNesting>0)OSIntNesting--;

if((OSIntNesting|OSLockNesting)==0){ (2)

OSIntExitY=OSUnMapTbl[OSRdyGrp];

OSPrioHighRdy=(INT8U)((OSIntExitY<<3)+OSUnMapTbl[OSRdyTbl[OSIntExitY]]);

if(OSPrioHighRdy!=OSPrioCur){

OSTCBHighRdy=OSTCBPrioTbl[OSPrioHighRdy]; (3)

OSCtxSwCtr++; //任务切换计数器加1

OSIntCtxSw(); (4)

}}}

OS_EXIT_CRITICAL();}

OSIntExit()函数主要完成如下四个功能:

(1)给中断嵌套层数计数器减1。

(2)找出准备就绪的最高优先级任务。

(3)检查准备就绪的最高优先级任务是否为被当前服务所中断的任务,若是,则返回到被中断的任务;若不是,则进行任务控制块指针切换,将即将运行任务的任务控制块指针指向当前准备就绪的优先级最高任务的任务控制块。

(4)调用中断级任务切换函数OSIntCtxSw(),进行任务切换。

和OSIntEnter()函数一样,OSIntExit()函数规定:如果多任务没有启动,则直接退出,系统按前后台方式处理中断。

OSIntExit()看起来很像任务调度函数OSSched(),不同之处有三点:

(1) OSIntExit()使中断嵌套层数减1,而重新的调度条件是:中断嵌套层数计数器和调度锁定嵌套计数器(OSLockNesting)二者都必须是零。

(2) OSRdyTbl[]所需的检索值Y是保存在全局变量OSIntExitY中的,这样做是为了避免在任务栈中安排局部变量。

(3)如果需要做任务切换,OSIntExit()将调用中断级任务切换函数OSIntCtxSw(),而不是调用任务级任务切换函数OS_TASK_SW()。4.2.2中断处理过程

如图4.6所示,μC/OS-Ⅱ的中断处理过程具体如下:

(1)当一个中断发生时,若中断是开放的,则CPU运行完毕当前指令。

(2) CPU自动将当前指令的下一条指令的程序计数器指针保存到堆栈中,然后再将中断矢量入口地址赋给程序计数器,将程序转入中断矢量入口单元。

(3)中断入口矢量单元中一般有一条长跳转指令,程序将根据长跳转指令的指向跳转到相应的用户程序去,执行中断服务子程序。从第(1)步到第(3)步执行完毕之间的时间差,就是中断延迟时间。图4.6μC/OS-Ⅱ的中断处理过程示意图

(4)程序保存所有需要保存的CPU寄存器。

(5)调用一个内核系统服务——中断进入函数OSIntEnter(),通知内核,CPU已经进入中断服务子程序,并且计算中断嵌套层次。μC/OS-Ⅱ的最大中断嵌套层次是255层,该数值主要是由中断嵌套层数计数器OSIntNesting的数据类型决定的。从第(1)步到第(5)步执行完毕之间的时间差,就是中断响应时间。(4)、(5)两步与前后台系统的中断服务子程序不同。

(6)中断服务用户子程序。

(7)中断返回前需要调用一个内核系统服务——中断脱离函数OSIntExit(),通知内核CPU要退出当前中断。中断脱离函数首先将中断嵌套计数器减1,若此时程序还处于中断嵌套中,则继续执行上一个中断;若程序没有中断嵌套,则中断脱离函数查找是否有更高优先级任务准备就绪。若有更高优先级任务准备就绪,程序则返回到准备就绪的最高优先级任务运行;若没有或者调度上锁,则程序返回到被中断的任务继续运行。

(8)在执行完毕OSIntExit()后,无论程序转向何处,都要恢复所有CPU寄存器。

(9)执行中断返回指令。

(10)返回。

4.3μC/OS-Ⅱ的时钟节拍

4.3.1时钟节拍

时钟节拍是特定的周期性中断(时钟中断),可以看做是系统心脏的脉动。时钟节拍是内核的最小计时单位,它完成两个功能:一是累计时间,μC/OS-Ⅱ定义了32位无符号整数OSTime来记录系统启动后时钟节拍滴答的数目;二是通过时钟中断来确定时间间隔,实现任务的延时及确定任务超时。时钟节拍频率取决于不同的应用,一般以10~100Hz为宜。时钟节拍频率越快,系统的额外开销就越大,实际频率取决于用户程序的精度。

μC/OS-Ⅱ中的时钟节拍服务是通过在中断服务子程序OSTickISR()中调用时钟节拍子程序OSTimeTick()来实现的。时钟节拍计时的单位是一个时钟节拍,由于时钟节拍计时存在抖动问题,因此计时的精度可能并不是一个时钟节拍,只是在每个时钟节拍中断到来的时候作一次裁决而已。那么抖动对计时精度是如何影响的呢?下面将分三种情况讨论说明。如图4.7~图4.9所示,假设时钟节拍每20ms发生一次,要求将任务A延时一个时钟节拍即20ms。图中阴影部分表示各个任务的运行时间,由于程序中可能存在循环和条件语句,因此同一任务在不同时期的运行时间长短可能不同,时钟节拍中断服务子程序的运行时间也不同。为了更方便地说明问题,图中的延时画得有所夸大。图4.7将任务延时一个时钟节拍(第一种情况)图4.8将任务延时一个时钟节拍(第二种情况)图4.9将任务延时一个时钟节拍(第三种情况)第一种情况,如图4.7所示,所有中断和高优先级的任务都超前于要求延时一个时钟节拍的任务A运行。高优先级的任务可能是一个,也可能是多个,每个任务可能只有一种延时,也可能有多种延时,执行的时间也可能不一样,图中阴影所表示的执行时间是所有高优先级任务执行时间的总和,它的长短是变化的。时钟中断服务子程序运行时间的长短也可能变化。中断的优先级总是高于任务,它是即时响应的,所以只有当中断服务子程序执行完毕返回后,准备就绪的高优先级任务才能运行,所有高优先级任务挂起后,最低优先级任务A才可能获得CPU使用权,转为运行态。中断和高优先级任务运行时间的变化导致了任务A延时的抖动。第二种情况,如图4.8所示,所有高优先级的任务和中断服务的执行时间都小于一个时钟节拍。由于时钟节拍中断服务子程序和所有高优先级任务第一次执行时间的总和很长,接近20ms,因此导致了任务A刚调用延时函数挂起,第二次时钟节拍又到来了。此时内核又将作一次裁决,将所有任务中不为0的延时项减1,并将延时项变为0的任务置于就绪状态,让高优先级且准备就绪的任务依优先级的高低顺序运行。此时任务A也因延时项减为0而处于就绪状态,一旦高优先级任务挂起,它就会立即转入运行态。由于时钟中断服务子程序和所有高优先级任务第二次运行时间的总和又很短,因此任务A得以很快转入运行态,其结果是导致了任务A的两次运行时间间隔很短。同理,以后的两次延时也有很多的变化。第三种情况,如图4.9所示,中断和所有高优先级的运行时间的总和大于一个时钟节拍。在这种情况下,拟延时一个时钟节拍的任务A实际上在两个时钟节拍后运行,产生了很多的误差。这在某些应用中或许是可以的,但在大多数场合是不能接受的。

清楚地认识0到一个节拍之间的延时过程是非常重要的,如果用户只想延时一个时钟节拍,而实际上得到的往往不是一个时钟节拍。即使用户的处理器的负荷不是很重,这种情况依然是存在的。为了保证足够的精度,延时设计时最好多加一个时钟节拍,例如要将任务延时5个时钟节拍,则应该在程序中延时6个。延时的抖动在所有的实时操作系统中都会存在,其根本原因可能在于:一是CPU负荷太重;二是系统设计可能不正确。一般解决此类问题的方法主要有如下几种:

(1)提高时钟节拍的频率;

(2)提高CPU微处理器的时钟频率;

(3)重新安排任务的优先级;

(4)避免使用浮点运算(如果非使用不可,尽量用单精度数);

(5)使用能较好地优化程序代码的编译器;

(6)时间要求苛刻的代码用汇编语言编写;

(7)选择处理速率更快的CPU。

4.3.2时钟节拍程序

μC/OS-Ⅱ中的时钟节拍服务是通过在时钟节拍中断服务子程序OSTickISR()中调用节拍服务子程序OSTimeTick()来实现的,在OSTimeTick()中还会调用OSTimeTickHook()函数,即时钟节拍用户扩展函数,该函数保留为用户自己编写,可以扩展应用。

1.时钟节拍中断服务子程序

时钟节拍中断服务子程序的示意代码如程序清单4.3所示。

程序清单4.3时钟节拍中断服务子程序的示意代码

voidOSTickISR(void){

保存处理器寄存器的值;

调用OSIntEnter()或是将OSIntNesting加1; /*通知内核进入中断 */

if(OSIntNesting==1)OSTCBCur->OSTCBStkPtr=StackPointer,调用OSTimeTick(); /*调节拍服务程序 */

清时钟中断;

重新允许中断;

调用OSIntExit(); /*通知内核推出中断 */

恢复处理器寄存器的值;

执行中断返回指令;

}时钟节拍中断服务子程序的特点如下:

(1)每个时钟周期中断一次,由它调用节拍服务函数OSTimeTick();

(2)时钟节拍中断服从μC/OS-Ⅱ所描述的全部规则;

(3)这段代码必须用汇编语言编写,因为在C语言里不能直接处理CPU的寄存器。

2.节拍服务子程序

节拍服务子程序OSTimeTick()的主要工作是:给每个用户任务控制块OS_TCB中的时间延时项OSTCBDly减1,直到等于0,执行时间直接与任务个数成正比。节拍服务子程序代码如程序清单4.4所示。

程序清单4.4OSTimeTick()代码

voidOSTimeTick(void){

OS_TCB*ptcb; /*定义一个事件控制块类型的指针 */

OSTimeTickHook(); /*调用由用户根据自己需要编写的扩展函数 */

ptcb=OSTCBList; /*将指针指向第一个事件控制块 */

while(ptcb->OSTCBPrio!=OS_IDLE_PRIO){ /*从第一个事件控制块起,做到空闲任务沿*//*事件控制块链表查找任务,直到空闲任务*/

OS_ENTER_CRITICAL(); /*关中断,保护全局变量 */

if(ptcb->OSTCBDly!=0){ /*延时项若不为0则继续,若为0则跳出 */

if(--ptcb->OSTCBDly==0){ /*若延时项减1后为0则继续,否则转出 */

if(!(ptcb->OSTCBStat&OS_STAT_SUSPEND)){ /*检查任务是否是被任务挂起函数挂起的,不是则任务不会进入就绪 */OSRdyGrp|=ptcb->OSTCBBitY;/*将任务置于就绪状态 */

OSRdyTbl[ptcb->OSTCBY]|=ptcb->OSTCBBitX;

}else{ptcb->OSTCBDly=1;} /*若是被任务挂起函数挂起的,则将延时项置1,继续保持挂起状态 */

}

}ptcb=ptcb->OSTCBNext; /*指针指向下一个事件控制块,继续查找 */

OS_EXIT_CRITICAL(); /*开中断 */

}

OS_ENTER_CRITICAL(); /*关中断,保护所要操作的全局变量 */

OSTime++; /*累计时钟总数 */

OS_EXIT_CRITICAL(); /*开中断 */

}4.3.3时钟节拍器的正确用法

μC/OS-Ⅱ时钟节拍的正确用法是应该在多任务系统启动以后再开启时钟节拍器,也即调用OSStart()后的第一件事就是开放时钟节拍中断,这段代码可以由用户嵌入到OSStart()函数内。反之,若在OSInit()与OSStart()之间开放时钟节拍器中断,时钟节拍中断就有可能在μC/OS-Ⅱ启动第一个任务之前发生,此时μC/OS-Ⅱ处在一种不确定的状态之中。用户应用程序有可能因此而崩溃。下面是一个错误用法的示意性程序清单(程序清单4.5),它在多任务调度前就开放了时钟中断。

程序清单4.5错误的时钟节拍器用法

voidmain(void){

OSInit(); /*初始化μC/OS-Ⅱ */

应用程序初始化代码;

调用OSTaskCreate()函数至少创建一个任务;

允许时钟节拍中断; /*绝对不要在这里允许时钟节拍中断!!! */

OSStart(); /*开始多任务调度 */

} 4.4μC/OS-Ⅱ的时间管理

μC/OS-Ⅱ有五个与时钟节拍有关的系统服务,它们分别如下:

(1) OSTimeDly():任务延时函数。

(2) OSTimeDlyHMSM():按时分秒毫秒延时函数。

(3) OSTimeDlyResume():让处在延时期的任务结束延时。

(4) OSTimeGet():获得系统时间。

(5) OSTimeSet():设置系统时间。

这些函数属于OS_TIME.C文件。要调用这些函数,必须首先在OS_CFG.H文件中设置配置常量,见表4.1。表4.1OS_CFG.H中与时间管理相关的配置常量一览表4.4.1任务延时函数——OSTimeDly()

μC/OS-Ⅱ的任务是一个无限循环,由于μC/OS-Ⅱ是可剥夺型内核,如果高优先级任务不主动挂起,低优先级任务就永远无法取得运行权,最高优先级任务将独占CPU的使用权。因此,μC/OS-Ⅱ规定:除了永不挂起的空闲任务外,其它所有的任务都要在合适的时候调用系统服务函数,自我挂起,暂时放弃CPU使用权,使低优先权任务能够得以运行。这种系统服务函数就包括这一小节将要介绍的任务延时函数OSTimeDly()和下一小节将要介绍的按时分秒毫秒延时函数OSTimeDlyHMSM()。

1.函数原型

函数原型如下:

voidOSTimeDly(INT16Uticks)

OSTimeDly()函数申请一个系统提供的延时,延时的单位是一个时钟节拍,最大可延时65535个时钟节拍。

调用该函数后,若延时时间不为0,则立即挂起当前任务,µC/OS-Ⅱ进行一次任务调度,并且执行下一个优先级最高的就绪态任务。任务调用OSTimeDly()后,一旦规定的时间期满或者有其它的任务通过调用OSTimeDlyResume()取消了延时,该任务就会马上进入就绪状态。当然,只有当该任务在所有就绪任务中具有最高的优先级时,它才会立即运行。由于中断不能延时,因此它的调用者一定是任务。

函数只有一个参数ticks,它表示要延时的时钟节拍数,无返回值。

2.注意事项

在调用OSTimeDly()函数时必须注意以下事项:

(1)时间的长短是用时钟节拍的数目来确定的。

(2)可提供的时钟节拍数范围是1~65535。

(3)参数为0,表明不进行延时操作,而立即返回调用者。

(4)为了确保设定的延时时间,建议设定的时钟节拍数加1。

(5)只能在任务中调用,无开关量。

3.程序流程和源代码

OSTimeDly()函数程序流程如图4.10所示,源代码如程序清单4.6。图4.10任务延时函数流程图

程序清单4.6OSTimeDly()函数的源代码

voidOSTimeDly(INT16Uticks){

if(ticks>0){/*如果加入指定一个0值,则表示不想对任务延时,函数立即返回调用者 */

OS_ENTER_CRITICAL(); /*关中断 */

if((OSRdyTbl[OSTCBCur->OSTCBY]&=~OSTCBCur->OSTCBBitX)==0){/*从就绪表中移出当前

任务*/

OSRdyGrp&=~OSTCBCur->OSTCBBitY;}OSTCBCur->OSTCBDly=ticks;/*保存节拍数,每隔一个时钟节拍,这个成员变量数减1 */

OS_EXIT_CRITICAL(); /*关中断*/

OSSched(); /*当前任务已经挂起,任务调度程序执行下一个优先级最高的就绪任务 */

}

}4.4.2按时、分、秒、毫秒延时函数——OSTimeDlyHMSM()

1.函数原型

函数原型如下:

INT8UOSTimeDlyHMSM(INT8Uhour,INT8Uminutes,INT8Useconds,INT8Umilli)

这是一个十分有用的函数,其功能是以时、分、秒、毫秒为单位进行延时。调用者只能是任务,调用后,如果延时时间不为0,系统将立即挂起当前任务,并进行任务调度。

μC/OS-Ⅱ可以将任务延时长达256个小时(接近11天)。该函数需要如下四个参数:

(1) hours:延时小时数,范围为0~255。

(2) mintues:延时分钟数,范围为0~59。

(3) seconds:延时秒数,范围为0~59。

(4) milli:延时毫秒数,范围为0~999。

2.返回值

OSTimeDlyHMSM()函数的返回值为下述内容之一:

(1) OS_ON_ERR:调用成功。

(2) OS_TIME_INVALID_MINUTES:参数错误,分钟数大于59。

(3) OS_TIME_INVALID_SECONDS:参数错误,秒数大于59。

(4) OS_TIME_INVALID_MILLI:参数错误,毫秒数大于999。

(5) OS_TIME_ZERO_DLY:四个参数全为0,表示不进行任何操作而直接返回。

3.注意事项

(1)要使用该函数,首先要用OS_CPU.H文件中定义的全局常数OS_TICKS_PER_SEC将时间转换为时钟节拍数。这个全局常数表示的是每秒钟时钟节拍器产生的节拍数量,称为时钟节拍频率,其值一般设置在10~100Hz之间。

(2)四个参数全为0,表示不进行任何操作,直接返回。

(3)当时钟周期大于等于1ms时,计时最小单位是一个时钟节拍,精度是0.5个节拍。例如,若将时钟节拍频率(OS_TICKS_PER_SEC)设置成100Hz(10ms),4ms的延时不会产生任何延时,而5ms的延时就等于延时10ms。

(4)当时钟周期小于1ms时,最小计数值为OS_TICKS_PER_SEC/1000个时钟节拍,分辨率为500L/OS_TICKS_PER_SEC/1000L。例如,OS_TICKS_PER_SEC = 1000,最小计数单位是1ms,精度为0.5(个最小计算单位)。

(5) µC/OS-Ⅱ支持的延时最长为65535个节拍。要想支持更长时间的延时,需采用一定的算法,一般的做法是将延时时钟数分割为两部分:一部分是65536个节拍的整数倍;另一部分是总数减去这个整数倍节拍后剩下的节拍数,然后先算剩下的节拍数,再算这个整数倍节拍数。例如,若OS_TICKS_PER_SEC的值为100,用户想延时15min,则OSTimeDlyHMSM()会延时15 × 60 × 100 = 90000个时钟。这个延时会被分割成两次32768个节拍的延时(因为用户只能延时65535个节拍而不是65536个节拍)和一次24464个节拍的延时。在这种情况下,OSTimeDlyHMSM()首先计算24464个节拍,然后计算两次32768个节拍。

(6)由于受到OSTimeDlyHMSM()具体实现方法的限制,用户不能用函数OSTimeDlyResume()结束延时调用OSTimeDlyHMSM()要求延时超过65535个节拍的任务。假如,时钟节拍的频率是100Hz,用户就不能调用OSTimeDlyHMSM(0,10,55,350)或更长延迟时间的任务。

(7)只能在任务中调用,开关量是OS_TIME_DLY_HMSM_EN。

4.源代码及其注释

OSTimeDly()函数的源代码如程序清单4.7所示。

程序清单4.7OSTimeDlyHMSM()函数的源代码

INT8UOSTimeDlyHMSM(INT8Uhours,INT8Uminutes,INT8Useconds,INT16Umilli)

{

INT32Uticks;

INT16Uloops;

if(hours>0||minutes>0||seconds>0||milli>0){ /*条件检查,全为0,则跳出 */

if(minutes>59)return(OS_TIME_INVALID_MINUTES);if(seconds>59)return(OS_TIME_INVALID_SECONDS);

if(milli>999) return(OS_TIME_INVALID_MILLI);

ticks=(INT32U)hours*3600L*OS_TICKS_PER_SEC

+(INT32U)minutes*60L*OS_TICKS_PER_SEC

+(INT32U)seconds*OS_TICKS_PER_SEC

+OS_TICKS_PER_SEC*((INT32U)milli

+500L/OS_TICKS_PER_SEC)/1000L; /*计算时钟节拍数,其精度为0.5个时钟 */loops=ticks/65536L; /*从这里开始,按注意事项第5条所述方法开始延时 */

ticks=ticks%65536L;

OSTimeDly(ticks);

while(loops>0){

OSTimeDly(32768);

OSTimeDly(32768);

loops--;

}

return(OS_NO_ERR);

}else{

return(OS_TIME_ZERO_DLY);

}

}

5.范例

必须注意的是:必须在OS_CFG.H文件中设置OS_TICKS_PER_SEC。OSTimeDlyHMSM()函数的使用范例如下:

viodtask(void*pdata){

for(;;){

应用程序;

OSTimeDlyHMSM(0,0,5,0); /*延时5s */

}

}4.4.3让处在延时期的任务结束延时函数——OSTimeDlyResume()

1.函数原型

函数原型如下:

INT8U OSTimeDlyResume(INT8Uprio)

OSTimeDlyResume()函数用于唤醒一个用OSTimeDly()或OSTimeDlyHMSM()函数延时的任务。正在延时的任务可以通过其它任务调用该函数取消延时来使自己处于就绪态,而不必等待延时期满。该函数还可以唤醒正在等待事件的任务,但不推荐使用这种方法。另外,如果任务是通过等待信号量、消息邮箱或消息队列来延时自己的,那么可以简单地通过控制信号量、消息邮箱或消息队列来恢复任务。这种情况存在的唯一问题是可能会多占用一些内存,因为它要求用户分配事件控制块。

不能唤醒一个用OSTimeDlyHMSM()延时且总延时时间超过65535个时钟节拍的任务。

只能在任务中调用,开关量是OS_TIME_DLY_RESUME_EN。

函数只需要1个参数prio,即指定要唤醒任务的优先级。

2.返回值

OSTimeDlyResume()函数的返回值为下述内容之一:

(1) OS_ON_ERR:调用成功。

(2) OS_PRIO_INVALID:参数指定的优先级大于OS_LOWEST_PRIO。

(3) OS_TASK_NOT_DLY:要唤醒的任务不在挂起状态。

(4) OS_TASK_NOT_EXIST:指定的任务不存在。

3.源代码及其注释

OSTimeDlyResume()函数的源代码如程序清单4.8所示。

程序清单4.8OSTimeDlyResume()函数的源代码

INT8UOSTimeDlyResume(INT8Uprio)

{

#ifOS_CRITICAL_METHOD=

=3

OS_CPU_SRcpu_sr;

#endif

OS_TCB*ptcb;

if(prio>=OS_LOWEST_PRIO)return(OS_PRIO_INVALID);/*确保任务优先级是有效的*/

OS_ENTER_CRITICAL();

ptcb=(OS_TCB*)OSTCBPrioTbl[prio];

if(ptcb!=(OS_TCB*)0){ /*确保所要恢复的任务存在 */

if(ptcb->OSTCBDly!=0){ /*确保任务在等待延时期满 */

ptcb->OSTCBDly=0; /*将.OSTCBDly设置为0,从而取消延时 */

if(!(ptcb->OSTCBStat&OS_STAT_SUSPEND)){ /*检查任务是否处于挂起状态 */

OSRdyGrp|=ptcb->OSTCBBitY;/*若挂起,则将任务置于就绪*/

OSRdyTbl[ptcb->OSTCBY

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论