- Main Page
- Related Pages
- Modules
- Data Structures
- Files

- File List
- Globals

# fasttimer.c

Go to the documentation of this file.

    00001 /*********************************************************************************
    00002
    00003  Copyright 2006-2008 MakingThings
    00004
    00005  Licensed under the Apache License,
    00006  Version 2.0 (the "License"); you may not use this file except in compliance
    00007  with the License. You may obtain a copy of the License at
    00008
    00009  http://www.apache.org/licenses/LICENSE-2.0
    00010
    00011  Unless required by applicable law or agreed to in writing, software distributed
    00012  under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
    00013  CONDITIONS OF ANY KIND, either express or implied. See the License for
    00014  the specific language governing permissions and limitations under the License.
    00015
    00016 *********************************************************************************/
    00017
    00018 /** \file fasttimer.c
    00019   FastTimer.
    00020   Functions to use the fast timer on the Make Controller Board.
    00021 */
    00022
    00023 #include "AT91SAM7X256.h"
    00024 #include "types.h"
    00025 #include "fasttimer.h"
    00026 #include "fasttimer_internal.h"
    00027 #include "config.h"
    00028 #include "rtos.h"
    00029
    00030 #define FAST_TIMER_CYCLES_PER_US 6
    00031
    00032 struct FastTimer_ FastTimer;
    00033
    00034 static int FastTimer_Init( void );
    00035 static int FastTimer_Deinit( void );
    00036 //static int FastTimer_GetCount( void );
    00037 static int FastTimer_GetTimeTarget( void );
    00038 static int FastTimer_GetTime( void );
    00039 static void FastTimer_SetTimeTarget( int );
    00040 static void FastTimer_Enable( void );
    00041
    00042 void FastTimer_Isr( void );
    00043
    00044 /** \defgroup FastTimer Fast Timer
    00045   The FastTimer subsystem provides a high resolution timer in a microsecond context.
    00046   If you don't need such high resolution timing, check the \ref Timer
    00047
    00048   The Fast Timer subsystem is based on a collection of \b FastTimerEntries.  To start a new timer, create a new
    00049   \b FastTimerEntry structure, initialize it with FastTimer_InitializeEntry( ), and start it with FastTimer_Set( ).
    00050
    00051   There are currently one main limitation to the Fast Timer system:
    00052   - In your callback function, you must not sleep or make any FreeRTOS-related calls.
    00053
    00054   \todo Allow the fast timer callbacks to cooperate with the \ref RTOS
    00055 * \ingroup Core
    00056 * @{
    00057 */
    00058
    00059 /**
    00060   Controls the active state of the Fast Timer system
    00061   @param active whether the FastTimer subsystem is active or not
    00062   @return Zero on success.
    00063   @see FastTimer_Set, FastTimer_Cancel
    00064 */
    00065 int FastTimer_SetActive( bool active )
    00066 {
    00067   if ( active )
    00068   {
    00069     if ( FastTimer.users++ == 0 )
    00070     {
    00071       int status;
    00072
    00073       status = FastTimer_Init();
    00074       if ( status != CONTROLLER_OK )
    00075       {
    00076         FastTimer.users--;
    00077         return status;
    00078       }
    00079     }
    00080   }
    00081   else
    00082   {
    00083     if ( --FastTimer.users == 0 )
    00084       FastTimer_Deinit();
    00085   }
    00086   return CONTROLLER_OK;
    00087 }
    00088
    00089 /**
    00090   Returns whether the timer subsystem is active or not
    00091   @return active.
    00092   @see FastTimer_Set, FastTimer_Cancel
    00093 */
    00094 int FastTimer_GetActive( )
    00095 {
    00096   return FastTimer.users > 0;
    00097 }
    00098
    00099 /**
    00100   Initializes a fast timer entry structure.
    00101   The event is signified by a callback to the function provided, after the interval specified.
    00102   The specified ID is passed back to the function to permit one function to work for many events.
    00103   Pass repeat = true to make the event continue to create callbacks until it is canceled.
    00104   Note that the timer entry structure needs to be created and managed by the caller.
    00105   The longest period for a fast timer entry is 2^32 / 1000000 = 4294s.
    00106   @param fastTimerEntry pointer to the FastTimerEntry to be intialized.
    00107   @param timerCallback pointer to the callback function.  The function must
    00108          be of the form \verbatim void callback( int id ) \endverbatim
    00109   @param id An integer specifying the ID the callback function is to be provided with.
    00110   @param timeUs The time in microseconds desired for the callback.
    00111   @param repeat Set whether the timer repeats or is a one-time event.
    00112   @see FastTimer_Cancel
    00113
    00114   \par Example
    00115   \code
    00116   TimerEntry myTimer; // our TimerEntry
    00117   FastTimer_InitializeEntry( &myTimer, myCallback, 0, 250, true );
    00118   FastTimer_Set( &myTimer ); // start our timer
    00119
    00120   void myCallback( int id ) // our code that will get called by the timer every 250 microseconds.
    00121   {
    00122     // do something here
    00123   }
    00124   \endcode
    00125 */
    00126 void FastTimer_InitializeEntry( FastTimerEntry* fastTimerEntry, void (*timerCallback)( int id ), int id, int timeUs, bool repeat )
    00127 {
    00128   int time = timeUs * FAST_TIMER_CYCLES_PER_US;
    00129
    00130   // Set the details into the free dude
    00131   fastTimerEntry->callback = timerCallback;
    00132   fastTimerEntry->id = id;
    00133   fastTimerEntry->timeCurrent = 0;
    00134   fastTimerEntry->timeInitial = time;
    00135   fastTimerEntry->repeat = repeat;
    00136   fastTimerEntry->next = NULL;
    00137 }
    00138
    00139 /**
    00140   * Change the requeted time of an entry.
    00141   * This must only be called within a callback caused by the Entry specified or when the
    00142   * entry is not being used.  If you need to change the duration of a timer, you need to cancel it
    00143   * and re-add it, or alter the time inside a callback.
    00144   @param fastTimerEntry A pointer to the FastTimerEntry to be intialized.
    00145   @param timeUs The time in microseconds desired for the callback.
    00146   */
    00147 void FastTimer_SetTime( FastTimerEntry* fastTimerEntry, int timeUs )
    00148 {
    00149   int time = timeUs * FAST_TIMER_CYCLES_PER_US;
    00150   fastTimerEntry->timeCurrent = time;
    00151   fastTimerEntry->timeInitial = time;
    00152 }
    00153
    00154 /** Sets the requested entry to run.
    00155   This routine adds the entry to the running queue and then decides if it needs
    00156   to start the timer (if it's not running) or alter the timer's clock for a shorter
    00157   period.
    00158   @param fastTimerEntry A pointer to the FastTimerEntry to be run.
    00159   */
    00160 int FastTimer_Set( FastTimerEntry* fastTimerEntry )
    00161 {
    00162   // this could be a lot smarter - for example, modifying the current period?
    00163   if ( !FastTimer.servicing )
    00164     TaskEnterCritical();
    00165
    00166   if ( !FastTimer.running )
    00167   {
    00168     FastTimer_SetActive( true );
    00169     FastTimer_SetTimeTarget( fastTimerEntry->timeInitial );
    00170     FastTimer_Enable();
    00171   }
    00172
    00173   // Calculate how long remaining
    00174   int target = FastTimer_GetTimeTarget();
    00175   int timeCurrent = FastTimer_GetTime();
    00176   int remaining = target - timeCurrent;
    00177
    00178   // Get the entry ready to roll
    00179   fastTimerEntry->timeCurrent = fastTimerEntry->timeInitial;
    00180
    00181   // Add entry
    00182   FastTimerEntry* first = FastTimer.first;
    00183   FastTimer.first = fastTimerEntry;
    00184   fastTimerEntry->next = first;
    00185
    00186   // Are we actually servicing an interupt right now?
    00187   if ( !FastTimer.servicing )
    00188   {
    00189     // No - so does the time requested by this new timer make the time need to come earlier?
    00190     if ( fastTimerEntry->timeCurrent < ( remaining - FASTTIMER_MARGIN ) )
    00191     {
    00192       // Damn it!  Reschedule the next callback
    00193       FastTimer_SetTimeTarget( target - ( remaining - fastTimerEntry->timeCurrent ));
    00194     }
    00195     else
    00196     {
    00197       // pretend that the existing time has been with us for the whole slice so that when the
    00198       // IRQ happens it credits the correct (reduced) time.
    00199       fastTimerEntry->timeCurrent += timeCurrent;
    00200     }
    00201   }
    00202   else
    00203   {
    00204     // Yep... we're servicing something right now
    00205
    00206     // Make sure the previous pointer is OK.  This comes up if we were servicing the first item
    00207     // and it subsequently wants to delete itself, it would need to alter the next pointer of the
    00208     // the new head... err... kind of a pain, this
    00209     if ( FastTimer.previous == NULL )
    00210       FastTimer.previous = fastTimerEntry;
    00211
    00212     // Need to make sure that if this new time is the lowest yet, that the IRQ routine
    00213     // knows that.  Since we added this entry onto the beginning of the list, the IRQ
    00214     // won't look at it again
    00215     if ( FastTimer.nextTime == -1 || FastTimer.nextTime > fastTimerEntry->timeCurrent )
    00216         FastTimer.nextTime = fastTimerEntry->timeCurrent;
    00217   }
    00218
    00219   if ( !FastTimer.servicing )
    00220     TaskExitCritical();
    00221
    00222   return CONTROLLER_OK;
    00223 }
    00224
    00225 /**
    00226   Stops the requested fast timer entry from running.
    00227   @param fastTimerEntry pointer to the FastTimerEntry to be cancelled.
    00228   */
    00229 int FastTimer_Cancel( FastTimerEntry* fastTimerEntry )
    00230 {
    00231   if ( !FastTimer.servicing )
    00232     TaskEnterCritical();
    00233
    00234   // Look through the running list - clobber the entry
    00235   FastTimerEntry* te = FastTimer.first;
    00236   FastTimerEntry* previousEntry = NULL;
    00237   while ( te != NULL )
    00238   {
    00239     // check for the requested entry
    00240     if ( te == fastTimerEntry )
    00241     {
    00242       // remove the entry from the list
    00243       if ( te == FastTimer.first )
    00244         FastTimer.first = te->next;
    00245       else
    00246         previousEntry->next = te->next;
    00247
    00248       // make sure the in-IRQ pointers are all OK
    00249       if ( FastTimer.servicing )
    00250       {
    00251         if ( FastTimer.previous == fastTimerEntry )
    00252           FastTimer.previous = previousEntry;
    00253         if ( FastTimer.next == fastTimerEntry )
    00254           FastTimer.next = te->next;
    00255       }
    00256
    00257       // update the pointers - leave previous where it is
    00258       te = te->next;
    00259     }
    00260     else
    00261     {
    00262       previousEntry = te;
    00263       te = te->next;
    00264     }
    00265   }
    00266
    00267   if ( !FastTimer.servicing )
    00268     TaskExitCritical();
    00269
    00270   return CONTROLLER_OK;
    00271 }
    00272
    00273 /** @}
    00274 */
    00275
    00276 //
    00277 // INTERNAL
    00278 //
    00279
    00280 /*
    00281 int FastTimer_GetCount()
    00282 {
    00283   int count;
    00284   TaskEnterCritical();
    00285   count = FastTimer.count;
    00286   FastTimer.count = 0;
    00287   TaskExitCritical();
    00288   return count;
    00289 }
    00290 */
    00291
    00292 // Enable the timer.  Disable is performed by the ISR when timer is at an end
    00293 void FastTimer_Enable( )
    00294 {
    00295   // Enable the device
    00296   // AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG;
    00297   AT91C_BASE_TC2->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG;
    00298   FastTimer.running = true;
    00299 }
    00300
    00301 int FastTimer_GetTimeTarget( )
    00302 {
    00303   return AT91C_BASE_TC2->TC_RC;
    00304 }
    00305
    00306 int FastTimer_GetTime( )
    00307 {
    00308   return AT91C_BASE_TC0->TC_CV;
    00309 }
    00310
    00311 void FastTimer_SetTimeTarget( int target )
    00312 {
    00313   AT91C_BASE_TC2->TC_RC = ( target < FASTTIMER_MAXCOUNT ) ? target : FASTTIMER_MAXCOUNT;
    00314 }
    00315
    00316 int FastTimer_Init()
    00317 {
    00318   FastTimer.first = NULL;
    00319
    00320   FastTimer.count = 0;
    00321   FastTimer.jitterTotal = 0;
    00322   FastTimer.jitterMax = 0;
    00323   FastTimer.jitterMaxAllDay = 0;
    00324
    00325   FastTimer.running = false;
    00326   FastTimer.servicing = false;
    00327
    00328   AT91C_BASE_PMC->PMC_PCER = 1 << AT91C_ID_TC2;
    00329
    00330   unsigned int mask ;
    00331   mask = 0x1 << AT91C_ID_TC2 | 0x01;
    00332
    00333   /* Disable the interrupt on the interrupt controller */
    00334   AT91C_BASE_AIC->AIC_IDCR = mask;
    00335
    00336   AT91C_BASE_AIC->AIC_SVR[ AT91C_ID_FIQ ] = (unsigned int)FastTimer_Isr;
    00337
    00338   /* Store the Source Mode Register */
    00339   AT91C_BASE_AIC->AIC_SMR[ AT91C_ID_TC2 ] = AT91C_AIC_SRCTYPE_INT_HIGH_LEVEL | 7  ;
    00340   /* Clear the interrupt on the interrupt controller */
    00341   AT91C_BASE_AIC->AIC_ICCR = mask ;
    00342
    00343   // Set the timer up.  We want just the basics, except when the timer compares
    00344   // with RC, retrigger
    00345   //
    00346   // MCK is 47923200
    00347   // DIV1: A tick MCK/2 times a second
    00348   // This makes every tick every 41.73344ns
    00349   // DIV2: A tick MCK/8 times a second
    00350   // This makes every tick every 167ns
    00351   // DIV3: A tick MCK/32 times a second
    00352   // This makes every tick every 668ns
    00353   // DIV4: A tick MCK/128 times a second
    00354   // This makes every tick every 2.671us
    00355   // DIV5: A tick MCK/1024 times a second
    00356   // This makes every tick every 21.368us
    00357   AT91C_BASE_TC2->TC_CMR = AT91C_TC_CLKS_TIMER_DIV2_CLOCK |  AT91C_TC_CPCTRG;
    00358
    00359   // Only interested in interrupts when the RC happens
    00360   AT91C_BASE_TC2->TC_IDR = 0xFF;
    00361   AT91C_BASE_TC2->TC_IER = AT91C_TC_CPCS;
    00362
    00363   // load the RC value with something
    00364   AT91C_BASE_TC2->TC_RC = FASTTIMER_MAXCOUNT;
    00365
    00366   // Make it fast forcing
    00367   AT91C_BASE_AIC->AIC_FFER = 0x1 << AT91C_ID_TC2;
    00368
    00369   // Enable the interrupt
    00370   AT91C_BASE_AIC->AIC_IECR = mask;
    00371
    00372   // Enable the device
    00373   AT91C_BASE_TC2->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG;
    00374
    00375   /// Finally, prep the IO flag if it's being used
    00376 #ifdef FASTIRQ_MONITOR_IO
    00377     Io_Start( FASTIRQ_MONITOR_IO, true );
    00378     Io_PioEnable( FASTIRQ_MONITOR_IO );
    00379     Io_SetOutput( FASTIRQ_MONITOR_IO );
    00380 #endif
    00381
    00382   return CONTROLLER_OK;
    00383 }
    00384
    00385 int FastTimer_Deinit()
    00386 {
    00387   return CONTROLLER_OK;
    00388 }

The Make Controller Kit is an open source project maintained by MakingThings.
MakingThings code is released under the Apache 2.0 license.
Bug tracker, development wiki and status can be found at http://dev.makingthings.com.
This document was last updated on 18 May 2009.