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