Nut/OS  4.10.3
API Reference
ostimer.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2001-2005 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  */
00033 
00034 /*
00035  * $Log$
00036  * Revision 1.8  2008/08/22 09:25:34  haraldkipp
00037  * Clock value caching and new functions NutArchClockGet, NutClockGet and
00038  * NutClockSet added.
00039  *
00040  * Revision 1.7  2008/08/11 11:51:19  thiagocorrea
00041  * Preliminary Atmega2560 compile options, but not yet supported.
00042  * It builds, but doesn't seam to run properly at this time.
00043  *
00044  * Revision 1.6  2008/08/11 06:59:17  haraldkipp
00045  * BSD types replaced by stdint types (feature request #1282721).
00046  *
00047  * Revision 1.5  2008/07/08 08:25:04  haraldkipp
00048  * NutDelay is no more architecture specific.
00049  * Number of loops per millisecond is configurable or will be automatically
00050  * determined.
00051  * A new function NutMicroDelay provides shorter delays.
00052  *
00053  * Revision 1.4  2006/12/20 15:14:41  freckle
00054  * corrected millisecond to nut ticks . Same bug as fixed at 2006-09-05
00055  *
00056  * Revision 1.3  2006/10/05 17:13:12  haraldkipp
00057  * Fixes bug #1567730. The problem had been reported by several people.
00058  * Lars Andersson provided the most complete solution, IMHO.
00059  *
00060  * Revision 1.2  2006/02/08 15:18:49  haraldkipp
00061  * ATmega2561 Support
00062  *
00063  * Revision 1.1  2005/07/26 18:02:40  haraldkipp
00064  * Moved from dev.
00065  *
00066  * Revision 1.2  2005/06/12 16:50:57  haraldkipp
00067  * Major redesign to provide better portability and reduce interrupt latency.
00068  *
00069  * Revision 1.1  2005/05/27 17:17:31  drsung
00070  * Moved the file
00071  *
00072  * Revision 1.7  2005/05/16 08:54:45  haraldkipp
00073  * Original routines did not work for Arthernet.
00074  *
00075  * Revision 1.6  2005/03/09 08:33:34  hwmaier
00076  * Finally implemented the correct timer routines and init for AT90CAN128. Timer2 is now used on AT90CAN128 rather Timer0 because Atmel (don't blame me) swapped the Timer designation.
00077  *
00078  * Revision 1.5  2005/02/21 12:38:00  phblum
00079  * Removed tabs and added semicolons after NUTTRACER macros
00080  *
00081  * Revision 1.4  2005/02/10 07:06:48  hwmaier
00082  * Changes to incorporate support for AT90CAN128 CPU
00083  *
00084  * Revision 1.3  2005/01/24 22:34:50  freckle
00085  * Added new tracer by Phlipp Blum <blum@tik.ee.ethz.ch>
00086  *
00087  * Revision 1.2  2004/12/16 08:40:35  haraldkipp
00088  * Late increment fixes ICCAVR bug.
00089  *
00090  * Revision 1.1  2004/03/16 16:48:46  haraldkipp
00091  * Added Jan Dubiec's H8/300 port.
00092  *
00093  * Revision 1.3  2004/03/05 20:38:18  drsung
00094  * Bugfix from bugfix. sorry!
00095  *
00096  * Revision 1.2  2004/03/05 20:19:45  drsung
00097  * Bugfix in NutTimerInit. ICCAVR failed to compile, if NUT_CPU_FREQ is defined.
00098  *
00099  * Revision 1.1  2004/02/01 18:49:48  haraldkipp
00100  * Added CPU family support
00101  *
00102  */
00103 
00104 #include <cfg/os.h>
00105 #include <sys/atom.h>
00106 #include <dev/irqreg.h>
00107 #include <arch/timer.h>
00108 
00118 #ifndef NUT_TICK_NFREQ
00119 #ifdef NUT_CPU_FREQ
00120 #define NUT_TICK_NFREQ  1000L
00121 #else
00122 #define NUT_TICK_NFREQ  1024L
00123 #endif
00124 #endif
00125 
00126 #ifdef NUT_CPU_FREQ             /* ----- NUT_CPU_FREQ */
00127 #ifndef NUT_TIMER_CRYSTAL
00128 #define NUT_TIMER_CRYSTAL   NUT_CPU_FREQ
00129 #endif
00130 #ifndef NUT_TIMER_PRESCALE
00131 #define NUT_TIMER_PRESCALE  128
00132 #endif
00133 #else                           /* ----- !NUT_CPU_FREQ */
00134 #ifndef NUT_TIMER_CRYSTAL
00135 #define NUT_TIMER_CRYSTAL   32768L
00136 #endif
00137 #ifndef NUT_TIMER_PRESCALE
00138 #define NUT_TIMER_PRESCALE  1
00139 #endif
00140 #endif                          /* ----- NUT_CPU_FREQ */
00141 
00142 /* Output compare register value. */
00143 #define OCR_VALUE       (((2 * NUT_TIMER_CRYSTAL / (NUT_TIMER_PRESCALE * NUT_TICK_NFREQ) + 1) / 2) - 1)
00144 
00145 /* Calculated number of timer ticks per second. */
00146 #define NUT_TICK_FREQ   ((2 * NUT_TIMER_CRYSTAL / (OCR_VALUE + 1) / NUT_TIMER_PRESCALE + 1) / 2)
00147 
00148 
00149 #ifdef NUT_CPU_FREQ             /* ----- NUT_CPU_FREQ */
00150 #if defined(MCU_AT90CAN128)
00151 #define TCCR_FLAGS  (_BV(CS20) | _BV(CS22) | _BV(WGM21))
00152 #elif defined(MCU_ATMEGA2560)
00153 #define TCCR_FLAGS  (_BV(WGM21))
00154 #define TCCR2B_FLAGS  (_BV(CS20) | _BV(CS22))
00155 #elif defined(MCU_ATMEGA2561)
00156 #define TCCR_FLAGS  (_BV(WGM21))
00157 #define TCCR2B_FLAGS  (_BV(CS20) | _BV(CS22))
00158 #elif defined(MCU_ATMEGA103)    /* MCU_ATMEGA103 */
00159 #define TCCR_FLAGS  (_BV(CS00) | _BV(CS02) | _BV(CTC0))
00160 #else                           /* MCU_ATMEGA128 */
00161 #define TCCR_FLAGS  (_BV(CS00) | _BV(CS02) | _BV(WGM01))
00162 #endif
00163 #else                           /* ----- !NUT_CPU_FREQ */
00164 #if defined(MCU_ATMEGA103)      /* MCU_ATMEGA103 */
00165 #define TCCR_FLAGS  (_BV(CS00) | _BV(CTC0))
00166 #define TCCR_AFLAGS _BV(CS01)
00167 #define ASSR_BIT    AS0
00168 #define ASSR_BUSY   (_BV(TCN0UB) | _BV(OCR0UB) | _BV(TCR0UB))
00169 #elif defined(MCU_ATMEGA128)    /* MCU_ATMEGA128 */
00170 #define TCCR_FLAGS  (_BV(CS00) | _BV(WGM01))
00171 #define TCCR_AFLAGS _BV(CS01)
00172 #define ASSR_BIT    AS0
00173 #define ASSR_BUSY   (_BV(TCN0UB) | _BV(OCR0UB) | _BV(TCR0UB))
00174 #else                           /* Other MCU */
00175 #error Define NUT_CPU_FREQ to compile for this CPU.
00176 #endif
00177 #endif                          /* ----- NUT_CPU_FREQ */
00178 
00179 #if defined(MCU_AT90CAN128) || defined(MCU_ATMEGA2560) || defined(MCU_ATMEGA2561)
00180 #define TCCRx       TCCR2A
00181 #define TCNTx       TCNT2
00182 #define OCRx        OCR2A
00183 #define TIFR_OCFx   _BV(OCF2A)
00184 #define TIFR_TOVx  _BV(TOV2)
00185 #define sig_TIMER   sig_OUTPUT_COMPARE2
00186 #else
00187 #define TCCRx       TCCR0
00188 #define TCNTx       TCNT0
00189 #define OCRx        OCR0
00190 #define TIFR_OCFx   _BV(OCF0)
00191 #define TIFR_TOVx  _BV(TOV0)
00192 #define sig_TIMER   sig_OUTPUT_COMPARE0
00193 #endif
00194 
00195 static uint32_t cpu_clock;
00196 
00202 #ifndef NUT_CPU_FREQ
00203 static uint32_t CountCpuLoops(void)
00204 {
00205 #ifdef __GNUC__
00206     uint32_t rc = 1;
00207 
00208     __asm__ __volatile__("firstovf:              \n\t"  /* */
00209                          "in %D0,%1              \n\t"  /* */
00210                          "andi %D0,%2            \n\t"  /* */
00211                          "breq firstovf          \n\t"  /* */
00212                          "out %1,%D0             \n\n"  /* */
00213                          /* This loop has 8 cycles. */
00214                          "nextovf:               \n\t"  /* */
00215                          "sec                    \n\t"  /* */
00216                          "adc %A0,__zero_reg__   \n\t"  /* */
00217                          "adc %B0,__zero_reg__   \n\t"  /* */
00218                          "adc %C0,__zero_reg__   \n\t"  /* */
00219                          "in %D0,%1              \n\t"  /* */
00220                          "andi %D0,%2            \n\t"  /* */
00221                          "breq nextovf           \n\t"  /* */
00222                          "clr %D0                    "  /* */
00223                          :"=d"(rc)      /* Output %0 */
00224                          :"I"(_SFR_IO_ADDR(TIFR))       /* Input %1 */
00225                          , "I"(TIFR_TOVx)      /* Input %2 */
00226                          , "0"(rc)      /* Input %0 */
00227         );
00228     return rc;
00229 #elif defined(__IMAGECRAFT__)
00230     uint32_t rc;
00231 
00232     asm("CLR  R0");
00233     asm("CLR  R16");
00234     asm("CLR  R17");
00235     asm("CLR  R18");
00236     asm("firstovf:");
00237     asm("IN   R19, 0x36");
00238     asm("ANDI R19,1");
00239     asm("BREQ firstovf");
00240     asm("OUT  0x36, R19");
00241     /* This loop has 8 cycles. */
00242     asm("nextovf:");
00243     asm("SEC");
00244     asm("ADC  R16, R0");
00245     asm("ADC  R17, R0");
00246     asm("ADC  R18, R0");
00247     asm("IN   R19, 0x36");
00248     asm("ANDI R19, 1");
00249     asm("BREQ nextovf");
00250     asm("CLR  R19");
00251     asm("STD  Y+0, R16");
00252     asm("STD  Y+1, R17");
00253     asm("STD  Y+2, R18");
00254     asm("STD  Y+3, R19");
00255 
00256     return rc;
00257 #endif
00258 }
00259 #endif
00260 
00270 #ifndef NUT_CPU_FREQ
00271 static uint32_t NutComputeCpuClock(void)
00272 {
00273     uint32_t rc;
00274 
00275     /* Disable timer interrupts. */
00276     NutDisableTimerIrq();
00277 
00278     /* Select asynchronous oscillator. */
00279     sbi(ASSR, ASSR_BIT);
00280 
00281     /* Reset counter register. */
00282     outb(TCNTx, 0);
00283 
00284     /* Set prescaler to 8. Overflow will occur every 62.5 ms. */
00285     outb(TCCRx, TCCR_AFLAGS);
00286 
00287     /* Wait for asynchronous busy clear. */
00288     while ((inb(ASSR) & ASSR_BUSY) != 0);
00289 
00290     /* Clear interrupt flag. */
00291     outb(TIFR, TIFR_TOVx);
00292 
00293     NutEnterCritical();
00294     rc = CountCpuLoops();
00295     NutExitCritical();
00296 
00297     return rc << 7;
00298 }
00299 #endif
00300 
00309 void NutRegisterTimer(void (*handler) (void *))
00310 {
00311 #ifdef NUT_CPU_FREQ
00312     cpu_clock = NUT_CPU_FREQ;
00313 #else
00314     cpu_clock = NutComputeCpuClock();
00315 #endif
00316 
00317     /* Disable timer interrupts. */
00318     NutDisableTimerIrq();
00319 
00320 #ifndef NUT_CPU_FREQ
00321     /* Select asynchronous oscillator. */
00322     sbi(ASSR, ASSR_BIT);
00323 #endif
00324 
00325     /* Reset counter register. */
00326     outb(TCNTx, 0);
00327 
00328     /* Set CTC mode and prescaler. */
00329     outb(TCCRx, TCCR_FLAGS);
00330 #ifdef TCCR2B_FLAGS
00331     outb(TCCR2B, TCCR2B_FLAGS);
00332 #endif
00333 
00334     /* Set output compare register. */
00335     outb(OCRx, OCR_VALUE);
00336 
00337 #ifndef NUT_CPU_FREQ
00338     /* Wait for asynchronous busy clear. */
00339     while ((inb(ASSR) & ASSR_BUSY) != 0);
00340 #endif
00341 
00342     /* Clear interrupt flags. */
00343     outb(TIFR, TIFR_OCFx);
00344 
00345     /* Enable timer compare interrupts. */
00346     NutRegisterIrqHandler(&sig_TIMER, handler, 0);
00347 }
00348 
00354 uint32_t NutArchClockGet(int idx)
00355 {
00356     return cpu_clock;
00357 }
00358 
00364 uint32_t NutGetTickClock(void)
00365 {
00366     return NUT_TICK_FREQ;
00367 }
00368 
00372 uint32_t NutTimerMillisToTicks(uint32_t ms)
00373 {
00374 #if (NUT_TICK_FREQ % 1000)
00375     if (ms >= 0x3E8000UL)
00376         return (ms / 1000UL) * NUT_TICK_FREQ;
00377     return (ms * NUT_TICK_FREQ + 999UL) / 1000UL;
00378 #else
00379     return ms * (NUT_TICK_FREQ / 1000UL);
00380 #endif
00381 }
00382