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

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