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.