Nut/OS  4.10.3
API Reference
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 uint32_t nut_delay_loops = NUT_DELAYLOOPS;
00266 #else
00267 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 #if defined(__AVR__)
00334         nut_delay_loops *= 103UL;
00335         nut_delay_loops /= 26UL;
00336 #else
00337         nut_delay_loops *= 137UL;
00338         nut_delay_loops /= 25UL;
00339 #endif
00340     }
00341 #endif
00342 #endif
00343 }
00344 
00373 void NutMicroDelay(uint32_t us)
00374 {
00375 #ifdef __NUT_EMULATION__
00376     usleep(us);
00377 #else
00378     register uint32_t cnt = nut_delay_loops * us / 1000;
00379 
00380     while (cnt--) {
00381         _NOP();
00382     }
00383 #endif
00384 }
00385 
00397 void NutDelay(uint8_t ms)
00398 {
00399     NutMicroDelay((uint32_t)ms * 1000);
00400 }
00401 
00411 void NutTimerInsert(NUTTIMERINFO * tn)
00412 {
00413     NUTTIMERINFO *tnp;
00414 
00415     NUTASSERT(tn != NULL);
00416 
00417     tn->tn_prev = NULL;
00418     for (tnp = nutTimerList; tnp; tnp = tnp->tn_next) {
00419         if (tn->tn_ticks_left < tnp->tn_ticks_left) {
00420             tnp->tn_ticks_left -= tn->tn_ticks_left;
00421             break;
00422         }
00423         tn->tn_ticks_left -= tnp->tn_ticks_left;
00424         tn->tn_prev = tnp;
00425     }
00426     tn->tn_next = tnp;
00427     if (tn->tn_next) {
00428         tn->tn_next->tn_prev = tn;
00429     }
00430     if (tn->tn_prev) {
00431         tn->tn_prev->tn_next = tn;
00432     }
00433     else {
00434         nutTimerList = tn;
00435     }
00436 }
00437 
00444 void NutTimerProcessElapsed(void)
00445 {
00446     NUTTIMERINFO *tn;
00447     uint32_t ticks;
00448     uint32_t ticks_new;
00449 
00450     // calculate ticks since last call
00451     ticks = NutGetTickCount();
00452     ticks_new = ticks - nut_ticks_resume;
00453     nut_ticks_resume = ticks;
00454 
00455     // process timers
00456     while (nutTimerList && ticks_new){
00457 
00458         tn = nutTimerList;
00459 
00460         // subtract time
00461         if (ticks_new < tn->tn_ticks_left) {
00462             tn->tn_ticks_left -= ticks_new;
00463             ticks_new = 0;
00464         } else {
00465             ticks_new -= tn->tn_ticks_left;
00466             tn->tn_ticks_left = 0;
00467         }
00468 
00469         // elapsed
00470         if (tn->tn_ticks_left == 0){
00471 
00472             // callback
00473             if (tn->tn_callback) {
00474                 (*tn->tn_callback) (tn, (void *) tn->tn_arg);
00475             }
00476             // remove from list
00477             nutTimerList = nutTimerList->tn_next;
00478             if (nutTimerList) {
00479                 nutTimerList->tn_prev = NULL;
00480             }
00481             if ((tn->tn_ticks_left = tn->tn_ticks) == 0) {
00482                 NutHeapFree(tn);
00483             }
00484             else {
00485                 // re-insert
00486                 NutTimerInsert(tn);
00487             }
00488         }
00489     }
00490 }
00491 
00511 NUTTIMERINFO * NutTimerCreate(uint32_t ticks, void (*callback) (HANDLE, void *), void *arg, uint8_t flags)
00512 {
00513     NUTTIMERINFO *tn;
00514 
00515     tn = NutHeapAlloc(sizeof(NUTTIMERINFO));
00516     if (tn) {
00517         tn->tn_ticks_left = ticks + NutGetTickCount() - nut_ticks_resume;
00518 
00519         /*
00520          * Periodic timers will reload the tick counter on each timer
00521          * intervall.
00522          */
00523         if (flags & TM_ONESHOT) {
00524             tn->tn_ticks = 0;
00525         } else {
00526             tn->tn_ticks = ticks;
00527         }
00528 
00529         /* Set callback and callback argument. */
00530         tn->tn_callback = callback;
00531         tn->tn_arg = arg;
00532     }
00533     return tn;
00534 }
00535 
00562 HANDLE NutTimerStartTicks(uint32_t ticks, void (*callback) (HANDLE, void *), void *arg, uint8_t flags)
00563 {
00564     NUTTIMERINFO *tn;
00565 
00566     tn = NutTimerCreate( ticks, callback, arg, flags);
00567     if (tn) {
00568         /* Add the timer to the list. */
00569         NutTimerInsert(tn);
00570     }
00571     return tn;
00572 }
00573 
00602 HANDLE NutTimerStart(uint32_t ms, void (*callback) (HANDLE, void *), void *arg, uint8_t flags)
00603 {
00604         return NutTimerStartTicks(NutTimerMillisToTicks(ms), callback, arg, flags);
00605 }
00606 
00628 void NutSleep(uint32_t ms)
00629 {
00630     if (ms) {
00631 
00632         /* remove running thread from runQueue */
00633         NutThreadRemoveQueue(runningThread, &runQueue);
00634         runningThread->td_state = TDS_SLEEP;
00635 
00636         if ((runningThread->td_timer = NutTimerStart(ms, NutThreadWake, runningThread, TM_ONESHOT)) != 0) {
00637 #ifdef NUTTRACER
00638             TRACE_ADD_ITEM(TRACE_TAG_THREAD_SLEEP,(int)runningThread);
00639 #endif
00640             NutThreadResume();
00641         } else
00642         {
00643             /* timer creation failed, restore queues */
00644             runningThread->td_queue = &runQueue;
00645             runningThread->td_qnxt  = runQueue;
00646             runningThread->td_state = TDS_RUNNING;
00647             runQueue = runningThread;
00648         }
00649     } else
00650         NutThreadYield();
00651 }
00652 
00665 void NutTimerStop(HANDLE handle)
00666 {
00667     NUTTIMERINFO *tn = (NUTTIMERINFO *)handle;
00668 
00669     NUTASSERT(tn != NULL);
00670 
00671     /* Disable periodic operation and callback. */
00672     tn->tn_ticks = 0;
00673     tn->tn_callback = NULL;
00674     /* If not already elapsed, expire the timer. */
00675     if (tn->tn_ticks_left) {
00676         if (tn->tn_prev) {
00677             tn->tn_prev->tn_next = tn->tn_next;
00678         }
00679         else {
00680             nutTimerList = tn->tn_next;
00681         }
00682         if (tn->tn_next) {
00683             tn->tn_next->tn_prev = tn->tn_prev;
00684             tn->tn_next->tn_ticks_left += tn->tn_ticks_left;
00685         }
00686         tn->tn_ticks_left = 0;
00687         NutTimerInsert(tn);
00688     }
00689 }
00690 
00699 uint32_t NutGetTickCount(void)
00700 {
00701     uint32_t rc;
00702 
00703 #ifdef __NUT_EMULATION__
00704     struct timeval   timeNow;
00705     gettimeofday( &timeNow, NULL );
00706     rc = (timeNow.tv_sec - timeStart.tv_sec) * 1000;
00707     rc += (timeNow.tv_usec - timeStart.tv_usec) / 1000;
00708 #else
00709     NutEnterCritical();
00710     rc = nut_ticks;
00711     NutExitCritical();
00712 #endif
00713 
00714     return rc;
00715 }
00716 
00731 uint32_t NutGetSeconds(void)
00732 {
00733     return NutGetTickCount() / NutGetTickClock();
00734 }
00735 
00752 uint32_t NutGetMillis(void)
00753 {
00754     // carefully stay within 32 bit values
00755     uint32_t ticks   = NutGetTickCount();
00756     uint32_t seconds = ticks / NutGetTickClock();
00757     ticks         -= seconds * NutGetTickClock();
00758     return seconds * 1000 + (ticks * 1000 ) / NutGetTickClock();
00759 }
00760 
00776 #if NUT_HWCLK_MAX
00777 uint32_t NutClockGet(int idx)
00778 {
00779     if (clock_cache[idx] == 0) {
00780         clock_cache[idx] = NutArchClockGet(idx) | NUT_CACHE_LVALID;
00781     }
00782     return clock_cache[idx] & ~NUT_CACHE_LVALID;
00783 }
00784 #endif
00785 
00801 int NutClockSet(int idx, uint32_t freq)
00802 {
00803     /* Clear all cached values. */
00804     memset(clock_cache, 0, sizeof(clock_cache));
00805 
00806     return 0;
00807 }
00808 
00817 #if !defined(NutGetCpuClock)
00818 uint32_t NutGetCpuClock(void)
00819 {
00820 #ifdef NUT_CPU_FREQ
00821     /* Keep this code small! Can we use a preprocessor
00822     ** macro to define NutGetCpuClock() as NUT_CPU_FREQ? */
00823     return NUT_CPU_FREQ;
00824 #else /* !NUT_CPU_FREQ */
00825     /* Keep this code fast for the normal case, where the
00826     ** cached value is valid. */
00827     if (clock_cache[NUT_HWCLK_CPU]) {
00828         return clock_cache[NUT_HWCLK_CPU] & ~NUT_CACHE_LVALID;
00829     }
00830 #if NUT_HWCLK_MAX
00831     return NutClockGet(NUT_HWCLK_CPU);
00832 #else /* !NUT_HWCLK_MAX */
00833     clock_cache[NUT_HWCLK_CPU] = NutArchClockGet(NUT_HWCLK_CPU) | NUT_CACHE_LVALID;
00834     return clock_cache[NUT_HWCLK_CPU] & ~NUT_CACHE_LVALID;
00835 #endif /* !NUT_HWCLK_MAX */
00836 #endif /* !NUT_CPU_FREQ */
00837 }
00838 #endif /* !NutGetCpuClock */
00839