00001 /* 00002 * "Copyright (c) 2008 Robert B. Reese, Bryan A. Jones, J. W. Bruce ("AUTHORS")" 00003 * All rights reserved. 00004 * (R. Reese, reese_AT_ece.msstate.edu, Mississippi State University) 00005 * (B. A. Jones, bjones_AT_ece.msstate.edu, Mississippi State University) 00006 * (J. W. Bruce, jwbruce_AT_ece.msstate.edu, Mississippi State University) 00007 * 00008 * Permission to use, copy, modify, and distribute this software and its 00009 * documentation for any purpose, without fee, and without written agreement is 00010 * hereby granted, provided that the above copyright notice, the following 00011 * two paragraphs and the authors appear in all copies of this software. 00012 * 00013 * IN NO EVENT SHALL THE "AUTHORS" BE LIABLE TO ANY PARTY FOR 00014 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT 00015 * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE "AUTHORS" 00016 * HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 00017 * 00018 * THE "AUTHORS" SPECIFICALLY DISCLAIMS ANY WARRANTIES, 00019 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 00020 * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS 00021 * ON AN "AS IS" BASIS, AND THE "AUTHORS" HAS NO OBLIGATION TO 00022 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS." 00023 * 00024 * Please maintain this header in its entirety when copying/modifying 00025 * these files. 00026 * 00027 * 00028 */ 00029 00030 00031 00032 // Documentation for this file. If the \file tag isn't present, 00033 // this file won't be documented. 00067 #include "pic24_all.h" 00068 00069 #if !USE_CLOCK_TIMEOUT 00070 //empty functions 00071 void checkClockTimeout(void) { 00072 } 00073 #else 00074 /* 00075 The purpose of the clock timeout functions is 00076 output a meaningful error in case the clock switch 00077 does not occur. 00078 00079 */ 00080 #define CLOCKTIMEOUT_MAX 200000 00081 #if defined(__PIC24H__) 00082 #define FRC_BRG ((40000000L/DEFAULT_BAUDRATE/16) - 1) 00083 #elif defined(__PIC24F__) 00084 #define FRC_BRG ((16000000L/DEFAULT_BAUDRATE/16) - 1) 00085 #else 00086 #error Unknown processor. 00087 #endif 00088 #define FRC_BRGH 0 00089 00090 00091 static void configFrcUART(void) { 00092 // First, switch to a known-good clock 00093 #if defined(__PIC24H__) 00094 configClockFRCPLL_FCY40MHz(); 00095 #elif defined(__PIC24F__) 00096 //safe choice: FCY=16 MHz, FRC+PLL 00097 configClockFRCPLL_FCY16MHz(); 00098 #else 00099 #error Unknown processor 00100 #endif 00101 00102 // Second, get UART I/O pins mapped and general config done. 00103 configDefaultUART(DEFAULT_BAUDRATE); 00104 // BRG register is probably wrong since not using defined FCY. Fix it. 00105 switch (DEFAULT_UART) { 00106 #if (NUM_UART_MODS >= 1) 00107 case 1 : 00108 U1BRG = FRC_BRG; 00109 U1MODEbits.BRGH = FRC_BRGH; 00110 break; 00111 #endif 00112 #if (NUM_UART_MODS >= 2) 00113 case 2 : 00114 U2BRG = FRC_BRG; 00115 U2MODEbits.BRGH = FRC_BRGH; 00116 break; 00117 #endif 00118 #if (NUM_UART_MODS >= 3) 00119 case 3 : 00120 U3BRG = FRC_BRG; 00121 U3MODEbits.BRGH = FRC_BRGH; 00122 break; 00123 #endif 00124 #if (NUM_UART_MODS >= 4) 00125 case 4 : 00126 U4BRG = FRC_BRG; 00127 U4MODEbits.BRGH = FRC_BRGH; 00128 break; 00129 #endif 00130 default : ASSERT(0); 00131 } 00132 } 00133 00134 static void checkClockTimeout(void) { 00135 static uint32 u32_timeoutCount = 0; 00136 00137 // See if the clock has already failed. If so, return to allow 00138 // diagnostic code to perform (hopefully safe) clock switches 00139 // in order to report errors. 00140 if (u32_timeoutCount == 0xFFFFFFFF) return; 00141 00142 // Otherwise, update timeout. If we the switch hasn't failed, 00143 // simple return to wait for the switch a bit more. 00144 u32_timeoutCount++; 00145 if (u32_timeoutCount < CLOCKTIMEOUT_MAX) return; 00146 00147 // Clock switch failed. Mark this in the timeout. 00148 u32_timeoutCount = 0xFFFFFFFF; 00149 00150 configFrcUART(); 00151 outString("\n\n" 00152 "Your clock choice failed to initialize, have switched to internal Fast RC oscillator +PLL.\n" 00153 "Check your setting for the 'CLOCK_CONFIG' macro.\n" 00154 "Watch the compiler output window when pic24_clockfreq.c is compiled, a warning message\n" 00155 "will tell you the selected value for 'CLOCK_CONFIG'.\n" 00156 "In MPLAB, use Project->Build Options->Project, then click on MPLAB C30 tab to see if \n" 00157 "the macro is defined there. If the macro is selecting an external crystal (the primary oscillator),\n" 00158 "and your board does not have a crystal, you will get this message.\n" 00159 "Delete the macro definition from the MPLAB project if you want to use the default \n" 00160 "clock choice of FRC + PLL.\n" 00161 "You must recompile and reprogram with an appropriate CLOCK_CONFIG choice for this code to execute.\n"); 00162 00163 while(1) { 00164 doHeartbeat(); // never return. 00165 } 00166 } 00167 #endif 00168 00169 00170 void switchClock(uint8 u8_source) { 00171 // Create a union that mirrors the OSCCON structure 00172 // with all its bit names but is also byte-accessable. 00173 OSCCONBITS OSCCONBITS_copy; 00174 00175 // Switch clock to use new choice specified by u8_choice. 00176 // Valid values are 0-7. 00177 // Throw an error if the source isn't in the list above. 00178 ASSERT(u8_source < 8); 00179 // 1. Disable interrupts per 7.11.2 FRM rev B under 00180 // "A recommended code sequence for a clock switch 00181 // includes the following:" heading. 00182 // Assumes there are no priority 7 interrupts enabled. 00183 asm("DISI #0x3FFF"); // Disable interrupts for a long time 00184 // 2. Switch to the PLL. Use compiler built-ins to unlock 00185 // clock switch registers. See 7.11.1 of the FRM rev B. 00186 OSCCONBITS_copy = OSCCONbits; // Copy OSCCON bits 00187 OSCCONBITS_copy.NOSC = u8_source; // Select new clock source 00188 OSCCONBITS_copy.OSWEN = 1; // Request clock switch 00189 // First write high byte, containing new clock source NOSC 00190 __builtin_write_OSCCONH(BITS2BYTEH(OSCCONBITS_copy)); 00191 // Then write low byte, requesting clock switch with OSWEN 00192 __builtin_write_OSCCONL(BITS2BYTEL(OSCCONBITS_copy)); 00193 asm("DISI #0"); // Re-enable them at the next instruction 00194 00195 #ifndef SIM 00196 // 3. Wait for switch to complete. 00197 // Note that oscillator switching is not supported by 00198 // the simulator, causing the statements below to 00199 // run forever. 00200 while (_OSWEN == 1) { 00201 checkClockTimeout(); 00202 } 00203 00204 // 4. Wait for the PLL to lock if using the PLL. 00205 // (Is this really necessary? It certainly can't hurt.) 00206 if ( (u8_source == GET_OSC_SEL_BITS(FNOSC_FRCPLL)) || 00207 (u8_source == GET_OSC_SEL_BITS(FNOSC_PRIPLL)) ) { 00208 while (_LOCK == 0); 00209 } 00210 #endif 00211 } 00212 00213 #if IS_CLOCK_CONFIG(SIM_CLOCK) 00214 #warning Clock configured for simulation, FCY = 1 Mhz 00215 #endif 00216 #if GET_IS_SUPPORTED(SIM_CLOCK) 00217 void configClockSim(void) { } 00218 #endif 00219 00220 00221 #if IS_CLOCK_CONFIG(FRCPLL_FCY16MHz) 00222 #warning Clock configured for FRCPLL, FCY = 16 MHz 00223 #endif 00224 #if GET_IS_SUPPORTED(FRCPLL_FCY16MHz) 00225 void configClockFRCPLL_FCY16MHz(void) { 00226 // To be safe: if this was run by a bootloader that chose FRCPLL mode, 00227 // then we can't change the bits below. To do so, first switch to FRC, 00228 // change bits, then switch back to FRCPLL. 00229 switchClock(GET_OSC_SEL_BITS(FNOSC_FRC)); 00230 // Two cases: 00231 // 1. Non-USB parts just have a FRC postscaler that feeds 00232 // the 4x PLL block. Set this postscaler to 1 since the 00233 // FRC runs at 8 MHz to get a 32 MHz FOSC = 16 MHz FCY. 00234 _RCDIV = 0; 00235 #ifdef _PLLDIV 00236 // 2. USB parts have a more complex clocking scheme. The 00237 // FRC postscaler feeds a PLL prescaler rather than 00238 // directly determining FOSC. The 00239 // PLL input must be 4 MHz, so choose a PLL prescaler 00240 // of 2 since the FRC runs at 8 MHz. 00241 _PLLDIV = 1; // 1 means a prescale of 2 00242 // The PLL multiplies this 4 MHz input to 96 MHz then 00243 // divides it by 3 to 32 MHz. A second PLL prescaler 00244 // then selects the final FOSC. Choose a prescale of 00245 // 1 so FOSC = 32 MHz, giving FCY = 16 MHz. 00246 _CPDIV = 0; // 0 means a prescale of 1 00247 #endif 00248 switchClock(GET_OSC_SEL_BITS(FNOSC_FRCPLL)); 00249 } 00250 #endif 00251 00252 00253 #if IS_CLOCK_CONFIG(FRC_FCY4MHz) 00254 #warning Clock configured for FRC, FCY = 4 MHz. 00255 #warning Baud rates of 19200 or lower recommended for this clock choice. 00256 #endif 00257 #if GET_IS_SUPPORTED(FRC_FCY4MHz) 00258 void configClockFRC_FCY4MHz(void) { 00259 // Ensure that the FRC postscaler is at '1' and not its reset default of '2' (PIC24F family) 00260 _RCDIV = 0; 00261 switchClock(GET_OSC_SEL_BITS(FNOSC_FRC)); 00262 } 00263 #endif 00264 00265 00266 #if IS_CLOCK_CONFIG(PRI_NO_PLL_7372KHzCrystal) 00267 #warning Clock configured for a 7.372 MHz crystal primary oscillator, no PLL 00268 #endif 00269 #if GET_IS_SUPPORTED(PRI_NO_PLL_7372KHzCrystal) 00270 void configClockPRI_NO_PLL_7372KHzCrystal(void) { 00271 switchClock(GET_OSC_SEL_BITS(FNOSC_PRI)); 00272 } 00273 #endif 00274 00275 00276 #if IS_CLOCK_CONFIG(FRC_FCY3685KHz) 00277 #warning Clock configured for FRC, FCY = 3.685 MHz 00278 #warning Baud rates of 9600 or lower recommended for this clock choice. 00279 #endif 00280 #if GET_IS_SUPPORTED(FRC_FCY3685KHz) 00281 void configClockFRC_FCY3685KHz(void) { 00282 switchClock(GET_OSC_SEL_BITS(FNOSC_FRC)); 00283 // Choose no tuning on FRC to get 7.37 MHz nominal FOSC. 00284 // Do after clock switch in case FRCPLL was in use, since 00285 // that would alter PLL input frequency. (Might be OK, but 00286 // this is perhaps safer.) 00287 _TUN = 0; 00288 } 00289 #endif 00290 00291 00292 #if IS_CLOCK_CONFIG(FRCPLL_FCY40MHz) 00293 #warning Clock configured for FRCPLL, FCY = 40 MHz 00294 #endif 00295 #if GET_IS_SUPPORTED(FRCPLL_FCY40MHz) 00296 void configClockFRCPLL_FCY40MHz(void) { 00297 // To be safe: if this was run by a bootloader that chose FRCPLL mode, 00298 // then we can't change the bits below. To do so, first switch to FRC, 00299 // change bits, then switch back to FRCPLL. 00300 switchClock(GET_OSC_SEL_BITS(FNOSC_FRC)); 00301 //settings for Cycle time = 40 MHz, internal oscillator with PLL 00302 // Tune the internal oscialltor to 6.85 MHz. Each unit 00303 // in the register below = 0.375% of the 7.37 MHz 00304 // nominal frequency in 2's complement per register 7-6 00305 // in section 7.4 of the FRM rev B. So: 00306 // Sanity check: -32*0.375% = -12% -> 6.4856 MHz per data sheet. 00307 // +30*0.375% = 11.25% -> 8.1991 Mhz not 8.23 MHz 00308 // given in the data sheet! 00309 // However, +31*0.375% = 11.625% -> 8.2268 MHz, so the above 00310 // is a typo. This error has been reported to Microchip and 00311 // confirmed. It should be fixed in next rev of data sheet. 00312 // Another concern: since the clock is +/- 2%, we could end 00313 // up with a > 8.0 MHz processor clock! At 8 MHz, this would 00314 // be 8.16 MHz, so the processor would run at 81.6 MHz. 00315 // Ignore this for now; probably, the chip will still run. 00316 00317 _TUN = -19; // Correct setting assuming the RC oscillator is exactly 7.37MHz. 00318 // It may need to be tweaked however. Use the echo.c program, and a baud rate 00319 // of 115,200 and increase/decrease TUN until you get no framing errors 00320 00321 // Choose PLL factors: Fref after first prescale must be 00322 // between 0.8 and 8.0 MHz. Choose a prescale of 8 00323 // for Fref of 0.856 MHz. 00324 _PLLPRE = 6; // Prescale = PLLPRE + 2 00325 // Fvco after multiply must be between 100 and 200 MHz. 00326 // Pick 160 MHz, so multiply by 187. 00327 _PLLDIV = 185; // Multiply = PLLDIV + 2 00328 // Final desired Fosc = 80 MHz for an Fcy = 40 MHz. 00329 // (See 7.7 of the FRM rev B). Pick 80 MHz, so postscale by 2. 00330 _PLLPOST = 0; // Postscale = 2 * (PLLPOST + 1) 00331 switchClock(GET_OSC_SEL_BITS(FNOSC_FRCPLL)); 00332 } 00333 #endif 00334 00335 #if IS_CLOCK_CONFIG(PRIPLL_7372KHzCrystal_40MHzFCY) 00336 #warning Clock configured for PRIPLL using a 7.3727 Mhz primary oscillator, FCY = 40 MHz 00337 #endif 00338 #if GET_IS_SUPPORTED(PRIPLL_7372KHzCrystal_40MHzFCY) 00339 void configClockPRIPLL_7372KHzCrystal_40MHzFCY(void) { 00340 // To be safe: if this was run by a bootloader that chose PRIPLL mode, 00341 // then we can't change the bits below. To do so, first switch to FRC, 00342 // change bits, then switch back to PRIPLL. 00343 switchClock(GET_OSC_SEL_BITS(FNOSC_FRC)); 00344 //settings for Cycle time = 40 MHz, primary oscillator with PLL 00345 _PLLPRE = 4; // Prescale = PLLPRE + 2 00346 _PLLDIV = 128; // Multiply = PLLDIV + 2 00347 _PLLPOST = 0; // Postscale = 2 * (PLLPOST + 1) 00348 switchClock(GET_OSC_SEL_BITS(FNOSC_PRIPLL)); 00349 } 00350 #endif 00351 00352 #if IS_CLOCK_CONFIG(PRIPLL_8MHzCrystal_40MHzFCY) 00353 #warning Clock configured for PRIPLL using an 8.0 Mhz primary oscillator, FCY = 40 MHz 00354 #endif 00355 #if GET_IS_SUPPORTED(PRIPLL_8MHzCrystal_40MHzFCY) 00356 void configClockPRIPLL_8MHzCrystal_40MHzFCY(void) { 00357 //settings for Cycle time = 40 MHz, primary oscillator with PLL 00358 //These PLL settings will give an FCY == Crystal Freq * 10/2, or FOSC = Crystal Freq * 10 00359 /* 00360 This settings assumes the external crystal on is 8.0MHz 00361 */ 00362 // To be safe: if this was run by a bootloader that chose PRIPLL mode, 00363 // then we can't change the bits below. To do so, first switch to FRC, 00364 // change bits, then switch back to PRIPLL. 00365 switchClock(GET_OSC_SEL_BITS(FNOSC_FRC)); 00366 _PLLPRE = 0; // Prescale = PLLPRE + 2 00367 _PLLDIV = 38; // Multiply = PLLDIV + 2 00368 _PLLPOST = 0; // Postscale = 2 * (PLLPOST + 1) 00369 switchClock(GET_OSC_SEL_BITS(FNOSC_PRIPLL)); 00370 } 00371 #endif 00372 00373 #if IS_CLOCK_CONFIG(PRIPLL_8MHzCrystal_16MHzFCY) 00374 #warning Clock configured for PRIPLL using a 8.0 Mhz primary oscillator, FCY = 16 MHz 00375 #endif 00376 #if GET_IS_SUPPORTED(PRIPLL_8MHzCrystal_16MHzFCY) 00377 void configClockPRIPLL_8MHzCrystal_16MHzFCY(void) { 00378 // To be safe: if this was run by a bootloader that chose FRCPLL mode, 00379 // then we can't change the bits below. To do so, first switch to FRC, 00380 // change bits, then switch back to FRCPLL. 00381 switchClock(GET_OSC_SEL_BITS(FNOSC_FRC)); 00382 // Two cases: 00383 // 1. Non-USB parts just have a FRC postscaler that feeds 00384 // the 4x PLL block. Set this postscaler to 1 since the 00385 // FRC runs at 8 MHz to get a 32 MHz FOSC = 16 MHz FCY. 00386 _RCDIV = 0; 00387 #ifdef _PLLDIV 00388 // 2. USB parts have a more complex clocking scheme. The 00389 // FRC postscaler feeds a PLL prescaler rather than 00390 // directly determining FOSC. The 00391 // PLL input must be 4 MHz, so choose a PLL prescaler 00392 // of 2 since the FRC runs at 8 MHz. 00393 _PLLDIV = 1; // 1 means a prescale of 2 00394 // The PLL multiplies this 4 MHz input to 96 MHz then 00395 // divides it by 3 to 32 MHz. A second PLL prescaler 00396 // then selects the final FOSC. Choose a prescale of 00397 // 1 so FOSC = 32 MHz, giving FCY = 16 MHz. 00398 _CPDIV = 0; // 0 means a prescale of 1 00399 #endif 00400 switchClock(GET_OSC_SEL_BITS(FNOSC_PRIPLL)); 00401 } 00402 #endif