MakingThings
  • Main Page
  • Related Pages
  • Modules
  • Data Structures
  • Files
  • File List
  • Globals

timer.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 timer.c 
00019   Functions to use the timer on the Make Controller Board.
00020   This uses the regular interrupt mechanism and as such may be blocked by other
00021   routines for significant numbers of milliseconds. 
00022 */
00023 
00024 #include "AT91SAM7X256.h"
00025 #include "types.h"
00026 #include "timer.h"
00027 #include "timer_internal.h"
00028 #include "config.h"
00029 #include "rtos.h"
00030 
00031 #define TIMER_CYCLES_PER_MS 48
00032 
00033 struct Timer_ Timer;
00034 
00035 static int Timer_Init( void );
00036 static int Timer_Deinit( void );
00037 //static int Timer_GetCount( void );
00038 static int Timer_GetTimeTarget( void );
00039 static int Timer_GetTime( void );
00040 static void Timer_SetTimeTarget( int );
00041 static void Timer_Enable( void );
00042 
00043 void Timer_Isr( void );
00044 
00045 /** \defgroup Timer Timer
00046 * The Timer subsystem provides a timer in a millisecond timeframe.
00047   For higher resolution timing, check the \ref FastTimer
00048 
00049   The Timer subsystem is based on a collection of \b TimerEntries.  To start a new timer, create a new
00050   \b TimerEntry structure, initialize it with Timer_InitializeEntry( ), and start it with Timer_Set( ).
00051 
00052   There are currently a couple of limitations to the Timer system:
00053   - In your callback function, you must not sleep or make any FreeRTOS-related calls.
00054   - To modify an existing TimerEntry, you must cancel the timer with Timer_Cancel( ), and reinitialize the TimerEntry.
00055 
00056   \todo Allow the timer callbacks to cooperate with the \ref RTOS
00057   \todo Allow existing timer entries to be modified (repeat or not, modify the period, etc.)
00058 * \ingroup Core
00059 * @{
00060 */
00061 
00062 /** 
00063   Controls the active state of the \b Timer subsystem
00064   @param active whether this subsystem is active or not
00065   @return Zero on success.
00066   @see Timer_Set, Timer_Cancel
00067 */
00068 int Timer_SetActive( bool active )
00069 {
00070   if ( active )
00071   {
00072     if ( Timer.users++ == 0 )
00073     {
00074       int status;
00075   
00076       status = Timer_Init();  
00077       if ( status != CONTROLLER_OK )
00078       {
00079         Timer.users--;
00080         return status;
00081       }
00082     }
00083   }
00084   else
00085   {
00086     if ( --Timer.users == 0 )
00087       Timer_Deinit();
00088   }
00089   return CONTROLLER_OK;
00090 }
00091 
00092 /** 
00093   Returns whether the timer subsystem is active or not
00094   @return active.
00095   @see Timer_Set, Timer_Cancel
00096 */
00097 int Timer_GetActive( )
00098 {
00099   return Timer.users > 0;
00100 }
00101 
00102 /** 
00103   Initializes a new timer structure.  
00104   The event is signified by a callback to the function provided, after the interval specified.  
00105   The specified ID is passed back to the function to permit one function to work for many events.  
00106   Pass repeat = true to make the event continue to create callbacks until it is canceled.
00107   @param timerEntry pointer to the TimerEntry to be intialized. 
00108   @param timerCallback pointer to the callback function.  The function must
00109          be of the form \verbatim void timerCallback( int id )\endverbatim
00110   @param id An integer specifying the ID the callback function is to be provided with.
00111   @param timeMs The time in milliseconds desired for the callback.
00112   @param repeat Set whether the timer repeats or is a one-time event.
00113   @see Timer_Cancel
00114 
00115   \par Example
00116   \code
00117   TimerEntry myTimer; // our TimerEntry
00118   Timer_InitializeEntry( &myTimer, myCallback, 0, 250, true );
00119   Timer_Set( &myTimer ); // start our timer
00120 
00121   void myCallback( int id ) // our code that will get called by the timer every 250 ms.
00122   {
00123     // do something here
00124   }
00125   \endcode
00126 */
00127 void Timer_InitializeEntry( TimerEntry* timerEntry, void (*timerCallback)( int id ), int id, int timeMs, bool repeat )
00128 {
00129   int time = timeMs * TIMER_CYCLES_PER_MS;
00130 
00131   // Set the details into the free dude
00132   timerEntry->callback = timerCallback;
00133   timerEntry->id = id;
00134   timerEntry->timeCurrent = 0;
00135   timerEntry->timeInitial = time;
00136   timerEntry->repeat = repeat;
00137   timerEntry->next = NULL;
00138 }
00139 
00140 /**
00141   Sets a given TimerEntry to run.
00142   This routine adds the entry to the running queue and then decides if it needs
00143   to start the timer (if it's not running) or alter the timer's clock for a shorter
00144   period.
00145   @param timerEntry pointer to the FastTimerEntry to be run. 
00146   */
00147 int Timer_Set( TimerEntry* timerEntry )
00148 {
00149   // this could be a lot smarter - for example, modifying the current period?
00150   if ( !Timer.servicing ) 
00151     TaskEnterCritical();
00152 
00153   if ( !Timer.running )
00154   {
00155     Timer_SetActive( true );
00156     Timer_SetTimeTarget( timerEntry->timeInitial );
00157     Timer_Enable();
00158   }  
00159 
00160   // Calculate how long remaining
00161   int target = Timer_GetTimeTarget();
00162   int timeCurrent = Timer_GetTime();
00163   int remaining = target - timeCurrent;
00164 
00165   // Get the entry ready to roll
00166   timerEntry->timeCurrent = timerEntry->timeInitial;
00167 
00168   // Add entry
00169   TimerEntry* first = Timer.first;
00170   Timer.first = timerEntry;
00171   timerEntry->next = first;
00172 
00173   // Are we actually servicing an interupt right now?
00174   if ( !Timer.servicing )
00175   {
00176     // No - so does the time requested by this new timer make the time need to come earlier?
00177     if ( timerEntry->timeCurrent < ( remaining - TIMER_MARGIN ) )
00178     {
00179       // Damn it!  Reschedule the next callback
00180       Timer_SetTimeTarget( target - ( remaining - timerEntry->timeCurrent ));
00181     }
00182     else
00183     {
00184       // pretend that the existing time has been with us for the whole slice so that when the 
00185       // IRQ happens it credits the correct (reduced) time.
00186       timerEntry->timeCurrent += timeCurrent;
00187     }
00188   }
00189   else
00190   {
00191     // Yep... we're servicing something right now
00192 
00193     // Make sure the previous pointer is OK.  This comes up if we were servicing the first item
00194     // and it subsequently wants to delete itself, it would need to alter the next pointer of the 
00195     // the new head... err... kind of a pain, this
00196     if ( Timer.previous == NULL )
00197       Timer.previous = timerEntry;
00198 
00199     // Need to make sure that if this new time is the lowest yet, that the IRQ routine 
00200     // knows that.  Since we added this entry onto the beginning of the list, the IRQ
00201     // won't look at it again
00202     if ( Timer.nextTime == -1 || Timer.nextTime > timerEntry->timeCurrent )
00203         Timer.nextTime = timerEntry->timeCurrent;
00204   }
00205 
00206   if ( !Timer.servicing ) 
00207     TaskExitCritical();
00208 
00209   return CONTROLLER_OK;
00210 }
00211 
00212 /** 
00213   Cancel a timer event.
00214   @param timerEntry The entry to be removed.
00215   @return 0 on success.
00216   @see Timer_Set
00217 */
00218 int Timer_Cancel( TimerEntry* timerEntry )
00219 {
00220   if ( !Timer.servicing ) 
00221     TaskEnterCritical();
00222 
00223   // Look through the running list - clobber the entry
00224   TimerEntry* te = Timer.first;
00225   TimerEntry* previousEntry = NULL;
00226   while ( te != NULL )
00227   {
00228     // check for the requested entry
00229     if ( te == timerEntry )
00230     {
00231       // remove the entry from the list
00232       if ( te == Timer.first )
00233         Timer.first = te->next;
00234       else
00235         previousEntry->next = te->next;
00236       
00237       // make sure the in-IRQ pointers are all OK
00238       if ( Timer.servicing )
00239       {
00240         if ( Timer.previous == timerEntry )
00241           Timer.previous = previousEntry;
00242         if ( Timer.next == timerEntry )
00243           Timer.next = te->next;
00244       }
00245 
00246       // update the pointers - leave previous where it is
00247       te = te->next;
00248     }
00249     else
00250     {
00251       previousEntry = te;
00252       te = te->next;
00253     }
00254   }
00255 
00256   if ( !Timer.servicing ) 
00257     TaskExitCritical();
00258 
00259   return CONTROLLER_OK;
00260 }
00261 
00262 /** @}
00263 */
00264 
00265 //
00266 // INTERNAL
00267 //
00268 
00269 /* commented out to avoid 'defined but not used' error
00270 int Timer_GetCount()
00271 {
00272   int count;
00273   TaskEnterCritical();
00274   count = Timer.count;
00275   Timer.count = 0;
00276   TaskExitCritical();
00277   return count;
00278 }
00279 */
00280 
00281 // Enable the timer.  Disable is performed by the ISR when timer is at an end
00282 void Timer_Enable( )
00283 {
00284   // Enable the device
00285   // AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG;
00286   AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG;
00287   Timer.running = true;
00288 }
00289 
00290 int Timer_GetTimeTarget( )
00291 {
00292   return AT91C_BASE_TC0->TC_RC;
00293 }
00294 
00295 int Timer_GetTime( )
00296 {
00297   return AT91C_BASE_TC0->TC_CV;
00298 }
00299 
00300 void Timer_SetTimeTarget( int target )
00301 {
00302   AT91C_BASE_TC0->TC_RC = ( target < 0xFFFF ) ? target : 0xFFFF;
00303 }
00304 
00305 int Timer_Init()
00306 {
00307   Timer.first = NULL;
00308 
00309   Timer.count = 0;
00310   Timer.jitterTotal = 0;
00311   Timer.jitterMax = 0;  
00312   Timer.jitterMaxAllDay = 0;
00313 
00314   Timer.running = false;
00315   Timer.servicing = false;
00316 
00317   // Configure TC by enabling PWM clock
00318   AT91C_BASE_PMC->PMC_PCER = 1 << AT91C_ID_TC0;
00319                                     
00320   unsigned int mask ;
00321   mask = 0x1 << AT91C_ID_TC0;
00322    
00323   /* Disable the interrupt on the interrupt controller */
00324   AT91C_BASE_AIC->AIC_IDCR = mask;
00325 
00326   /* Save the interrupt handler routine pointer */  
00327   AT91C_BASE_AIC->AIC_SVR[ AT91C_ID_TC0 ] = (unsigned int)Timer_Isr;
00328 
00329   /* Store the Source Mode Register */
00330   // 4 PRIORITY is random
00331   AT91C_BASE_AIC->AIC_SMR[ AT91C_ID_TC0 ] = AT91C_AIC_SRCTYPE_INT_HIGH_LEVEL | 4  ;
00332   /* Clear the interrupt on the interrupt controller */
00333   AT91C_BASE_AIC->AIC_ICCR = mask ;
00334 
00335   // Set the timer up.  We want just the basics, except when the timer compares 
00336   // with RC, retrigger
00337   //
00338   // MCK is 47923200
00339   // DIV1: A tick MCK/2 times a second
00340   // This makes every tick every 41.73344ns
00341   // DIV2: A tick MCK/8 times a second
00342   // This makes every tick every 167ns
00343   // DIV3: A tick MCK/32 times a second
00344   // This makes every tick every 668ns  
00345   // DIV4: A tick MCK/128 times a second
00346   // This makes every tick every 2.671us
00347   // DIV5: A tick MCK/1024 times a second
00348   // This makes every tick every 21.368us
00349   // CPCTRG makes the RC event reset the counter and trigger it to restart
00350   AT91C_BASE_TC0->TC_CMR = AT91C_TC_CLKS_TIMER_DIV5_CLOCK | AT91C_TC_CPCTRG;
00351                    
00352   // Only really interested in interrupts when the RC happens
00353   AT91C_BASE_TC0->TC_IDR = 0xFF; 
00354   AT91C_BASE_TC0->TC_IER = AT91C_TC_CPCS; 
00355 
00356   // load the RC value with something
00357   AT91C_BASE_TC0->TC_RC = 0xFFFF;
00358 
00359   // Enable the interrupt
00360   AT91C_BASE_AIC->AIC_IECR = mask;
00361 
00362   return CONTROLLER_OK;
00363 }
00364 
00365 int Timer_Deinit()
00366 {
00367   return CONTROLLER_OK;
00368 }

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.