timer.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2001-2006 by egnite Software GmbH. All rights reserved.
00003  *
00004  * Redistribution and use in source and binary forms, with or without
00005  * modification, are permitted provided that the following conditions
00006  * are met:
00007  *
00008  * 1. Redistributions of source code must retain the above copyright
00009  *    notice, this list of conditions and the following disclaimer.
00010  * 2. Redistributions in binary form must reproduce the above copyright
00011  *    notice, this list of conditions and the following disclaimer in the
00012  *    documentation and/or other materials provided with the distribution.
00013  * 3. Neither the name of the copyright holders nor the names of
00014  *    contributors may be used to endorse or promote products derived
00015  *    from this software without specific prior written permission.
00016  *
00017  * THIS SOFTWARE IS PROVIDED BY EGNITE SOFTWARE GMBH AND CONTRIBUTORS
00018  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00019  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00020  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL EGNITE
00021  * SOFTWARE GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00022  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00023  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
00024  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
00025  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
00026  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
00027  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
00028  * SUCH DAMAGE.
00029  *
00030  * For additional information see http://www.ethernut.de/
00031  */
00032 
00196 #include <cfg/os.h>
00197 #include <dev/irqreg.h>
00198 
00199 #include <sys/types.h>
00200 #include <sys/atom.h>
00201 #include <sys/heap.h>
00202 #include <sys/thread.h>
00203 #include <sys/timer.h>
00204 
00205 #ifdef NUTDEBUG
00206 #include <sys/osdebug.h>
00207 #endif
00208 
00209 #ifdef NUTTRACER
00210 #include <sys/tracer.h>
00211 #endif
00212 
00213 #if defined (__linux__) || defined(__APPLE__) || defined(__CYGWIN__)
00214 #include <sys/time.h>
00215 
00216 static struct timeval   timeStart;
00217 #endif
00218 
00223 
00227 NUTTIMERINFO *nutTimerList;
00228 
00229 /*
00230  * Last processing time of elapsed timers. 
00231  */
00232 static u_long nut_ticks_resume; 
00233 
00237 volatile u_long nut_ticks;
00238 
00239 // volatile u_long nut_tick_dist[32];
00240 
00244 #if defined(NUT_DELAYLOOPS)
00245 volatile u_long nut_delay_loops = NUT_DELAYLOOPS;
00246 #else
00247 volatile u_long nut_delay_loops;
00248 #endif
00249 
00253 //Not Used u_long nut_delay_loops_clk;
00254 
00258 #if !(defined (__linux__) || defined(__APPLE__) || defined(__CYGWIN__))
00259 #ifdef USE_TIMER
00260 SIGNAL( SIG_TIMER ) 
00261 #else
00262 static void NutTimerIntr(void *arg)
00263 #endif
00264 {
00265     nut_ticks++;
00266     // nut_tick_dist[TCNT0]++;
00267 }
00268 #endif
00269 
00270 
00278 void NutTimerInit(void)
00279 {
00280 #if defined (__linux__) || defined(__APPLE__) || defined(__CYGWIN__)
00281     gettimeofday( &timeStart, NULL );
00282 #else
00283     NutRegisterTimer(NutTimerIntr);
00284     NutEnableTimerIrq();
00285 //Not Used     /* Remember the CPU clock for which the loop counter is valid. */
00286 //Not Used     nut_delay_loops_clk = NutGetCpuClock();
00287 #if !defined(NUT_DELAYLOOPS)
00288 #ifndef NUT_TICK_FREQ
00289 #define NUT_TICK_FREQ   1000UL
00290 #endif
00291     {
00292         /* Wait for the next tick. */
00293         u_long cnt = NutGetTickCount();
00294         while (cnt == NutGetTickCount());
00295         /* Wait for the next tick, counting loops. */
00296         cnt = NutGetTickCount();
00297         while (cnt == NutGetTickCount()) {
00298             nut_delay_loops++;
00299         }
00300         /* 
00301          * The loop above needs more cycles than the actual delay loop.
00302          * Apply the correction found by trial and error. Works acceptable
00303          * with GCC for Ethernut 1 and 3.
00304          */
00305         nut_delay_loops *= 103UL;
00306         nut_delay_loops /= 26UL;
00307     }
00308 #endif
00309 #endif
00310 }
00311 
00327 void NutMicroDelay(u_long us)
00328 {
00329 #if defined (__linux__) || defined(__APPLE__) || defined(__CYGWIN__)
00330     usleep(us);
00331 #else
00332     register u_long cnt = nut_delay_loops * us / 1000;
00333 
00334     while (cnt--) {
00335         _NOP();
00336     }
00337 #endif
00338 }
00339 
00351 void NutDelay(u_char ms)
00352 {
00353     NutMicroDelay((u_long)ms * 1000);
00354 }
00355 
00365 void NutTimerInsert(NUTTIMERINFO * tn)
00366 {
00367     NUTTIMERINFO *tnp;
00368 
00369     tn->tn_prev = NULL;
00370     for (tnp = nutTimerList; tnp; tnp = tnp->tn_next) {
00371         if (tn->tn_ticks_left < tnp->tn_ticks_left) {
00372             tnp->tn_ticks_left -= tn->tn_ticks_left;
00373             break;
00374         }
00375         tn->tn_ticks_left -= tnp->tn_ticks_left;
00376         tn->tn_prev = tnp;
00377     }
00378     tn->tn_next = tnp;
00379     if (tn->tn_next) {
00380         tn->tn_next->tn_prev = tn;
00381     }
00382     if (tn->tn_prev) {
00383         tn->tn_prev->tn_next = tn;
00384     }
00385     else {
00386         nutTimerList = tn;
00387     }
00388 }
00389 
00396 void NutTimerProcessElapsed(void)
00397 {
00398     NUTTIMERINFO *tn;
00399     u_long ticks;
00400     u_long ticks_new;
00401 
00402     // calculate ticks since last call
00403     ticks = NutGetTickCount();
00404     ticks_new = ticks - nut_ticks_resume;
00405     nut_ticks_resume = ticks;
00406     
00407     // process timers
00408     while (nutTimerList && ticks_new){
00409         
00410         tn = nutTimerList;
00411 
00412         // subtract time
00413         if (ticks_new < tn->tn_ticks_left) {
00414             tn->tn_ticks_left -= ticks_new;
00415             ticks_new = 0;
00416         } else {
00417             ticks_new -= tn->tn_ticks_left;
00418             tn->tn_ticks_left = 0;
00419         }
00420         
00421         // elapsed
00422         if (tn->tn_ticks_left == 0){
00423 
00424             // callback
00425             if (tn->tn_callback) {
00426                 (*tn->tn_callback) (tn, (void *) tn->tn_arg);
00427             }
00428             // remove from list
00429             nutTimerList = nutTimerList->tn_next;
00430             if (nutTimerList) {
00431                 nutTimerList->tn_prev = NULL;
00432             }
00433             if ((tn->tn_ticks_left = tn->tn_ticks) == 0) {
00434                 NutHeapFree(tn);
00435             }
00436             else {
00437                 // re-insert
00438                 NutTimerInsert(tn);
00439             }
00440         }
00441     }
00442 }
00443 
00463 NUTTIMERINFO * NutTimerCreate(u_long ticks, void (*callback) (HANDLE, void *), void *arg, u_char flags)
00464 {
00465     NUTTIMERINFO *tn;
00466     
00467     tn = NutHeapAlloc(sizeof(NUTTIMERINFO));
00468     if (tn) {
00469         tn->tn_ticks_left = ticks + NutGetTickCount() - nut_ticks_resume;
00470         
00471         /*
00472          * Periodic timers will reload the tick counter on each timer 
00473          * intervall.
00474          */
00475         if (flags & TM_ONESHOT) {
00476             tn->tn_ticks = 0;
00477         } else {
00478             tn->tn_ticks = ticks;
00479         }
00480         
00481         /* Set callback and callback argument. */
00482         tn->tn_callback = callback;
00483         tn->tn_arg = arg;
00484     }
00485     return tn;    
00486 }
00487 
00514 HANDLE NutTimerStartTicks(u_long ticks, void (*callback) (HANDLE, void *), void *arg, u_char flags)
00515 {
00516     NUTTIMERINFO *tn;
00517 
00518     tn = NutTimerCreate( ticks, callback, arg, flags);
00519     if (tn) {
00520         /* Add the timer to the list. */
00521         NutTimerInsert(tn);
00522     }
00523     return tn;
00524 }
00525 
00554 HANDLE NutTimerStart(u_long ms, void (*callback) (HANDLE, void *), void *arg, u_char flags)
00555 {
00556         return NutTimerStartTicks(NutTimerMillisToTicks(ms), callback, arg, flags);
00557 }
00558 
00580 void NutSleep(u_long ms)
00581 {
00582     if (ms) {
00583 
00584         /* remove running thread from runQueue */
00585         NutThreadRemoveQueue(runningThread, &runQueue);
00586         runningThread->td_state = TDS_SLEEP;
00587 
00588         if ((runningThread->td_timer = NutTimerStart(ms, NutThreadWake, runningThread, TM_ONESHOT)) != 0) {
00589 #ifdef NUTTRACER
00590             TRACE_ADD_ITEM(TRACE_TAG_THREAD_SLEEP,(int)runningThread);
00591 #endif
00592             NutThreadResume();
00593         } else
00594         {
00595             /* timer creation failed, restore queues */
00596             runningThread->td_queue = &runQueue;
00597             runningThread->td_qnxt  = runQueue;
00598             runningThread->td_state = TDS_RUNNING;
00599             runQueue = runningThread;
00600         }
00601     } else
00602         NutThreadYield();
00603 }
00604 
00617 void NutTimerStop(HANDLE handle)
00618 {
00619     NUTTIMERINFO *tn = (NUTTIMERINFO *)handle;
00620 
00621     /* Disable periodic operation and callback. */
00622     tn->tn_ticks = 0;
00623     tn->tn_callback = NULL;
00624     /* If not already elapsed, expire the timer. */
00625     if (tn->tn_ticks_left) {
00626         if (tn->tn_prev) {
00627             tn->tn_prev->tn_next = tn->tn_next;
00628         }
00629         else {
00630             nutTimerList = tn->tn_next;
00631         }
00632         if (tn->tn_next) {
00633             tn->tn_next->tn_prev = tn->tn_prev;
00634             tn->tn_next->tn_ticks_left += tn->tn_ticks_left;
00635         }
00636         tn->tn_ticks_left = 0;
00637         NutTimerInsert(tn);
00638     }
00639 }
00640 
00649 u_long NutGetTickCount(void)
00650 {
00651     u_long rc;
00652 
00653 #if defined (__linux__) || defined(__APPLE__) || defined(__CYGWIN__)
00654     struct timeval   timeNow;
00655 
00656     gettimeofday( &timeNow, NULL );
00657     rc = (timeNow.tv_sec - timeStart.tv_sec) * 1000;
00658     rc += (timeNow.tv_usec - timeStart.tv_usec) / 1000;
00659 #else
00660     NutEnterCritical();
00661     rc = nut_ticks;
00662     NutExitCritical();
00663 #endif
00664 
00665     return rc;
00666 }
00667 
00682 u_long NutGetSeconds(void)
00683 {
00684     return NutGetTickCount() / NutGetTickClock();
00685 }
00686 
00703 u_long NutGetMillis(void)
00704 {
00705     // carefully stay within 32 bit values
00706     u_long ticks   = NutGetTickCount();
00707     u_long seconds = ticks / NutGetTickClock();
00708     ticks         -= seconds * NutGetTickClock();
00709     return seconds * 1000 + (ticks * 1000 ) / NutGetTickClock();
00710 }
00711 

© 2000-2007 by egnite Software GmbH - visit http://www.ethernut.de/