MakingThings
  • 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.