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 }