导 航┆主 站┆文 章┆资 料┆下 载┆美 食┆读 书┆笑 话┆博 客┆论 坛
>> Tony嵌入式开发论坛静态版首页
查看完全版本:Tony嵌入式开发论坛

 

    [转帖]VxWorks下几种定时/延时方法的小结[原创]
    作者:Tony 时间:2005-8-10 16:31:49

    hy_p

    编程过程中,出于需要,大家或多或少要用到定时/延时。VxWorks下提供了几种定时/延时机制,根据收集的一些资料和VxWorks相关文档,在学习和上机实验的基础上,对它们的使用以及我所遇到的问题做一个总结,希望对大家能有所帮助。不正确之处,恳请斧正。
    1 taskDelay
    taskdelay()提供了一个简单的任务睡眠机制,也常用于需要定时/延时机制的应用中。它的格式是STATUS taskDelay(int ticks /* number of ticks to delay task */),可以看出使用该函数实现延时的单位为tick(一般系统中一个tick都是ms级的)。在VxWorks下可以这样使用taskDelay()函数:taskDelay(sysClkRateGet()*1)。函数sysClkRateGet()返回系统的时钟速率,单位是tick数/每秒(利用函数sysClkRateSet()可以改变系统的时钟速率)。在POSIX中有一个与taskdelay()相对应的函数――nanosleep()(下文中有介绍)。这两个函数仅仅是延时单位不同,效果是相同的。
    利用taskdelay(),可以将调用的任务移动到具有相同优先级的就绪队列尾部。特别的,可以通过调用taskdelay(0),将cpu交给系统中其他相同优先级的任务。延时为0的调用只能用于taskdelay()中,nanosleep()认为这种调用是错误的。
    taskdelay()会导致调用的任务在指定的延时期间(以ticks计数)放弃cpu,使任务处于DELAY状态(因此,其不能用于中断服务程序中)。通常其受到任务调度的影响,但在等待一些与中断无关联的外部条件时,其是有用的。如果调用的任务受到一个信号,指出其没有被阻塞或被忽略,taskDelay()将返回ERROR,并在信号处理程序运行后设置errno为EINTR。
    2 WatchDog
    VxWorks提供了一个看门狗定时器(watchdog timer)机制,利用提供的函数,任何任务都可以创建一个看门狗定时器,经过指定的延时后,实现在系统时钟ISR的上下文中运行指定的程序。在VxWorks中,看门狗定时器作为系统时钟中断服务程序的一部分来维护。因此,与看门狗定时器相联系的函数运行在系统时钟中断级。在使用看门狗定时器实现定时/延时时,经过指定的时间,应用程序中断在系统时钟中断优先级上面,然而,如果内核不能立即执行这个中断服务程序,这个任务就被排队到tExcTask 工作队列中去,处在 tExcTask 工作队列中的任务的优先级为 tExcTask (通常是 0)。对于使用看门狗定时器的中断服务程序,仍然必须遵守一般的ISR所要遵守的规则。
    通过wdCreate( )可以创建一个看门狗定时器。调用wdStart()启动定时器,延时参数以tick为单位,同时还要指定定时完成后要调用的程序。如果你的应用程序需要多个看门狗函数,使用wdCreate( )为每个需求产生独立的看门狗ID。因为对于给定的看门狗ID,只有最近的wdStart()有效。在指定的tick计数到达之前,要取消一个看门狗计时器,可以调用wdCancel()实现。
    每调用一次wdStart(),看门狗定时器只执行一次。对于一些要求周期性执行的应用程序。要获得该效果,定时器函数本身必须通过递归调用wdStart()来重新启动定时器。
    利用看门狗定时器,调用的任务不会被阻塞:因为wdStart()调用是立即返回的。使用该方法实现延时,关联函数所受限制太大,好多系统调用不可用,郁闷也…
    3 timer(POSIX)
    3.1 timer
    VxWorks提供IEEE的POSIX 1003.1b标准定时器接口。使用这种定时器机制,在指定的时间间隔后,任务向自身发信号。定时器是建立在时钟和信号之上。
    程序可以创建创建、设置和删除一个定时器。当定时器到达期限,将向任务发送默认的信号(SIGALRM)。
    使用timer的一般流程:
    /* 创建定时器 */
    if(timer_create(CLOCK_REALTIME,0,&mytimer)==ERROR)
    return(ERROR);
    /* 用户程序与定时器相连 */
    if(timer_connect(mytimer,(VOIDFUNCPTR)my_handler,0)==ERROR)
    return(ERROR);
    /* 设置定时器值 */
    if(timer_settime(mytimer,0,&value,0)==ERROR)
    return(ERROR);
    /* 一段延时 */
    。。。。。。
    /* 删除定时器 */
    if (timer_delete(mytimer)==ERROR)
    return(ERROR);
    在使用定时器时,容易忽略定时结束后任务向自身发信号这一处理步骤。即在定时结束后,要向创建定时器的任务发送信号,如果此时任务已不存在,定时程序将不能执行,提示的错误是“interrupt: timerWdHandler : kill failed (timer=******,tid=******,errno=0x16)”(有一个《关于timer(定时器)中几个函数的疑问!》帖子,其中提到该问题)。在上机时,我设置了一个较长时间的定时器,在创建定时器任务中使用相等的延时操作(taskDelay()、nanosleep()随便哪个都行),此时程序正常运行,定时结束后正确地执行关联到定时器的程序;再次运行,在定时结束前,我在shell下删除掉创建定时器的任务,定时结束后又出现上述的错误。因此,使用POSIX定时器时,一定要注意不能让创建定时器的任务在触发定时程序之前结束,否则。。。准备reset吧
    3.2 nanosleep()
    函数nanosleep()的功能与VxWorks提供的taskDelay()类似,nanosleep()允许指定以秒和纳秒为单位的定时/延时时间,taskDelay()以tick作为定时/延时时间。两者只是延时单位不同,而不是精度不同,都由系统时钟频率决定。与taskDelay()一样,使用nanosleep()实现定时/延时的任务也受调度的影响,在实际操作中一定要注意这一点。
    使用nanosleep()时,定时参数使用的是结构itimerspe中的it_value,可以指定it_value中的秒和纳秒为定时/延时时间。函数的使用较为简单,不再赘述。
    4 其它
    使用带有超时值timeout的msgQReceive()、semTask()也可以实现延时(有使用这种方式来进行延时的人吗?不有吗?有吗?不有吗?有吗?不有吗?。。。)。
    该方法将阻塞任务,让其等待超时,利用该方法实现延时可以使用函数semTask(…,timeout)、msgQReceive(…,…,…,timeout)。调用任务被放进阻塞队列中(注意,使用semTask和msgQReceive时,time的值不能是NO_WAIT,如果指定NO_WAIT的话,将立即返回,也就达不到延时的效果了^_^)。如果你希望任务不被阻塞,还能继续执行,sorry,此法不行,使用看门狗吧。
    当阻塞的任务满足继续执行的条件后,将被放入ready队列。这里要注意的是,任务被放入ready队列并不意味着立刻就被执行。想想看,如果比它高优先级的任务正在占用资源,或也在等资源,那么…延时将是不确定的。

    -------------------------------------------------------------------------------------------------------------------------------------------------------------------


    作者:foreverlos 时间:2005-8-28 14:07:54

    多谢分享

    -------------------------------------------------------------------------------------------------------------------------------------------------------------------


    作者:ValorTiger 时间:2005-8-10 16:51:07

    太好了,谢谢搂主。

    -------------------------------------------------------------------------------------------------------------------------------------------------------------------


    作者:Tony 时间:2005-8-10 16:34:04

    [总结+原创]vxworks下延时方法小结

    http://bbs.edw.com.cn/dispbbs.asp?BoardID=3&ID=29331

    yyqwin


    最近,所作的工程需要1ms的定时器,我参考了精华区的相关文

    章并在各位大侠的帮助下,我终于实现了1ms的精确定时,现在看

    到还有网友在求助相关问题,我这儿就小结一下,希望对大家有所

    帮助。

    1、taskDelay()
    taskdelay()是最简单的延时方法,他的单位是tick。它的函数

    原型是
    STATUS taskDelay
    (
    int ticks /* number of ticks to delay

    task */
    )

    其中,tick是vxworks下时钟中断的频率,可以通过

    sysClkRateSet( ) 设置单位ticks数/秒,一般在60-100之间。通

    过sysClkRateGet()可以得到现在系统的时钟频率。

    在VxWorks下可以这样使用taskDelay()函数:
    ...
    taskDelay(sysClkRateGet()*5);/* 延时5s */
    ...
    利用taskdelay(),可以将调用的任务从就绪态转到睡眠态但是不

    能用于中断服务程序中。特别的,可以通过调用taskdelay(0),将

    cpu交给系统中其他相同优先级的任务。

    由于其延时的单位是tick,所以其延时精度并不高,但对于延时

    10ms以上的系统足够了。
    2 WatchDog
    VxWorks提供了一个看门狗定时器,他的主要作用是防止程序跑

    飞,但是利用看门狗同样可实现延时。
    利用提供的函数,任何任务都可以通过wdCreate( )创建一个看门

    狗定时器,经过指定的延时后,实现在系统时钟ISR的上下文中运

    行指定的程序。在VxWorks中,看门狗定时器作为系统时钟中断服

    务程序的一部分来维护。因此,与看门狗定时器相联系的函数运行

    在系统时钟中断级,延时单位为tick。如果你的应用程序需要多个

    看门狗函数,使用wdCreate( )为每个需求产生独立的看门狗ID。

    因为对于给定的看门狗ID,只有最近的wdStart()有效。
    ...

    void windDemo(void)
    {
    semHighId=semBCreate(SEM_Q_PRIORITY,SEM_FULL);
    semLowId=semBCreate(SEM_Q_PRIORITY,SEM_FULL);

    wdHighId=wdCreate();
    wdLowId=wdCreate();
    windDemoId=taskIdSelf();

    FOREVER
    {

    highPriId=taskSpawn("tHighPri",100,VX_SUPERVISOR_MODE,4000

    ,(FUNCPTR)taskHighPri,0,0,0,0,0,0,0,0,0,0);

    lowPriId=taskSpawn("tLowPri",150,VX_SUPERVISOR_MODE,4000,(

    FUNCPTR)taskLowPri,0,0,0,0,0,0,0,0,0,0);

    taskSuspend(0);
    }
    }

    LOCAL void taskHighPri(void)
    {
    for(;;)
    {
    printf("This is from taskHighPri

    Task!\n");
    wdStart (wdHighId, 0.05*sysClkRateGet(),

    (FUNCPTR) HighCollectRoutine, (int) wdHighId);
    semTake (semHighId, WAIT_FOREVER);
    }
    }

    LOCAL void taskLowPri(void)
    {
    for(;;)
    {
    printf("This is from taskLowPri Task

    !\n");
    wdStart (wdLowId, sysClkRateGet(),

    (FUNCPTR) LowCollectRoutine, (int) wdLowId);
    semTake (semLowId, WAIT_FOREVER);
    }
    }

    void HighCollectRoutine(WDOG_ID myWdId)
    {
    wdStart (myWdId, 0.05*sysClkRateGet(), (FUNCPTR)

    HighCollectRoutine, (int) myWdId);
    semGive(semHighId);
    }

    void LowCollectRoutine(WDOG_ID myWdId)
    {
    wdStart (myWdId, sysClkRateGet(), (FUNCPTR)

    LowCollectRoutine, (int) myWdId);
    semGive(semLowId);
    }


    适用于延时10ms以上的系统。
    利用看门狗定时器,调用的任务不会被阻塞:因为wdStart()调用

    是立即返回的。
    3 辅助时钟

    要想达到1ms甚至us级的延时就需要使用辅助时钟。cpu上至少有两

    个timer,timer1用于系统时钟,处理系统级任务,时钟频率一般

    不能太高。timer2用于辅助时钟,利用它可以得到精确的延时。
    ...
    #define AUX_CLK_MAX 1000

    void windDemo(void)
    {
    semHighId=semBCreate(SEM_Q_PRIORITY,SEM_FULL);

    windDemoId=taskIdSelf();

    FOREVER
    {

    highPriId=taskSpawn("tHighPri",100,VX_SUPERVISOR_MODE,4000

    ,(FUNCPTR)taskHighPri,0,0,0,0,0,0,0,0,0,0);


    taskSuspend(0);
    }
    }

    void InterISR(void)
    {
    semGive(semHighId);
    }

    void test(void)
    {
    sysAuxClkDisable();

    /* Disable system aux clock */
    sysAuxClkConnect((FUNCPTR)InterISR, 0);
    sysAuxClkRateSet(AUX_CLK_MAX); /* Set system

    aux clock rate */
    sysAuxClkEnable();
    }

    LOCAL void taskHighPri(void)
    {
    test();
    for(;;)
    {
    printf("This is from taskHighPri

    Task!\n");
    semTake (semHighId, WAIT_FOREVER);
    }
    }

    其他方法,还望各位大侠补充!

    emailtony

    用高精度时钟,

    sysTimeStamp();

    void high_res_delay( int microsecond )

    { int t, t1,t2;

    t1 = sysTimestamp();

    while( microsecnod--) { t = 0;

    while(t < timer_ticks_per_usec) {

    t2 = sysTimestamp();

    if( t2>t1) t += (t2-t1);

    t1 = t2

    }

    }

    Many top drivers are using such style codes to solve high resolution delay or timing.

    -------------------------------------------------------------------------------------------------------------------------------------------------------------------



查看完全版本:Tony嵌入式开发论坛
Copyright 2006-2008 Cevx.Com Cevx.Net 制作 版权所有
网友发帖仅代表个人观点,与本论坛立场无关