Contiki 2.6

clocks.c

00001 /*
00002  * File: clocks.c
00003  * Description: STM32W108 internal, clock specific HAL functions
00004  * This file is provided for completeness and it should not be modified
00005  * by customers as it comtains code very tightly linked to undocumented
00006  * device features
00007  *
00008  * <!--(C) COPYRIGHT 2010 STMicroelectronics. All rights reserved.        -->
00009  */
00010 
00011 #include PLATFORM_HEADER
00012 #include "error.h"
00013 
00014 #include "hal/hal.h"
00015 #include "hal/micro/cortexm3/mpu.h"
00016 #include "hal/micro/cortexm3/mfg-token.h"
00017 
00018 
00019 //Provide a simple means for enabling calibration debug output
00020 #define CALDBG(x)
00021 //#define CALDBG(x) x
00022 
00023 //The slowest frequency for the 10kHz RC source is 8kHz (125us).  The PERIOD
00024 //register updates every 16 cycles, so to be safe 17 cycles = 2125us.  But,
00025 //we need twice this maximum time because the period measurement runs
00026 //asynchronously, and the value of CLKRC_TUNE is changed immediately before
00027 //the delay.
00028 #define SLOWRC_PERIOD_SETTLE_TIME 4250
00029 //The CLK_PERIOD register measures the number of 12MHz clock cycles that
00030 //occur in 16 cycles of the SlowRC clock.  This is meant to smooth out the the
00031 //noise inherently present in the analog RC source.  While these 16 cycles
00032 //smooths out most noise, there is still some jitter in the bottom bits of
00033 //CLK_PERIOD.  To further smooth out the noise, we take several readings of
00034 //CLK_PERIOD and average them out.  Testing has shown that the bottom 3 and 4
00035 //bits of CLK_PERIOD contain most of the jitter.  Averaging 8 samples will
00036 //smooth out 3 bits of jitter and provide a realiable and stable reading useful
00037 //in the calculations, while taking much less time than 16 or 32 samples.
00038 #define SLOWRC_PERIOD_SAMPLES 8
00039 //The register CLK1K_CAL is a fractional divider that divides the 10kHz analog
00040 //source with the goal of generating a 1024Hz, clk1k output.
00041 //  10000Hz / CLK1K_CAL = 1024Hz.
00042 //Since the CLK_PERIOD register measures the number of 12MHz cycles in 16
00043 //cycles of the RC:
00044 //     16 * 12000000
00045 //     ------------- = ~10kHz
00046 //      CLK_PERIOD
00047 //and
00048 //  ~10kHz / 1024 = X
00049 //where X is the fractional number that belongs in CLK1K_CAL.  Since the
00050 //integer portion of CLK1K_CAL is bits 15:11 and the fractional is 10:0,
00051 //multiplying X by 2048 (bit shift left by 11) generates the proper CLK1K_CAL
00052 //register value.
00053 //
00054 //Putting this all together:
00055 //     16 * 12000000 * 2048     384000000
00056 //     --------------------  = ------------  =  CLK1K_CAL
00057 //      CLK_PERIOD * 1024       CLK_PERIOD
00058 //
00059 #define CLK1K_NUMERATOR 384000000
00060 void halInternalCalibrateSlowRc( void )
00061 {
00062   int8u i;
00063   int32u average=0;
00064   int16s delta;
00065   int32u period;
00066   
00067   CALDBG(
00068     stSerialPrintf(ST_ASSERT_SERIAL_PORT, "halInternalCalibrateSlowRc:\r\n");
00069   )
00070   
00071   ////---- STEP 1: coarsely tune SlowRC in analog section to ~10kHz ----////
00072     //To operate properly across the full temperature and voltage range,
00073     //the RC source in the analog section needs to be first coarsely tuned
00074     //to 10kHz.  The CLKRC_TUNE register, which is 2's compliment, provides 16
00075     //steps at ~400Hz per step yielding approximate frequences of 8kHz at 7
00076     //and 15kHz at -8.
00077     //Start with our reset values for TUNE and CAL
00078     CLK_PERIODMODE = 0; //measure SlowRC
00079     CLKRC_TUNE = CLKRC_TUNE_RESET;
00080     CLK1K_CAL = CLK1K_CAL_RESET;
00081     //wait for the PERIOD register to properly update
00082     halCommonDelayMicroseconds(SLOWRC_PERIOD_SETTLE_TIME);
00083     //Measure the current CLK_PERIOD to obtain a baseline
00084     CALDBG(
00085       stSerialPrintf(ST_ASSERT_SERIAL_PORT,
00086       "period: %u, ", CLK_PERIOD);
00087       stSerialPrintf(ST_ASSERT_SERIAL_PORT, "%u Hz\r\n", 
00088                        ((int16u)(((int32u)192000000)/((int32u)CLK_PERIOD))));
00089     )
00090     //For 10kHz, the ideal CLK_PERIOD is 19200.  Calculate the PERIOD delta.
00091     //It's possible for a chip's 10kHz source RC to be too far out of range
00092     //for the CLKRC_TUNE to bring it back to 10kHz.  Therefore, we have to
00093     //ensure that our delta correction does not exceed the tune range so
00094     //tune has to be capped to the end of the vailable range so it does not
00095     //wrap.  Even if we cannot achieve 10kHz, the 1kHz calibration can still
00096     //properly correct to 1kHz.
00097     //Each CLKRC_TUNE step yields a CLK_PERIOD delta of *approximately* 800.
00098     //Calculate how many steps we are off.  While dividing by 800 may seem
00099     //like an ugly calculation, the precision of the result is worth the small
00100     //bit of code and time needed to do a divide.
00101     period = CLK_PERIOD;
00102     //Round to the nearest integer
00103     delta = (19200+400) - period;
00104     delta /= 800;
00105     //CLKRC_TUNE is a 4 bit signed number.  cap the delta to 7/-8
00106     if(delta > 7) {
00107       delta = 7;
00108     }
00109     if(delta < -8) {
00110       delta = -8;
00111     }
00112     CALDBG(
00113       stSerialPrintf(ST_ASSERT_SERIAL_PORT, "TUNE steps delta: %d\r\n",
00114                         delta);
00115     )
00116     CLKRC_TUNE = delta;
00117     //wait for PERIOD to update before taking another sample
00118     halCommonDelayMicroseconds(SLOWRC_PERIOD_SETTLE_TIME);
00119     CALDBG(
00120       stSerialPrintf(ST_ASSERT_SERIAL_PORT,
00121       "period: %u, ", CLK_PERIOD);
00122       stSerialPrintf(ST_ASSERT_SERIAL_PORT, "%u Hz\r\n", 
00123                        ((int16u)(((int32u)192000000)/((int32u)CLK_PERIOD))));
00124     )
00125     //The analog section should now be producing an output of ~10kHz
00126     
00127   ////---- STEP 2: fine tune the SlowRC to 1024Hz ----////
00128     //Our goal is to generate a 1024Hz source.  The register CLK1K_CAL is a
00129     //fractional divider that divides the 10kHz analog source and generates
00130     //the clk1k output.  At reset, the default value is 0x5000 which yields a
00131     //division of 10.000.  By averaging several samples of CLK_PERIOD, we
00132     //can then calculate the proper divisor need for CLK1K_CAL to make 1024Hz.
00133     for(i=0;i<SLOWRC_PERIOD_SAMPLES;i++) {
00134       halCommonDelayMicroseconds(SLOWRC_PERIOD_SETTLE_TIME);
00135       average += CLK_PERIOD;
00136     }
00137     //calculate the average, with proper rounding
00138     average = (average+(SLOWRC_PERIOD_SAMPLES/2))/SLOWRC_PERIOD_SAMPLES;
00139     CALDBG(
00140       stSerialPrintf(ST_ASSERT_SERIAL_PORT, "average: %u, %u Hz\r\n",
00141         ((int16u)average), ((int16u)(((int32u)192000000)/((int32u)average))));
00142     )
00143     
00144     //using an average period sample, calculate the clk1k divisor
00145     CLK1K_CAL = (int16u)(CLK1K_NUMERATOR/average);
00146     CALDBG(
00147       stSerialPrintf(ST_ASSERT_SERIAL_PORT,"CLK1K_CAL=%2X\r\n",CLK1K_CAL);
00148     )
00149     //The SlowRC timer is now producing a 1024Hz tick (+/-2Hz).
00150     
00151   CALDBG(
00152     stSerialPrintf(ST_ASSERT_SERIAL_PORT, "DONE\r\n");
00153   )
00154 }
00155 
00156 
00157 //The slowest frequency for the FastRC source is 4MHz (250ns).  The PERIOD
00158 //register updates every 256 cycles, so to be safe 257 cycles = 64us.  But,
00159 //we need twice this maximum time because the period measurement runs
00160 //asynchronously, and the value of OSCHF_TUNE is changed immediately before
00161 //the delay.
00162 #define FASTRC_PERIOD_SETTLE_TIME 128
00163 //The CLK_PERIOD register measures the number of 12MHz cycles in 256
00164 //cycles of OSCHF:
00165 //     256 * 12000000
00166 //     ------------- = ~12MHz
00167 //      CLK_PERIOD
00168 void halInternalCalibrateFastRc(void)
00169 {
00170   int32s newTune = -16;
00171   
00172   CALDBG(
00173     stSerialPrintf(ST_ASSERT_SERIAL_PORT, "halInternalCalibrateFastRc:\r\n");
00174   )
00175   
00176   ////---- coarsely tune FastRC in analog section to ~12MHz ----////
00177     //The RC source in the analog section needs to be coarsely tuned
00178     //to 12MHz.  The OSCHF_TUNE register, which is 2's compliment, provides 32
00179     //steps at ~0.5MHz per step yielding approximate frequences of 4MHz at 15
00180     //and 20MHz at -16.
00181     CLK_PERIODMODE = 1; //measure FastRC
00182     CALDBG(
00183       //start at the fastest possible frequency
00184       OSCHF_TUNE = newTune;
00185       //wait for the PERIOD register to properly update
00186       halCommonDelayMicroseconds(FASTRC_PERIOD_SETTLE_TIME);
00187       //Measure the current CLK_PERIOD to obtain a baseline
00188       stSerialPrintf(ST_ASSERT_SERIAL_PORT,
00189       "period: %u, ", CLK_PERIOD);
00190       stSerialPrintf(ST_ASSERT_SERIAL_PORT, "%u kHz\r\n", 
00191                        ((int16u)((((int32u)3072000000)/((int32u)CLK_PERIOD))/1000)));
00192     )
00193     //For 12MHz, the ideal CLK_PERIOD is 256.  Tune the frequency down until
00194     //the period is <= 256, which says the frequency is as close to 12MHz as
00195     //possible (without going over 12MHz)
00196     //Start at the fastest possible frequency (-16) and increase to the slowest
00197     //possible (15).  When CLK_PERIOD is <=256 or we run out of tune values,
00198     //we're done.
00199     for(;newTune<16;newTune++) {
00200       //decrease frequency by one step (by increasing tune value)
00201       OSCHF_TUNE = newTune;
00202       //wait for the PERIOD register to properly update
00203       halCommonDelayMicroseconds(FASTRC_PERIOD_SETTLE_TIME);
00204       //kickout if we're tuned
00205       if(CLK_PERIOD>=256) {
00206         break;
00207       }
00208     }
00209     CALDBG(
00210       //Measure the current CLK_PERIOD to show the final result
00211       stSerialPrintf(ST_ASSERT_SERIAL_PORT,
00212       "period: %u, ", CLK_PERIOD);
00213       stSerialPrintf(ST_ASSERT_SERIAL_PORT, "%u kHz\r\n", 
00214                        ((int16u)((((int32u)3072000000)/((int32u)CLK_PERIOD))/1000)));
00215     )
00216     
00217     //The analog section should now be producing an output of 11.5MHz - 12.0MHz
00218 }
00219 
00220 
00221 
00222 
00223 
00224 
00225 
00226 
00227 
00228 
00229 
00230 
00231 
00232 
00233 
00234 
00235 
00236 
00237 
00238 
00239 
00240 
00241 
00242 
00243 
00244 
00245 
00246 
00247 
00248 
00249 
00250 
00251 
00252 
00253 
00254 
00255 
00256 
00257 
00258 
00259 
00260 
00261 #define OSC24M_BIASTRIM_OFFSET  (0x2)
00262 #define OSC24M_BIASTRIM_MIN     (0+OSC24M_BIASTRIM_OFFSET)
00263 #define OSC24M_BIASTRIM_MAX     OSC24M_BIASTRIM_OSC24M_BIAS_TRIM_MASK
00264 #define OSC24M_BIASTRIM_MSB     (1 << (OSC24M_BIASTRIM_OSC24M_BIAS_TRIM_BITS-1))
00265 #define OSC24M_BIASTRIM_UNINIT  (0xFFFF)
00266 tokTypeMfgOsc24mBiasTrim biasTrim=OSC24M_BIASTRIM_UNINIT;
00267 
00268 
00269 
00270 
00271 
00272 
00273 
00274 
00275 
00276 
00277 
00278 
00279 
00280 
00281 
00282 
00283 
00284 
00285 
00286 
00287 
00288 
00289 
00290 
00291 
00292 
00293 
00294 
00295 
00296 
00297 
00298 
00299 
00300 
00301 //This function is intended to be called periodically, from the stack and
00302 //application, to check the XTAL bias trim is within appropriate levels
00303 //and adjust if not.  This function is *not* designed to be used before
00304 //halInternalSwitchToXtal has been called.
00305 void halCommonCheckXtalBiasTrim(void)
00306 {
00307   //HI is set indicating the trim value is too high.  Decrement the trim.
00308   if((OSC24M_COMP & OSC24M_HI) == OSC24M_HI) {
00309     biasTrim--;
00310   }
00311   
00312   //LO is cleared indicating the trim value is too low.  Inrement the trim.
00313   if((OSC24M_COMP & OSC24M_LO) != OSC24M_LO) {
00314     biasTrim++;
00315     //Add an offset to the bias trim as a factor of safety.
00316     if(biasTrim < (OSC24M_BIASTRIM_MAX - OSC24M_BIASTRIM_OFFSET)) {
00317       biasTrim +=  OSC24M_BIASTRIM_OFFSET;
00318     } else {
00319       biasTrim = OSC24M_BIASTRIM_MAX;
00320     }
00321   }
00322   
00323   //Don't allow bias trim to dip below the offset regardless of LO.
00324   if(biasTrim<OSC24M_BIASTRIM_OFFSET) {
00325     biasTrim = OSC24M_BIASTRIM_OFFSET;
00326   }
00327   
00328   OSC24M_BIASTRIM = biasTrim;
00329 }
00330 
00331 static boolean setBiasCheckLow(void)
00332 {
00333   OSC24M_BIASTRIM = biasTrim;
00334   halCommonDelayMicroseconds(1500);
00335   return ((OSC24M_COMP & OSC24M_LO) == OSC24M_LO);
00336 }
00337 
00338 void halInternalSearchForBiasTrim(void)
00339 {
00340   int8u bit;
00341   
00342   //Enable the XTAL so we can search for the proper bias trim (NOTE: This
00343   //will also forcefully ensure we're on the OSCHF so that we don't
00344   //accidentally trip the NMI while searching.)
00345   OSC24M_CTRL = OSC24M_CTRL_OSC24M_EN;
00346   
00347   //Do a binary search of the 4-bit bias trim values to find
00348   //smallest bias trim value for which LO = 1.
00349   biasTrim = 0;
00350   bit = (OSC24M_BIASTRIM_MSB << 1);
00351   do {
00352     bit >>= 1;
00353     biasTrim += bit;
00354     //Set trim and wait for 1.5ms to allow the oscillator to stabilize.
00355     if(setBiasCheckLow()) {
00356       biasTrim -= bit;
00357     }
00358   } while(bit);
00359   
00360   //If the last bias value went too low, increment it.
00361   if((OSC24M_COMP & OSC24M_LO) != OSC24M_LO) {
00362     biasTrim++;
00363   }
00364   
00365   //Add an offset to the bias trim as a factor of safety.
00366   if(biasTrim < (OSC24M_BIASTRIM_MAX - OSC24M_BIASTRIM_OFFSET)) {
00367     biasTrim +=  OSC24M_BIASTRIM_OFFSET;
00368   } else {
00369     biasTrim = OSC24M_BIASTRIM_MAX;
00370   }
00371   
00372   //Using the shadow variable, the clock switch logic will take over from here,
00373   //enabling, verifying, and tweaking as needed.
00374 }
00375 
00376 
00377 //This function configures the flash access controller for optimal
00378 //current consumption when FCLK is operating at 24MHz.  By providing
00379 //this function the calling code does not have to be aware of the
00380 //details of setting FLASH_ACCESS.
00381 static void halInternalConfigXtal24MhzFlashAccess(void)
00382 {
00383   ATOMIC(
00384     BYPASS_MPU( 
00385       #if defined(CORTEXM3_STM32W108)
00386         FLASH_ACCESS = (FLASH_ACCESS_PREFETCH_EN          |
00387                         (1<<FLASH_ACCESS_CODE_LATENCY_BIT));
00388       #endif
00389     )
00390   )
00391 } 
00392 
00393 //NOTE:  The global "shadow" variable biasTrim will be set by either:
00394 // A) TOKEN_MFG_OSC24M_BIAS_TRIM when booting fresh
00395 // B) searchForBiasTrim() when booting fresh and the token is not valid 
00396 // C) halInternalSwitchToXtal() if halInternalSwitchToXtal() already ran
00397 void halInternalSwitchToXtal(void)
00398 {
00399   boolean loSet;
00400   boolean hiSet;
00401   boolean setTrimOneLastTime = FALSE;
00402   
00403   //If it hasn't yet been initialized, 
00404   //preload our biasTrim shadow variable from the token.  If the token is
00405   //not set, then run a search to find an initial value.  The bias trim
00406   //algorithm/clock switch logic will always use the biasTrim shadow
00407   //variable as the starting point for finding the bias, and then
00408   //save that new bias to the shadow variable.
00409   if(biasTrim == OSC24M_BIASTRIM_UNINIT) {
00410     halCommonGetMfgToken(&biasTrim, TOKEN_MFG_OSC24M_BIAS_TRIM);
00411     if(biasTrim == 0xFFFF) {
00412       halInternalSearchForBiasTrim();
00413     }
00414   }
00415 
00416   //Ensure the XTAL is enabled (with the side effect of ensuring we're
00417   //still on OSCHF).
00418   OSC24M_CTRL = OSC24M_CTRL_OSC24M_EN;
00419   
00420   do {
00421     //Set trim to our shadow variable and wait for 1.5ms to allow the
00422     //oscillator to stabilize.
00423     loSet = setBiasCheckLow();
00424     hiSet = (OSC24M_COMP & OSC24M_HI) == OSC24M_HI;
00425     
00426     //The bias is too low, so we need to increment the bias trim.
00427     if(!loSet) {
00428       biasTrim++;
00429     }
00430     
00431     //The bias is too high, so we need to decrement the bias trim.
00432     if(hiSet) {
00433       //but don't trim below our min value
00434       if(biasTrim>OSC24M_BIASTRIM_MIN) {
00435         biasTrim--;
00436         setTrimOneLastTime = TRUE;
00437       }
00438     }
00439     
00440     //Kickout when HI=0 and LO=1 or we've hit the MAX or the MIN
00441   } while( (hiSet || !loSet)              &&
00442            (biasTrim<OSC24M_BIASTRIM_MAX) &&
00443            (biasTrim>OSC24M_BIASTRIM_MIN) );
00444   
00445   //The LO bit being cleared means we've corrected up from the bottom and
00446   //therefore need to apply the offset.  Additionally, if our trim value
00447   //is below the offset, we still need to apply the offset.  And, when
00448   //applying the offset respect the max possible value of the trim.
00449   if(!loSet || (biasTrim<OSC24M_BIASTRIM_OFFSET)){  
00450     if(biasTrim < (OSC24M_BIASTRIM_MAX - OSC24M_BIASTRIM_OFFSET)) {
00451       biasTrim +=  OSC24M_BIASTRIM_OFFSET;
00452     } else {
00453       biasTrim = OSC24M_BIASTRIM_MAX;
00454     }
00455     setTrimOneLastTime = TRUE;
00456   }
00457   
00458   if(setTrimOneLastTime) {
00459     setBiasCheckLow();
00460   }
00461   
00462   //We've found a valid trim value and we've waited for the oscillator
00463   //to stabalize, it's now safe to select the XTAL
00464   OSC24M_CTRL |= OSC24M_CTRL_OSC24M_SEL;
00465   
00466   //If the XTAL switch failed, the NMI ISR will trigger, creeping the bias
00467   //trim up higher, and if max bias is reached the ISR will trigger a reset.
00468   
00469   //Our standard mode of operation is 24MHz (CPU/FCLK is sourced from SYSCLK)
00470   CPU_CLKSEL = CPU_CLKSEL_FIELD;
00471   //Configure flash access for optimal current consumption at 24MHz
00472   halInternalConfigXtal24MhzFlashAccess();
00473 }