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 
00212 #include <cfg/os.h>
00213 #include <dev/irqreg.h>
00214 
00215 #include <sys/types.h>
00216 #include <sys/atom.h>
00217 #include <sys/heap.h>
00218 #include <sys/thread.h>
00219 #include <sys/timer.h>
00220 #include <sys/nutdebug.h>
00221 
00222 #ifdef NUTDEBUG
00223 #include <sys/osdebug.h>
00224 #endif
00225 
00226 #ifdef NUTTRACER
00227 #include <sys/tracer.h>
00228 #endif
00229 
00230 #ifdef __NUT_EMULATION__
00231 #include <sys/time.h>
00232 static struct timeval   timeStart;
00233 #endif
00234 
00235 #include <string.h>
00236 
00241 
00245 NUTTIMERINFO *nutTimerList;
00246 
00247 /*
00248  * Last processing time of elapsed timers. 
00249  */
00250 static uint32_t nut_ticks_resume; 
00251 
00255 volatile uint32_t nut_ticks;
00256 
00257 // volatile uint32_t nut_tick_dist[32];
00258 
00259 static uint32_t clock_cache[NUT_HWCLK_MAX + 1];
00260 
00264 #if defined(NUT_DELAYLOOPS)
00265 volatile uint32_t nut_delay_loops = NUT_DELAYLOOPS;
00266 #else
00267 volatile uint32_t nut_delay_loops;
00268 #endif
00269 
00273 //Not Used uint32_t nut_delay_loops_clk;
00274 
00278 #ifndef __NUT_EMULATION__
00279 #ifdef USE_TIMER
00280 SIGNAL( SIG_TIMER ) 
00281 #else
00282 static void NutTimerIntr(void *arg)
00283 #endif
00284 {
00285     nut_ticks++;
00286     // nut_tick_dist[TCNT0]++;
00287 #ifdef NUTDEBUG_CHECK_STACKMIN
00288     if((nut_ticks % 1000) == 0) {
00289         NUTTHREADINFO *tdp = NutThreadStackCheck(NUTDEBUG_CHECK_STACKMIN);
00290         if (tdp) {
00291             NUTFATAL(tdp->td_name, __FILE__, __LINE__, "more stack space");
00292         }
00293     }
00294 #endif
00295 }
00296 #endif
00297 
00298 
00306 void NutTimerInit(void)
00307 {
00308 #ifdef __NUT_EMULATION__
00309     gettimeofday( &timeStart, NULL );
00310 #else
00311     NutRegisterTimer(NutTimerIntr);
00312     NutEnableTimerIrq();
00313 //Not Used     /* Remember the CPU clock for which the loop counter is valid. */
00314 //Not Used     nut_delay_loops_clk = NutGetCpuClock();
00315 #if !defined(NUT_DELAYLOOPS)
00316 #ifndef NUT_TICK_FREQ
00317 #define NUT_TICK_FREQ   1000UL
00318 #endif
00319     {
00320         /* Wait for the next tick. */
00321         uint32_t cnt = NutGetTickCount();
00322         while (cnt == NutGetTickCount());
00323         /* Wait for the next tick, counting loops. */
00324         cnt = NutGetTickCount();
00325         while (cnt == NutGetTickCount()) {
00326             nut_delay_loops++;
00327         }
00328         /* 
00329          * The loop above needs more cycles than the actual delay loop.
00330          * Apply the correction found by trial and error. Works acceptable
00331          * with GCC for Ethernut 1 and 3.
00332          */
00333         nut_delay_loops *= 103UL;
00334         nut_delay_loops /= 26UL;
00335     }
00336 #endif
00337 #endif
00338 }
00339 
00355 void NutMicroDelay(uint32_t us)
00356 {
00357 #ifdef __NUT_EMULATION__
00358     usleep(us);
00359 #else
00360     register uint32_t cnt = nut_delay_loops * us / 1000;
00361 
00362     while (cnt--) {
00363         _NOP();
00364     }
00365 #endif
00366 }
00367 
00379 void NutDelay(uint8_t ms)
00380 {
00381     NutMicroDelay((uint32_t)ms * 1000);
00382 }
00383 
00393 void NutTimerInsert(NUTTIMERINFO * tn)
00394 {
00395     NUTTIMERINFO *tnp;
00396 
00397     NUTASSERT(tn != NULL);
00398 
00399     tn->tn_prev = NULL;
00400     for (tnp = nutTimerList; tnp; tnp = tnp->tn_next) {
00401         if (tn->tn_ticks_left < tnp->tn_ticks_left) {
00402             tnp->tn_ticks_left -= tn->tn_ticks_left;
00403             break;
00404         }
00405         tn->tn_ticks_left -= tnp->tn_ticks_left;
00406         tn->tn_prev = tnp;
00407     }
00408     tn->tn_next = tnp;
00409     if (tn->tn_next) {
00410         tn->tn_next->tn_prev = tn;
00411     }
00412     if (tn->tn_prev) {
00413         tn->tn_prev->tn_next = tn;
00414     }
00415     else {
00416         nutTimerList = tn;
00417     }
00418 }
00419 
00426 void NutTimerProcessElapsed(void)
00427 {
00428     NUTTIMERINFO *tn;
00429     uint32_t ticks;
00430     uint32_t ticks_new;
00431 
00432     // calculate ticks since last call
00433     ticks = NutGetTickCount();
00434     ticks_new = ticks - nut_ticks_resume;
00435     nut_ticks_resume = ticks;
00436     
00437     // process timers
00438     while (nutTimerList && ticks_new){
00439         
00440         tn = nutTimerList;
00441 
00442         // subtract time
00443         if (ticks_new < tn->tn_ticks_left) {
00444             tn->tn_ticks_left -= ticks_new;
00445             ticks_new = 0;
00446         } else {
00447             ticks_new -= tn->tn_ticks_left;
00448             tn->tn_ticks_left = 0;
00449         }
00450         
00451         // elapsed
00452         if (tn->tn_ticks_left == 0){
00453 
00454             // callback
00455             if (tn->tn_callback) {
00456                 (*tn->tn_callback) (tn, (void *) tn->tn_arg);
00457             }
00458             // remove from list
00459             nutTimerList = nutTimerList->tn_next;
00460             if (nutTimerList) {
00461                 nutTimerList->tn_prev = NULL;
00462             }
00463             if ((tn->tn_ticks_left = tn->tn_ticks) == 0) {
00464                 NutHeapFree(tn);
00465             }
00466             else {
00467                 // re-insert
00468                 NutTimerInsert(tn);
00469             }
00470         }
00471     }
00472 }
00473 
00493 NUTTIMERINFO * NutTimerCreate(uint32_t ticks, void (*callback) (HANDLE, void *), void *arg, uint8_t flags)
00494 {
00495     NUTTIMERINFO *tn;
00496     
00497     tn = NutHeapAlloc(sizeof(NUTTIMERINFO));
00498     if (tn) {
00499         tn->tn_ticks_left = ticks + NutGetTickCount() - nut_ticks_resume;
00500         
00501         /*
00502          * Periodic timers will reload the tick counter on each timer 
00503          * intervall.
00504          */
00505         if (flags & TM_ONESHOT) {
00506             tn->tn_ticks = 0;
00507         } else {
00508             tn->tn_ticks = ticks;
00509         }
00510         
00511         /* Set callback and callback argument. */
00512         tn->tn_callback = callback;
00513         tn->tn_arg = arg;
00514     }
00515     return tn;    
00516 }
00517 
00544 HANDLE NutTimerStartTicks(uint32_t ticks, void (*callback) (HANDLE, void *), void *arg, uint8_t flags)
00545 {
00546     NUTTIMERINFO *tn;
00547 
00548     tn = NutTimerCreate( ticks, callback, arg, flags);
00549     if (tn) {
00550         /* Add the timer to the list. */
00551         NutTimerInsert(tn);
00552     }
00553     return tn;
00554 }
00555 
00584 HANDLE NutTimerStart(uint32_t ms, void (*callback) (HANDLE, void *), void *arg, uint8_t flags)
00585 {
00586         return NutTimerStartTicks(NutTimerMillisToTicks(ms), callback, arg, flags);
00587 }
00588 
00610 void NutSleep(uint32_t ms)
00611 {
00612     if (ms) {
00613 
00614         /* remove running thread from runQueue */
00615         NutThreadRemoveQueue(runningThread, &runQueue);
00616         runningThread->td_state = TDS_SLEEP;
00617 
00618         if ((runningThread->td_timer = NutTimerStart(ms, NutThreadWake, runningThread, TM_ONESHOT)) != 0) {
00619 #ifdef NUTTRACER
00620             TRACE_ADD_ITEM(TRACE_TAG_THREAD_SLEEP,(int)runningThread);
00621 #endif
00622             NutThreadResume();
00623         } else
00624         {
00625             /* timer creation failed, restore queues */
00626             runningThread->td_queue = &runQueue;
00627             runningThread->td_qnxt  = runQueue;
00628             runningThread->td_state = TDS_RUNNING;
00629             runQueue = runningThread;
00630         }
00631     } else
00632         NutThreadYield();
00633 }
00634 
00647 void NutTimerStop(HANDLE handle)
00648 {
00649     NUTTIMERINFO *tn = (NUTTIMERINFO *)handle;
00650 
00651     NUTASSERT(tn != NULL);
00652 
00653     /* Disable periodic operation and callback. */
00654     tn->tn_ticks = 0;
00655     tn->tn_callback = NULL;
00656     /* If not already elapsed, expire the timer. */
00657     if (tn->tn_ticks_left) {
00658         if (tn->tn_prev) {
00659             tn->tn_prev->tn_next = tn->tn_next;
00660         }
00661         else {
00662             nutTimerList = tn->tn_next;
00663         }
00664         if (tn->tn_next) {
00665             tn->tn_next->tn_prev = tn->tn_prev;
00666             tn->tn_next->tn_ticks_left += tn->tn_ticks_left;
00667         }
00668         tn->tn_ticks_left = 0;
00669         NutTimerInsert(tn);
00670     }
00671 }
00672 
00681 uint32_t NutGetTickCount(void)
00682 {
00683     uint32_t rc;
00684 
00685 #ifdef __NUT_EMULATION__
00686     struct timeval   timeNow;
00687     gettimeofday( &timeNow, NULL );
00688     rc = (timeNow.tv_sec - timeStart.tv_sec) * 1000;
00689     rc += (timeNow.tv_usec - timeStart.tv_usec) / 1000;
00690 #else
00691     NutEnterCritical();
00692     rc = nut_ticks;
00693     NutExitCritical();
00694 #endif
00695 
00696     return rc;
00697 }
00698 
00713 uint32_t NutGetSeconds(void)
00714 {
00715     return NutGetTickCount() / NutGetTickClock();
00716 }
00717 
00734 uint32_t NutGetMillis(void)
00735 {
00736     // carefully stay within 32 bit values
00737     uint32_t ticks   = NutGetTickCount();
00738     uint32_t seconds = ticks / NutGetTickClock();
00739     ticks         -= seconds * NutGetTickClock();
00740     return seconds * 1000 + (ticks * 1000 ) / NutGetTickClock();
00741 }
00742 
00758 #if NUT_HWCLK_MAX
00759 uint32_t NutClockGet(int idx)
00760 {
00761     if (clock_cache[idx] == 0) {
00762         clock_cache[idx] = NutArchClockGet(idx) | NUT_CACHE_LVALID;
00763     }
00764     return clock_cache[idx] & ~NUT_CACHE_LVALID;
00765 }
00766 #endif
00767 
00783 int NutClockSet(int idx, uint32_t freq)
00784 {
00785     /* Clear all cached values. */
00786     memset(clock_cache, 0, sizeof(clock_cache));
00787 
00788     return 0;
00789 }
00790 
00799 #if !defined(NutGetCpuClock)
00800 uint32_t NutGetCpuClock(void)
00801 {
00802 #ifdef NUT_CPU_FREQ
00803     /* Keep this code small! Can we use a preprocessor
00804     ** macro to define NutGetCpuClock() as NUT_CPU_FREQ? */
00805     return NUT_CPU_FREQ;
00806 #else /* !NUT_CPU_FREQ */
00807     /* Keep this code fast for the normal case, where the
00808     ** cached value is valid. */
00809     if (clock_cache[NUT_HWCLK_CPU]) {
00810         return clock_cache[NUT_HWCLK_CPU] & ~NUT_CACHE_LVALID;
00811     }
00812 #if NUT_HWCLK_MAX
00813     return NutClockGet(NUT_HWCLK_CPU);
00814 #else /* !NUT_HWCLK_MAX */
00815     clock_cache[NUT_HWCLK_CPU] = NutArchClockGet(NUT_HWCLK_CPU) | NUT_CACHE_LVALID;
00816     return clock_cache[NUT_HWCLK_CPU] & ~NUT_CACHE_LVALID;
00817 #endif /* !NUT_HWCLK_MAX */
00818 #endif /* !NUT_CPU_FREQ */
00819 }
00820 #endif /* !NutGetCpuClock */
00821 

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