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 
00203 #include <cfg/os.h>
00204 #include <dev/irqreg.h>
00205 
00206 #include <sys/types.h>
00207 #include <sys/atom.h>
00208 #include <sys/heap.h>
00209 #include <sys/thread.h>
00210 #include <sys/timer.h>
00211 
00212 #ifdef NUTDEBUG
00213 #include <sys/osdebug.h>
00214 #endif
00215 
00216 #ifdef NUTTRACER
00217 #include <sys/tracer.h>
00218 #endif
00219 
00220 #if defined (__linux__) || defined(__APPLE__) || defined(__CYGWIN__)
00221 #include <sys/time.h>
00222 
00223 static struct timeval   timeStart;
00224 #endif
00225 
00226 #include <string.h>
00227 
00232 
00236 NUTTIMERINFO *nutTimerList;
00237 
00238 /*
00239  * Last processing time of elapsed timers. 
00240  */
00241 static uint32_t nut_ticks_resume; 
00242 
00246 volatile uint32_t nut_ticks;
00247 
00248 // volatile uint32_t nut_tick_dist[32];
00249 
00250 static uint32_t clock_cache[NUT_HWCLK_MAX + 1];
00251 
00255 #if defined(NUT_DELAYLOOPS)
00256 volatile uint32_t nut_delay_loops = NUT_DELAYLOOPS;
00257 #else
00258 volatile uint32_t nut_delay_loops;
00259 #endif
00260 
00264 //Not Used uint32_t nut_delay_loops_clk;
00265 
00269 #if !(defined (__linux__) || defined(__APPLE__) || defined(__CYGWIN__))
00270 #ifdef USE_TIMER
00271 SIGNAL( SIG_TIMER ) 
00272 #else
00273 static void NutTimerIntr(void *arg)
00274 #endif
00275 {
00276     nut_ticks++;
00277     // nut_tick_dist[TCNT0]++;
00278 }
00279 #endif
00280 
00281 
00289 void NutTimerInit(void)
00290 {
00291 #if defined (__linux__) || defined(__APPLE__) || defined(__CYGWIN__)
00292     gettimeofday( &timeStart, NULL );
00293 #else
00294     NutRegisterTimer(NutTimerIntr);
00295     NutEnableTimerIrq();
00296 //Not Used     /* Remember the CPU clock for which the loop counter is valid. */
00297 //Not Used     nut_delay_loops_clk = NutGetCpuClock();
00298 #if !defined(NUT_DELAYLOOPS)
00299 #ifndef NUT_TICK_FREQ
00300 #define NUT_TICK_FREQ   1000UL
00301 #endif
00302     {
00303         /* Wait for the next tick. */
00304         uint32_t cnt = NutGetTickCount();
00305         while (cnt == NutGetTickCount());
00306         /* Wait for the next tick, counting loops. */
00307         cnt = NutGetTickCount();
00308         while (cnt == NutGetTickCount()) {
00309             nut_delay_loops++;
00310         }
00311         /* 
00312          * The loop above needs more cycles than the actual delay loop.
00313          * Apply the correction found by trial and error. Works acceptable
00314          * with GCC for Ethernut 1 and 3.
00315          */
00316         nut_delay_loops *= 103UL;
00317         nut_delay_loops /= 26UL;
00318     }
00319 #endif
00320 #endif
00321 }
00322 
00338 void NutMicroDelay(uint32_t us)
00339 {
00340 #if defined (__linux__) || defined(__APPLE__) || defined(__CYGWIN__)
00341     usleep(us);
00342 #else
00343     register uint32_t cnt = nut_delay_loops * us / 1000;
00344 
00345     while (cnt--) {
00346         _NOP();
00347     }
00348 #endif
00349 }
00350 
00362 void NutDelay(uint8_t ms)
00363 {
00364     NutMicroDelay((uint32_t)ms * 1000);
00365 }
00366 
00376 void NutTimerInsert(NUTTIMERINFO * tn)
00377 {
00378     NUTTIMERINFO *tnp;
00379 
00380     tn->tn_prev = NULL;
00381     for (tnp = nutTimerList; tnp; tnp = tnp->tn_next) {
00382         if (tn->tn_ticks_left < tnp->tn_ticks_left) {
00383             tnp->tn_ticks_left -= tn->tn_ticks_left;
00384             break;
00385         }
00386         tn->tn_ticks_left -= tnp->tn_ticks_left;
00387         tn->tn_prev = tnp;
00388     }
00389     tn->tn_next = tnp;
00390     if (tn->tn_next) {
00391         tn->tn_next->tn_prev = tn;
00392     }
00393     if (tn->tn_prev) {
00394         tn->tn_prev->tn_next = tn;
00395     }
00396     else {
00397         nutTimerList = tn;
00398     }
00399 }
00400 
00407 void NutTimerProcessElapsed(void)
00408 {
00409     NUTTIMERINFO *tn;
00410     uint32_t ticks;
00411     uint32_t ticks_new;
00412 
00413     // calculate ticks since last call
00414     ticks = NutGetTickCount();
00415     ticks_new = ticks - nut_ticks_resume;
00416     nut_ticks_resume = ticks;
00417     
00418     // process timers
00419     while (nutTimerList && ticks_new){
00420         
00421         tn = nutTimerList;
00422 
00423         // subtract time
00424         if (ticks_new < tn->tn_ticks_left) {
00425             tn->tn_ticks_left -= ticks_new;
00426             ticks_new = 0;
00427         } else {
00428             ticks_new -= tn->tn_ticks_left;
00429             tn->tn_ticks_left = 0;
00430         }
00431         
00432         // elapsed
00433         if (tn->tn_ticks_left == 0){
00434 
00435             // callback
00436             if (tn->tn_callback) {
00437                 (*tn->tn_callback) (tn, (void *) tn->tn_arg);
00438             }
00439             // remove from list
00440             nutTimerList = nutTimerList->tn_next;
00441             if (nutTimerList) {
00442                 nutTimerList->tn_prev = NULL;
00443             }
00444             if ((tn->tn_ticks_left = tn->tn_ticks) == 0) {
00445                 NutHeapFree(tn);
00446             }
00447             else {
00448                 // re-insert
00449                 NutTimerInsert(tn);
00450             }
00451         }
00452     }
00453 }
00454 
00474 NUTTIMERINFO * NutTimerCreate(uint32_t ticks, void (*callback) (HANDLE, void *), void *arg, uint8_t flags)
00475 {
00476     NUTTIMERINFO *tn;
00477     
00478     tn = NutHeapAlloc(sizeof(NUTTIMERINFO));
00479     if (tn) {
00480         tn->tn_ticks_left = ticks + NutGetTickCount() - nut_ticks_resume;
00481         
00482         /*
00483          * Periodic timers will reload the tick counter on each timer 
00484          * intervall.
00485          */
00486         if (flags & TM_ONESHOT) {
00487             tn->tn_ticks = 0;
00488         } else {
00489             tn->tn_ticks = ticks;
00490         }
00491         
00492         /* Set callback and callback argument. */
00493         tn->tn_callback = callback;
00494         tn->tn_arg = arg;
00495     }
00496     return tn;    
00497 }
00498 
00525 HANDLE NutTimerStartTicks(uint32_t ticks, void (*callback) (HANDLE, void *), void *arg, uint8_t flags)
00526 {
00527     NUTTIMERINFO *tn;
00528 
00529     tn = NutTimerCreate( ticks, callback, arg, flags);
00530     if (tn) {
00531         /* Add the timer to the list. */
00532         NutTimerInsert(tn);
00533     }
00534     return tn;
00535 }
00536 
00565 HANDLE NutTimerStart(uint32_t ms, void (*callback) (HANDLE, void *), void *arg, uint8_t flags)
00566 {
00567         return NutTimerStartTicks(NutTimerMillisToTicks(ms), callback, arg, flags);
00568 }
00569 
00591 void NutSleep(uint32_t ms)
00592 {
00593     if (ms) {
00594 
00595         /* remove running thread from runQueue */
00596         NutThreadRemoveQueue(runningThread, &runQueue);
00597         runningThread->td_state = TDS_SLEEP;
00598 
00599         if ((runningThread->td_timer = NutTimerStart(ms, NutThreadWake, runningThread, TM_ONESHOT)) != 0) {
00600 #ifdef NUTTRACER
00601             TRACE_ADD_ITEM(TRACE_TAG_THREAD_SLEEP,(int)runningThread);
00602 #endif
00603             NutThreadResume();
00604         } else
00605         {
00606             /* timer creation failed, restore queues */
00607             runningThread->td_queue = &runQueue;
00608             runningThread->td_qnxt  = runQueue;
00609             runningThread->td_state = TDS_RUNNING;
00610             runQueue = runningThread;
00611         }
00612     } else
00613         NutThreadYield();
00614 }
00615 
00628 void NutTimerStop(HANDLE handle)
00629 {
00630     NUTTIMERINFO *tn = (NUTTIMERINFO *)handle;
00631 
00632     /* Disable periodic operation and callback. */
00633     tn->tn_ticks = 0;
00634     tn->tn_callback = NULL;
00635     /* If not already elapsed, expire the timer. */
00636     if (tn->tn_ticks_left) {
00637         if (tn->tn_prev) {
00638             tn->tn_prev->tn_next = tn->tn_next;
00639         }
00640         else {
00641             nutTimerList = tn->tn_next;
00642         }
00643         if (tn->tn_next) {
00644             tn->tn_next->tn_prev = tn->tn_prev;
00645             tn->tn_next->tn_ticks_left += tn->tn_ticks_left;
00646         }
00647         tn->tn_ticks_left = 0;
00648         NutTimerInsert(tn);
00649     }
00650 }
00651 
00660 uint32_t NutGetTickCount(void)
00661 {
00662     uint32_t rc;
00663 
00664 #if defined (__linux__) || defined(__APPLE__) || defined(__CYGWIN__)
00665     struct timeval   timeNow;
00666 
00667     gettimeofday( &timeNow, NULL );
00668     rc = (timeNow.tv_sec - timeStart.tv_sec) * 1000;
00669     rc += (timeNow.tv_usec - timeStart.tv_usec) / 1000;
00670 #else
00671     NutEnterCritical();
00672     rc = nut_ticks;
00673     NutExitCritical();
00674 #endif
00675 
00676     return rc;
00677 }
00678 
00693 uint32_t NutGetSeconds(void)
00694 {
00695     return NutGetTickCount() / NutGetTickClock();
00696 }
00697 
00714 uint32_t NutGetMillis(void)
00715 {
00716     // carefully stay within 32 bit values
00717     uint32_t ticks   = NutGetTickCount();
00718     uint32_t seconds = ticks / NutGetTickClock();
00719     ticks         -= seconds * NutGetTickClock();
00720     return seconds * 1000 + (ticks * 1000 ) / NutGetTickClock();
00721 }
00722 
00738 #if NUT_HWCLK_MAX
00739 uint32_t NutClockGet(int idx)
00740 {
00741     if (clock_cache[idx] == 0) {
00742         clock_cache[idx] = NutArchClockGet(idx) | NUT_CACHE_LVALID;
00743     }
00744     return clock_cache[idx] & ~NUT_CACHE_LVALID;
00745 }
00746 #endif
00747 
00763 int NutClockSet(int idx, uint32_t freq)
00764 {
00765     /* Clear all cached values. */
00766     memset(clock_cache, 0, sizeof(clock_cache));
00767 
00768     return 0;
00769 }
00770 
00779 #if !defined(NutGetCpuClock)
00780 uint32_t NutGetCpuClock(void)
00781 {
00782 #ifdef NUT_CPU_FREQ
00783     /* Keep this code small! Can we use a preprocessor
00784     ** macro to define NutGetCpuClock() as NUT_CPU_FREQ? */
00785     return NUT_CPU_FREQ;
00786 #else /* !NUT_CPU_FREQ */
00787     /* Keep this code fast for the normal case, where the
00788     ** cached value is valid. */
00789     if (clock_cache[NUT_HWCLK_CPU]) {
00790         return clock_cache[NUT_HWCLK_CPU] & ~NUT_CACHE_LVALID;
00791     }
00792 #if NUT_HWCLK_MAX
00793     return NutClockGet(NUT_HWCLK_CPU);
00794 #else /* !NUT_HWCLK_MAX */
00795     clock_cache[NUT_HWCLK_CPU] = NutArchClockGet(NUT_HWCLK_CPU) | NUT_CACHE_LVALID;
00796     return clock_cache[NUT_HWCLK_CPU] & ~NUT_CACHE_LVALID;
00797 #endif /* !NUT_HWCLK_MAX */
00798 #endif /* !NUT_CPU_FREQ */
00799 }
00800 #endif /* !NutGetCpuClock */
00801 

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