pic24_clockfreq.c

Go to the documentation of this file.
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 200000L
00081 #if  ( defined(__PIC24H__) || defined(__dsPIC33F__) )
00082 #define FRC_FCY 40000000L
00083 #elif ( defined(__PIC24F__) || defined(__PIC24FK__) )
00084 #define FRC_FCY 16000000L
00085 #else
00086 #error Unknown processor.
00087 #endif
00088 #define FRC_BRGH 0
00089 
00090 
00091 static void configFrcUART(void) {
00092   float f_brg;
00093   uint16 UxBRG;
00094   // First, switch to a known-good clock
00095 #if ( defined(__PIC24H__) || defined(__dsPIC33F__) )
00096   configClockFRCPLL_FCY40MHz();
00097 #elif ( defined(__PIC24F__) || defined(__PIC24FK__) )
00098   //safe choice: FCY=16 MHz, FRC+PLL
00099   configClockFRCPLL_FCY16MHz();
00100 #else
00101 #error Unknown processor
00102 #endif
00103 
00104   // Second, get UART I/O pins mapped and general config done.
00105   configDefaultUART(DEFAULT_BAUDRATE);
00106   // BRG register is probably wrong since not using defined FCY. Fix it.
00107 #if (FRC_BRGH == 0)
00108   f_brg = (((float) FRC_FCY)/((float) DEFAULT_BAUDRATE)/16.0) - 1.0;
00109 #else
00110   f_brg = (((float) FRC_FCY)/((float) DEFAULT_BAUDRATE)/4.0) - 1.0;
00111 #endif
00112   UxBRG = roundFloatToUint16(f_brg);
00113   switch (DEFAULT_UART) {
00114 #if (NUM_UART_MODS >= 1)
00115     case 1 :
00116       U1BRG = UxBRG;
00117       U1MODEbits.BRGH = FRC_BRGH;
00118       break;
00119 #endif
00120 #if (NUM_UART_MODS >= 2)
00121     case 2 :
00122       U2BRG = UxBRG;
00123       U2MODEbits.BRGH = FRC_BRGH;
00124       break;
00125 #endif
00126 #if (NUM_UART_MODS >= 3)
00127     case 3 :
00128       U3BRG = UxBRG;
00129       U3MODEbits.BRGH = FRC_BRGH;
00130       break;
00131 #endif
00132 #if (NUM_UART_MODS >= 4)
00133     case 4 :
00134       U4BRG = UxBRG;
00135       U4MODEbits.BRGH = FRC_BRGH;
00136       break;
00137 #endif
00138     default : ASSERT(0);
00139  }
00140 }
00141 
00142 static void checkClockTimeout(void) {
00143   static uint32 u32_timeoutCount = 0;
00144   
00145   // See if the clock has already failed. If so, return to allow
00146   // diagnostic code to perform (hopefully safe) clock switches
00147   // in order to report errors.
00148   if (u32_timeoutCount == 0xFFFFFFFF) return;
00149 
00150   // Otherwise, update timeout. If we the switch hasn't failed,
00151   // simple return to wait for the switch a bit more.
00152   u32_timeoutCount++;
00153   if (u32_timeoutCount < CLOCKTIMEOUT_MAX) return;
00154 
00155   // Clock switch failed. Mark this in the timeout.
00156   u32_timeoutCount = 0xFFFFFFFF;
00157 
00158   configFrcUART();
00159   outString("\n\n"
00160             "Your clock choice failed to initialize, have switched to internal Fast RC oscillator +PLL.\n"
00161             "Check your setting for the 'CLOCK_CONFIG' macro.\n"
00162             "Watch the compiler output window when pic24_clockfreq.c is compiled, a warning message\n"
00163             "will tell you the selected value for 'CLOCK_CONFIG'.\n"
00164             "In MPLAB, use Project->Build Options->Project, then click on MPLAB C30 tab to see if \n"
00165             "the macro is defined there. If the macro is selecting an external crystal (the primary oscillator),\n"
00166             "and your board does not have a crystal, you will get this message.\n"
00167             "Delete the macro definition from the MPLAB project if you want to use the default \n"
00168             "clock choice of FRC + PLL.\n"
00169             "You must recompile and reprogram with an appropriate CLOCK_CONFIG choice for this code to execute.\n");
00170 
00171   while(1) {
00172     doHeartbeat();  // never return.
00173   }
00174 }
00175 #endif
00176 
00177 
00178 void switchClock(uint8 u8_source) {
00179   // Create a union that mirrors the OSCCON structure
00180   // with all its bit names but is also byte-accessable.
00181   OSCCONBITS OSCCONBITS_copy;
00182 
00183   // Switch clock to use new choice specified by u8_choice.
00184   // Valid values are 0-7.
00185   // Throw an error if the source isn't in the list above.
00186   ASSERT(u8_source < 8);
00187   // 1. Disable interrupts per 7.11.2 FRM rev B under
00188   //    "A recommended code sequence for a clock switch
00189   //     includes the following:" heading.
00190   //    Assumes there are no priority 7 interrupts enabled.
00191   asm("DISI #0x3FFF"); // Disable interrupts for a long time
00192   // 2. Switch to the PLL. Use compiler built-ins to unlock
00193   //    clock switch registers. See 7.11.1 of the FRM rev B.
00194   OSCCONBITS_copy = OSCCONbits;      // Copy OSCCON bits
00195   OSCCONBITS_copy.NOSC = u8_source;  // Select new clock source
00196   OSCCONBITS_copy.OSWEN = 1;         // Request clock switch
00197   // First write high byte, containing new clock source NOSC
00198   __builtin_write_OSCCONH(BITS2BYTEH(OSCCONBITS_copy));
00199   // Then write low byte, requesting clock switch with OSWEN
00200   __builtin_write_OSCCONL(BITS2BYTEL(OSCCONBITS_copy));
00201   asm("DISI #0");     // Re-enable them at the next instruction
00202 
00203 #ifndef SIM
00204   // 3. Wait for switch to complete.
00205   //    Note that oscillator switching is not supported by
00206   //    the simulator, causing the statements below to
00207   //    run forever.
00208   while (_OSWEN == 1) {
00209     checkClockTimeout();
00210   }
00211 
00212   // 4. Wait for the PLL to lock if using the PLL.
00213   // (Is this really necessary? It certainly can't hurt.)
00214   if ( (u8_source == GET_OSC_SEL_BITS(FNOSC_FRCPLL)) ||
00215        (u8_source == GET_OSC_SEL_BITS(FNOSC_PRIPLL)) ) {
00216     while (_LOCK == 0) {
00217       checkClockTimeout();
00218     }
00219   }
00220 
00221   // 5. Make sure clock switch worked
00222   while (_COSC != u8_source) checkClockTimeout();
00223 #endif
00224 }
00225 
00226 #if IS_CLOCK_CONFIG(SIM_CLOCK)
00227 #warning Clock configured for simulation, FCY = 1 Mhz
00228 #endif
00229 #if GET_IS_SUPPORTED(SIM_CLOCK)
00230 void configClockSim(void) { }
00231 #endif
00232 
00233 
00234 #if IS_CLOCK_CONFIG(FRCPLL_FCY16MHz)
00235 #warning Clock configured for FRCPLL, FCY = 16 MHz
00236 #endif
00237 #if GET_IS_SUPPORTED(FRCPLL_FCY16MHz)
00238 void configClockFRCPLL_FCY16MHz(void) {
00239   // To be safe: if this was run by a bootloader that chose FRCPLL mode,
00240   // then we can't change the bits below. To do so, first switch to FRC,
00241   // change bits, then switch back to FRCPLL.
00242   switchClock(GET_OSC_SEL_BITS(FNOSC_FRC));
00243   // Two cases:
00244   //   1. Non-USB parts just have a FRC postscaler that feeds
00245   //      the 4x PLL block. Set this postscaler to 1 since the
00246   //      FRC runs at 8 MHz to get a 32 MHz FOSC = 16 MHz FCY.
00247   _RCDIV = 0;
00248 #ifdef _CPDIV
00249   //      The PLL multiplies this 4 MHz input to 96 MHz then
00250   //      divides it by 3 to 32 MHz. A second PLL prescaler
00251   //      then selects the final FOSC. Choose a prescale of
00252   //      1 so FOSC = 32 MHz, giving FCY = 16 MHz.
00253   _CPDIV = 0;  // 0 means a prescale of 1
00254 #endif
00255 #ifdef _PLLDIV
00256   //   2. USB parts have a more complex clocking scheme. The
00257   //      FRC postscaler feeds a PLL prescaler rather than
00258   //      directly determining FOSC. The
00259   //      PLL input must be 4 MHz, so choose a PLL prescaler
00260   //      of 2 since the FRC runs at 8 MHz.
00261   _PLLDIV = 1;  // 1 means a prescale of 2
00262 #elif defined(PLLDIV_NODIV)
00263 #warning Ensure that the PLLDIV value is set to divide by 2 in the configuration bits for FRCPLL_FCY16MHz clock option!!
00264 #endif
00265 #ifdef _PLLEN
00266   _PLLEN = 1;
00267 #warning PLL Enabled
00268 #endif
00269   switchClock(GET_OSC_SEL_BITS(FNOSC_FRCPLL));
00270 }
00271 #endif
00272 
00273 
00274 #if IS_CLOCK_CONFIG(FRC_FCY4MHz)
00275 #warning Clock configured for FRC, FCY = 4 MHz.
00276 #warning Baud rates of 19200 or lower recommended for this clock choice.
00277 #endif
00278 #if GET_IS_SUPPORTED(FRC_FCY4MHz)
00279 void configClockFRC_FCY4MHz(void) {
00280   // Ensure that the FRC postscaler is at '1' and not its reset default of '2' (PIC24F family)
00281   _RCDIV = 0;
00282   switchClock(GET_OSC_SEL_BITS(FNOSC_FRC));
00283 }
00284 #endif
00285 
00286 
00287 #if IS_CLOCK_CONFIG(PRI_NO_PLL_7372KHzCrystal)
00288 #warning Clock configured for a 7.372 MHz crystal primary oscillator, no PLL
00289 #endif
00290 #if GET_IS_SUPPORTED(PRI_NO_PLL_7372KHzCrystal)
00291 void configClockPRI_NO_PLL_7372KHzCrystal(void) {
00292   switchClock(GET_OSC_SEL_BITS(FNOSC_PRI));
00293 }
00294 #endif
00295 
00296 
00297 #if IS_CLOCK_CONFIG(FRC_FCY3685KHz)
00298 #warning Clock configured for FRC, FCY = 3.685 MHz
00299 #warning Baud rates of 9600 or lower recommended for this clock choice.
00300 #endif
00301 #if GET_IS_SUPPORTED(FRC_FCY3685KHz)
00302 void configClockFRC_FCY3685KHz(void) {
00303   switchClock(GET_OSC_SEL_BITS(FNOSC_FRC));
00304   // Choose no tuning on FRC to get 7.37 MHz nominal FOSC.
00305   // Do after clock switch in case FRCPLL was in use, since
00306   // that would alter PLL input frequency. (Might be OK, but
00307   // this is perhaps safer.)
00308   _TUN = 0;
00309 }
00310 #endif
00311 
00312 
00313 #if IS_CLOCK_CONFIG(FRCPLL_FCY40MHz)
00314 #warning Clock configured for FRCPLL, FCY = 40 MHz
00315 #endif
00316 #if GET_IS_SUPPORTED(FRCPLL_FCY40MHz)
00317 void configClockFRCPLL_FCY40MHz(void) {
00318   // To be safe: if this was run by a bootloader that chose FRCPLL mode,
00319   // then we can't change the bits below. To do so, first switch to FRC,
00320   // change bits, then switch back to FRCPLL.
00321   switchClock(GET_OSC_SEL_BITS(FNOSC_FRC));
00322   //settings for Cycle time = 40 MHz, internal oscillator with PLL
00323   // Tune the internal oscialltor to 6.85 MHz. Each unit
00324   // in the register below = 0.375% of the 7.37 MHz
00325   // nominal frequency in 2's complement per register 7-6
00326   // in section 7.4 of the FRM rev B. So:
00327   // Sanity check: -32*0.375% = -12% -> 6.4856 MHz per data sheet.
00328   //               +30*0.375% = 11.25% -> 8.1991 Mhz not 8.23 MHz
00329   //                                      given in the data sheet!
00330   //      However, +31*0.375% = 11.625% -> 8.2268 MHz, so the above
00331   //      is a typo. This error has been reported to Microchip and
00332   //      confirmed. It should be fixed in next rev of data sheet.
00333   // Another concern: since the clock is +/- 2%, we could end
00334   // up with a > 8.0 MHz processor clock! At 8 MHz, this would
00335   // be 8.16 MHz, so the processor would run at 81.6 MHz.
00336   // Ignore this for now; probably, the chip will still run.
00337 
00338   _TUN = -19;  // Correct setting assuming the RC oscillator is exactly 7.37MHz.
00339   // It may need to be tweaked however. Use the echo.c program, and a baud rate
00340   // of 115,200 and increase/decrease TUN until you get no framing errors
00341 
00342   // Choose PLL factors: Fref after first prescale must be
00343   // between 0.8 and 8.0 MHz. Choose a prescale of 8
00344   // for Fref of 0.856 MHz.
00345   _PLLPRE = 6; // Prescale = PLLPRE + 2
00346   // Fvco after multiply must be between 100 and 200 MHz.
00347   // Pick 160 MHz, so multiply by 187.
00348   _PLLDIV = 185; // Multiply = PLLDIV + 2
00349   // Final desired Fosc = 80 MHz for an Fcy = 40 MHz.
00350   // (See 7.7 of the FRM rev B). Pick 80 MHz, so postscale by 2.
00351   _PLLPOST = 0; // Postscale = 2 * (PLLPOST + 1)
00352   switchClock(GET_OSC_SEL_BITS(FNOSC_FRCPLL));
00353 }
00354 #endif
00355 
00356 #if IS_CLOCK_CONFIG(PRIPLL_7372KHzCrystal_40MHzFCY)
00357 #warning Clock configured for PRIPLL using a 7.3727 Mhz primary oscillator, FCY = 40 MHz
00358 #endif
00359 #if GET_IS_SUPPORTED(PRIPLL_7372KHzCrystal_40MHzFCY)
00360 void configClockPRIPLL_7372KHzCrystal_40MHzFCY(void) {
00361   // To be safe: if this was run by a bootloader that chose PRIPLL mode,
00362   // then we can't change the bits below. To do so, first switch to FRC,
00363   // change bits, then switch back to PRIPLL.
00364   switchClock(GET_OSC_SEL_BITS(FNOSC_FRC));
00365   //settings for Cycle time = 40 MHz, primary oscillator with PLL
00366   _PLLPRE = 4; // Prescale = PLLPRE + 2
00367   _PLLDIV = 128; // 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_40MHzFCY)
00374 #warning Clock configured for PRIPLL using an 8.0 Mhz primary oscillator, FCY = 40 MHz
00375 #endif
00376 #if GET_IS_SUPPORTED(PRIPLL_8MHzCrystal_40MHzFCY)
00377 void configClockPRIPLL_8MHzCrystal_40MHzFCY(void) {
00378   //settings for Cycle time = 40 MHz, primary oscillator with PLL
00379   //These PLL settings will give an FCY == Crystal Freq * 10/2, or FOSC = Crystal Freq * 10
00380   /*
00381   This settings assumes the external crystal on is 8.0MHz
00382   */
00383   // To be safe: if this was run by a bootloader that chose PRIPLL mode,
00384   // then we can't change the bits below. To do so, first switch to FRC,
00385   // change bits, then switch back to PRIPLL.
00386   switchClock(GET_OSC_SEL_BITS(FNOSC_FRC));
00387   _PLLPRE = 0; // Prescale = PLLPRE + 2
00388   _PLLDIV = 38; // Multiply = PLLDIV + 2
00389   _PLLPOST = 0; // Postscale = 2 * (PLLPOST + 1)
00390   switchClock(GET_OSC_SEL_BITS(FNOSC_PRIPLL));
00391 }
00392 #endif
00393 
00394 #if IS_CLOCK_CONFIG(PRIPLL_8MHzCrystal_16MHzFCY)
00395 #warning Clock configured for PRIPLL using a 8.0 Mhz primary oscillator, FCY = 16 MHz
00396 #endif
00397 #if GET_IS_SUPPORTED(PRIPLL_8MHzCrystal_16MHzFCY)
00398 void configClockPRIPLL_8MHzCrystal_16MHzFCY(void) {
00399   // To be safe: if this was run by a bootloader that chose FRCPLL mode,
00400   // then we can't change the bits below. To do so, first switch to FRC,
00401   // change bits, then switch back to FRCPLL.
00402   switchClock(GET_OSC_SEL_BITS(FNOSC_FRC));
00403   // Two cases:
00404   //   1. Non-USB parts just have a FRC postscaler that feeds
00405   //      the 4x PLL block. Set this postscaler to 1 since the
00406   //      FRC runs at 8 MHz to get a 32 MHz FOSC = 16 MHz FCY.
00407   _RCDIV = 0;
00408 #ifdef _CPDIV
00409   //      The PLL multiplies this 4 MHz input to 96 MHz then
00410   //      divides it by 3 to 32 MHz. A second PLL prescaler
00411   //      then selects the final FOSC. Choose a prescale of
00412   //      1 so FOSC = 32 MHz, giving FCY = 16 MHz.
00413   _CPDIV = 0;  // 0 means a prescale of 1
00414 #endif
00415 #ifdef _PLLDIV
00416   //   2. USB parts have a more complex clocking scheme. The
00417   //      FRC postscaler feeds a PLL prescaler rather than
00418   //      directly determining FOSC. The
00419   //      PLL input must be 4 MHz, so choose a PLL prescaler
00420   //      of 2 since the FRC runs at 8 MHz.
00421   _PLLDIV = 1;  // 1 means a prescale of 2
00422 #elif defined(PLLDIV_NODIV)
00423 #warning Ensure that the PLLDIV value is set to divide by 2 in the configuration bits for PRIPLL_8MHzCrystal_16MHzFCY clock option!!
00424 #endif
00425 #ifdef _PLLEN
00426   _PLLEN = 1;
00427 #warning PLL Enabled
00428 #endif
00429 
00430   switchClock(GET_OSC_SEL_BITS(FNOSC_PRIPLL));
00431 }
00432 #endif
00433 
00434 #if IS_CLOCK_CONFIG(PRI_8MHzCrystal_4MHzFCY)
00435 #warning Clock configured for PRI using a 8.0 Mhz primary oscillator, FCY = 4 MHz
00436 #endif
00437 #if GET_IS_SUPPORTED(PRI_8MHzCrystal_4MHzFCY)
00438 void configClockPRI_8MHzCrystal_4MHzFCY(void) {
00439   // To be safe: if this was run by a bootloader that chose FRCPLL mode,
00440   // then we can't change the bits below. To do so, first switch to FRC,
00441   // change bits, then switch back to FRCPLL.
00442   switchClock(GET_OSC_SEL_BITS(FNOSC_PRI));
00443 }
00444 #endif

Generated on Mon Oct 18 07:40:46 2010 for Python-on-a-chip by  doxygen 1.5.9