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.