PIC24 Support Libraries
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
pic24_clockfreq.c
Go to the documentation of this file.
1 /*
2  * "Copyright (c) 2008 Robert B. Reese, Bryan A. Jones, J. W. Bruce ("AUTHORS")"
3  * All rights reserved.
4  * (R. Reese, reese_AT_ece.msstate.edu, Mississippi State University)
5  * (B. A. Jones, bjones_AT_ece.msstate.edu, Mississippi State University)
6  * (J. W. Bruce, jwbruce_AT_ece.msstate.edu, Mississippi State University)
7  *
8  * Permission to use, copy, modify, and distribute this software and its
9  * documentation for any purpose, without fee, and without written agreement is
10  * hereby granted, provided that the above copyright notice, the following
11  * two paragraphs and the authors appear in all copies of this software.
12  *
13  * IN NO EVENT SHALL THE "AUTHORS" BE LIABLE TO ANY PARTY FOR
14  * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
15  * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE "AUTHORS"
16  * HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
17  *
18  * THE "AUTHORS" SPECIFICALLY DISCLAIMS ANY WARRANTIES,
19  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
20  * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
21  * ON AN "AS IS" BASIS, AND THE "AUTHORS" HAS NO OBLIGATION TO
22  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS."
23  *
24  * Please maintain this header in its entirety when copying/modifying
25  * these files.
26  *
27  *
28  */
29 
30 
31 
32 // Documentation for this file. If the \file tag isn't present,
33 // this file won't be documented.
34 /** \file
35  * Configure and possibly switch the processor clock to the
36  * desired frequency, based on selections made in
37  * pic24_clockfreq.h. Each entry in the \ref CLOCK_CONFIG
38  * table should have a corresponding block of code in this
39  * file to configure the clock. Typical code structure is:
40  *
41  * \code
42  * #if IS_CLOCK_CONFIG(FRCPLL_FCY16MHz)
43  * # warning Clock configured for FRCPLL, FCY = 16 MHz
44  * #endif
45  * #if GET_IS_SUPPORTED(FRCPLL_FCY16MHz)
46  * void configClockFRCPLL_FCY16MHz(void) {
47  * ... code to configure this clock ...
48  * // Typically, after setup code above, swtich to
49  * // the newly configured oscillator.
50  * switchClock(OSC_SEL_BITS);
51  * }
52  * #endif
53  * \endcode
54  *
55  * Notes: is some modes, the configuration bit settings suggest
56  * that a clock switch unnecessary. For example, FNOSC_PRI
57  * or FNOSC_FRC should require no clock switching. However, if
58  * this was run from a bootloader, it may switch the clock away
59  * from the settings determined by the configuration bits,
60  * so a switch is still performed.
61  * Likewise, in the PIC24F/H modes that use the PLL, the bootloader
62  * could be using the PLL. In this can, PLL settings can't be
63  * changed. So, the code switches to the FRC, changes PLL bits
64  * (when it's guaranteed to be safe), then switch back to the PLL.
65  */
66 
67 #include "pic24_clockfreq.h"
68 #include "pic24_util.h"
69 #include "pic24_serial.h"
70 #include "pic24_unittest.h"
71 
72 #if SIM
73 // The simulator doesn't call this function, so omit it.
74 #elif !USE_CLOCK_TIMEOUT
75 //empty functions
76 void checkClockTimeout(void) {
77 }
78 #else
79 /*
80 The purpose of the clock timeout functions is
81 output a meaningful error in case the clock switch
82 does not occur.
83 
84 */
85 # define CLOCKTIMEOUT_MAX 200000L
86 # if ( defined(__PIC24H__) || defined(__dsPIC33F__) )
87 # define FRC_FCY 40000000L
88 # elif ( defined(__PIC24F__) || defined(__PIC24FK__) )
89 # define FRC_FCY 16000000L
90 # elif ( defined(__PIC24E__) || defined(__dsPIC33E__) )
91 # define FRC_FCY 60000000L
92 # else
93 # error "Unknown processor."
94 # endif
95 # define FRC_BRGH 0
96 
97 
98 static void configFrcUART(void) {
99  // First, switch to a known-good clock
100 # if ( defined(__PIC24H__) || defined(__dsPIC33F__) )
101  configClockFRCPLL_FCY40MHz();
102 # elif ( defined(__PIC24E__) || defined(__dsPIC33E__) )
103  configClockFRCPLL_FCY60MHz();
104 # elif ( defined(__PIC24F__) || defined(__PIC24FK__) )
105  // Safe choice: FCY=16 MHz, FRC+PLL.
106  configClockFRCPLL_FCY16MHz();
107 # else
108 # error "Unknown processor."
109 #endif
110 
111  // Second, get UART I/O pins mapped and general config done.
113 
114  // BRG register is probably wrong since it uses FCY, not FRC_FCY. Fix it.
115 # if (DEFAULT_UART == 1)
116  U1BRG = compute_brg(FRC_FCY, FRC_BRGH, DEFAULT_BAUDRATE);
117  U1MODEbits.BRGH = FRC_BRGH;
118 # elif (DEFAULT_UART == 2)
119  U2BRG = compute_brg(FRC_FCY, FRC_BRGH, DEFAULT_BAUDRATE);
120  U2MODEbits.BRGH = FRC_BRGH;
121 # elif (DEFAULT_UART == 3)
122  U3BRG = compute_brg(FRC_FCY, FRC_BRGH, DEFAULT_BAUDRATE);
123  U3MODEbits.BRGH = FRC_BRGH;
124 # elif (DEFAULT_UART == 4)
125  U4BRG = compute_brg(FRC_FCY, FRC_BRGH, DEFAULT_BAUDRATE);
126  U4MODEbits.BRGH = FRC_BRGH;
127 # else
128 # error "Invalid DEFAULT_UART."
129 # endif
130 }
131 
132 static uint32_t u32_timeoutCount;
133 static void checkClockTimeout(void) {
134 
135  // See if the clock has already failed. If so, return to allow
136  // diagnostic code to perform (hopefully safe) clock switches
137  // in order to report errors.
138  if (u32_timeoutCount == 0xFFFFFFFF) return;
139 
140  // Otherwise, update timeout. If we the switch hasn't failed,
141  // simple return to wait for the switch a bit more.
142  u32_timeoutCount++;
143  if (u32_timeoutCount < CLOCKTIMEOUT_MAX) return;
144 
145  configFrcUART();
146  outString("\n\nYour clock choice failed to initialize. See " __FILE__ " line " TOSTRING(__LINE__) " for more details.");
147  // If the above string was printed out, either:
148  // 1. Check the crystal / oscillator attached to your chip. It doesn't work.
149  // 2. Tell the library to use the built-in oscillator, instead of an external one.
150  // To do thi, check your setting for the 'CLOCK_CONFIG' macro.
151  // Watch the compiler output window when pic24_clockfreq.c is compiled; a warning message
152  // there will tell you the selected value for CLOCK_CONFIG. Change this value to
153  // something that doesn't require an oscillator. See pic24_clockfreq.h for a list
154  // of valid choices.
155 
156 
157  while(1) {
158  doHeartbeat(); // never return.
159  }
160 }
161 #endif
162 
163 
164 void switchClock(uint8_t u8_source) {
165  // Create a union that mirrors the OSCCON structure
166  // with all its bit names but is also byte-accessable.
167  OSCCONBITS OSCCONBITS_copy;
168 
169  // Switch clock to use new choice specified by u8_choice.
170  // Valid values are 0-7.
171  // Throw an error if the source isn't in the list above.
172  ASSERT(u8_source < 8);
173  // 1. Disable interrupts per 7.11.2 FRM rev B under
174  // "A recommended code sequence for a clock switch
175  // includes the following:" heading.
176  // Assumes there are no priority 7 interrupts enabled.
177  asm("DISI #0x3FFF"); // Disable interrupts for a long time
178  // 2. Switch to the PLL. Use compiler built-ins to unlock
179  // clock switch registers. See 7.11.1 of the FRM rev B.
180  OSCCONBITS_copy = OSCCONbits; // Copy OSCCON bits
181  OSCCONBITS_copy.NOSC = u8_source; // Select new clock source
182  OSCCONBITS_copy.OSWEN = 1; // Request clock switch
183  // First write high byte, containing new clock source NOSC
184  __builtin_write_OSCCONH(BITS2BYTEH(OSCCONBITS_copy));
185  // Then write low byte, requesting clock switch with OSWEN
186  __builtin_write_OSCCONL(BITS2BYTEL(OSCCONBITS_copy));
187  asm("DISI #0"); // Re-enable them at the next instruction
188 
189 #ifndef SIM
190  // 3. Wait for switch to complete.
191  // Note that oscillator switching is not supported by
192  // the simulator, causing the statements below to
193  // run forever.
194 # if USE_CLOCK_TIMEOUT
195  u32_timeoutCount = 0;
196 # endif
197  while (_OSWEN == 1) {
198  checkClockTimeout();
199  }
200 
201  // 4. Wait for the PLL to lock if using the PLL.
202  // (Is this really necessary? It certainly can't hurt.)
203  if ( (u8_source == GET_OSC_SEL_BITS(FNOSC_FRCPLL)) ||
204  (u8_source == GET_OSC_SEL_BITS(FNOSC_PRIPLL)) ) {
205  while (_LOCK == 0) {
206  checkClockTimeout();
207  }
208  }
209 
210  // 5. Make sure clock switch worked
211  while (_COSC != u8_source) checkClockTimeout();
212 #endif
213 }
214 
215 #if IS_CLOCK_CONFIG(SIM_CLOCK)
216 # warning "Clock configured for simulation, FCY = 1 Mhz."
217 #endif
218 #if GET_IS_SUPPORTED(SIM_CLOCK)
219 void configClockSim(void) { }
220 #endif
221 
222 
223 #if IS_CLOCK_CONFIG(FRCPLL_FCY16MHz)
224 # warning "Clock configured for FRCPLL, FCY = 16 MHz."
225 #endif
226 #if GET_IS_SUPPORTED(FRCPLL_FCY16MHz)
227 void configClockFRCPLL_FCY16MHz(void) {
228  // To be safe: if this was run by a bootloader that chose FRCPLL mode,
229  // then we can't change the bits below. To do so, first switch to FRC,
230  // change bits, then switch back to FRCPLL.
231  switchClock(GET_OSC_SEL_BITS(FNOSC_FRC));
232  // Two cases:
233  // 1. Non-USB parts just have a FRC postscaler that feeds
234  // the 4x PLL block. Set this postscaler to 1 since the
235  // FRC runs at 8 MHz to get a 32 MHz FOSC = 16 MHz FCY.
236  _RCDIV = 0;
237 # ifdef _CPDIV
238  // The PLL multiplies this 4 MHz input to 96 MHz then
239  // divides it by 3 to 32 MHz. A second PLL prescaler
240  // then selects the final FOSC. Choose a prescale of
241  // 1 so FOSC = 32 MHz, giving FCY = 16 MHz.
242  _CPDIV = 0; // 0 means a prescale of 1
243 # endif
244 # ifdef _PLLDIV
245  // 2. USB parts have a more complex clocking scheme. The
246  // FRC postscaler feeds a PLL prescaler rather than
247  // directly determining FOSC. The
248  // PLL input must be 4 MHz, so choose a PLL prescaler
249  // of 2 since the FRC runs at 8 MHz.
250  _PLLDIV = 1; // 1 means a prescale of 2
251 # elif defined(PLLDIV_NODIV)
252 # warning "Ensure that the PLLDIV value is set to divide by 2 in the configuration bits for FRCPLL_FCY16MHz clock option!!"
253 # endif
254 # ifdef _PLLEN
255  _PLLEN = 1;
256 # warning "PLL Enabled."
257 # endif
258  switchClock(GET_OSC_SEL_BITS(FNOSC_FRCPLL));
259 }
260 #endif
261 
262 
263 #if IS_CLOCK_CONFIG(FRC_FCY4MHz)
264 # warning "Clock configured for FRC, FCY = 4 MHz."
265 # warning "Baud rates of 19200 or lower recommended for this clock choice."
266 #endif
267 #if GET_IS_SUPPORTED(FRC_FCY4MHz)
268 void configClockFRC_FCY4MHz(void) {
269  // Ensure that the FRC postscaler is at '1' and not its reset default of '2' (PIC24F family)
270  _RCDIV = 0;
271  switchClock(GET_OSC_SEL_BITS(FNOSC_FRC));
272 }
273 #endif
274 
275 
276 #if IS_CLOCK_CONFIG(PRI_NO_PLL_7372KHzCrystal)
277 # warning "Clock configured for a 7.372 MHz crystal primary oscillator, no PLL."
278 #endif
279 #if GET_IS_SUPPORTED(PRI_NO_PLL_7372KHzCrystal)
280 void configClockPRI_NO_PLL_7372KHzCrystal(void) {
281  switchClock(GET_OSC_SEL_BITS(FNOSC_PRI));
282 }
283 #endif
284 
285 
286 #if IS_CLOCK_CONFIG(FRC_FCY3685KHz)
287 # warning "Clock configured for FRC, FCY = 3.685 MHz."
288 #endif
289 #if GET_IS_SUPPORTED(FRC_FCY3685KHz)
290 void configClockFRC_FCY3685KHz(void) {
291  switchClock(GET_OSC_SEL_BITS(FNOSC_FRC));
292  // Choose no tuning on FRC to get 7.37 MHz nominal FOSC.
293  // Do after clock switch in case FRCPLL was in use, since
294  // that would alter PLL input frequency. (Might be OK, but
295  // this is perhaps safer.)
296  _TUN = 0;
297 }
298 #endif
299 
300 
301 #if IS_CLOCK_CONFIG(FRCPLL_FCY40MHz)
302 # warning "Clock configured for FRCPLL, FCY = 40 MHz."
303 #endif
304 #if GET_IS_SUPPORTED(FRCPLL_FCY40MHz)
305 void configClockFRCPLL_FCY40MHz(void) {
306  // To be safe: if this was run by a bootloader that chose FRCPLL mode,
307  // then we can't change the bits below. To do so, first switch to FRC,
308  // change bits, then switch back to FRCPLL.
309  switchClock(GET_OSC_SEL_BITS(FNOSC_FRC));
310  // A concern: since the clock is +/- 2%, we could end
311  // up with a > 8.0 MHz processor clock! At 8 MHz, this would
312  // be 8.16 MHz, so the processor would run at 81.6 MHz.
313  // Ignore this for now; probably, the chip will still run.
314 
315  _TUN = 0; // Correct setting assuming the RC oscillator is exactly 7.37MHz.
316  // It may need to be tweaked however. Use the echo.c program, and a baud rate
317  // of 115,200 and increase/decrease TUN until you get no framing errors
318 
319  // Choose PLL factors: Fref after first prescale must be
320  // between 0.8 and 8.0 MHz. Choose a prescale of 9
321  // for Fref of 0.8189 MHz.
322  _PLLPRE = 7; // Prescale = PLLPRE + 2
323  // Fvco after multiply must be between 100 and 200 MHz.
324  // Pick 159.7 MHz, so multiply by 195.
325  _PLLDIV = 193; // Multiply = PLLDIV + 2
326  // Final desired Fosc = 79.8 MHz for an Fcy = 39.9 MHz.
327  // (See 7.7 of the FRM rev B). Pick 80 MHz, so postscale by 2.
328  _PLLPOST = 0; // Postscale = 2 * (PLLPOST + 1)
329  switchClock(GET_OSC_SEL_BITS(FNOSC_FRCPLL));
330 }
331 #endif
332 
333 #if IS_CLOCK_CONFIG(FRCPLL_FCY60MHz)
334 # warning "Clock configured for FRCPLL, FCY = 60 MHz."
335 #endif
336 #if GET_IS_SUPPORTED(FRCPLL_FCY60MHz)
337 void configClockFRCPLL_FCY60MHz(void) {
338  // To be safe: if this was run by a bootloader that chose FRCPLL mode,
339  // then we can't change the bits below. To do so, first switch to FRC,
340  // change bits, then switch back to FRCPLL.
341  switchClock(GET_OSC_SEL_BITS(FNOSC_FRC));
342 
343  _TUN = 0; // Correct setting assuming the RC oscillator is exactly 7.37MHz.
344  // It may need to be tweaked however. Use the echo.c program, and a baud rate
345  // of 115,200 and increase/decrease TUN until you get no framing errors
346 
347  // For PIC24E Family, Fplli must be
348  // between 0.8 MHz and 8.0 MHz. Choose a prescale of 2
349  // for Fplli of 3.685 MHz.
350  _PLLPRE = 0; // Prescale = PLLPRE + 2
351  // Fsys after multiply must be between 120 and 340 MHz.
352  // Pick 240 MHz, so multiply by 65.
353  _PLLDIV = 63; // Multiply = PLLDIV + 2
354  // Final desired Fosc = 120 MHz for an Fcy = 60 MHz.
355  // Pick 120 MHz, so postscale by 2.
356  _PLLPOST = 0; // Postscale = 2 * (PLLPOST + 1)
357  switchClock(GET_OSC_SEL_BITS(FNOSC_FRCPLL));
358 }
359 #endif
360 
361 #if IS_CLOCK_CONFIG(FRCPLL_FCY70MHz)
362 # warning "Clock configured for FRCPLL, FCY = 70 MHz."
363 #endif
364 #if GET_IS_SUPPORTED(FRCPLL_FCY70MHz)
365 void configClockFRCPLL_FCY70MHz(void) {
366  // To be safe: if this was run by a bootloader that chose FRCPLL mode,
367  // then we can't change the bits below. To do so, first switch to FRC,
368  // change bits, then switch back to FRCPLL.
369  switchClock(GET_OSC_SEL_BITS(FNOSC_FRC));
370 
371  _TUN = 0; // Correct setting assuming the RC oscillator is exactly 7.37MHz.
372  // It may need to be tweaked however. Use the echo.c program, and a baud rate
373  // of 115,200 and increase/decrease TUN until you get no framing errors
374 
375  // For PIC24E Family, Fplli must be
376  // between 0.8 MHz and 8.0 MHz. Choose a prescale of 2
377  // for Fplli of 3.685 MHz.
378  _PLLPRE = 0; // Prescale = PLLPRE + 2
379  // Fsys after multiply must be between 120 and 340 MHz.
380  // Pick 280 MHz, so multiply by 76.
381  _PLLDIV = 74; // Multiply = PLLDIV + 2
382  // Final desired Fosc = 140 MHz for an Fcy = 70 MHz.
383  // Pick 140 MHz, so postscale by 2.
384  _PLLPOST = 0; // Postscale = 2 * (PLLPOST + 1)
385  switchClock(GET_OSC_SEL_BITS(FNOSC_FRCPLL));
386 }
387 #endif
388 
389 
390 
391 #if IS_CLOCK_CONFIG(PRIPLL_7372KHzCrystal_40MHzFCY)
392 # warning "Clock configured for PRIPLL using a 7.3727 Mhz primary oscillator, FCY = 40 MHz."
393 #endif
394 #if GET_IS_SUPPORTED(PRIPLL_7372KHzCrystal_40MHzFCY)
395 void configClockPRIPLL_7372KHzCrystal_40MHzFCY(void) {
396  // To be safe: if this was run by a bootloader that chose PRIPLL mode,
397  // then we can't change the bits below. To do so, first switch to FRC,
398  // change bits, then switch back to PRIPLL.
399  switchClock(GET_OSC_SEL_BITS(FNOSC_FRC));
400  //settings for Cycle time = 40 MHz, primary oscillator with PLL
401  _PLLPRE = 4; // Prescale = PLLPRE + 2
402  _PLLDIV = 128; // Multiply = PLLDIV + 2
403  _PLLPOST = 0; // Postscale = 2 * (PLLPOST + 1)
404  switchClock(GET_OSC_SEL_BITS(FNOSC_PRIPLL));
405 }
406 #endif
407 
408 #if IS_CLOCK_CONFIG(PRIPLL_8MHzCrystal_40MHzFCY)
409 # warning "Clock configured for PRIPLL using an 8.0 Mhz primary oscillator, FCY = 40 MHz."
410 #endif
411 #if GET_IS_SUPPORTED(PRIPLL_8MHzCrystal_40MHzFCY)
412 void configClockPRIPLL_8MHzCrystal_40MHzFCY(void) {
413  //settings for Cycle time = 40 MHz, primary oscillator with PLL
414  //These PLL settings will give an FCY == Crystal Freq * 10/2, or FOSC = Crystal Freq * 10
415  /*
416  This settings assumes the external crystal on is 8.0MHz
417  */
418  // To be safe: if this was run by a bootloader that chose PRIPLL mode,
419  // then we can't change the bits below. To do so, first switch to FRC,
420  // change bits, then switch back to PRIPLL.
421  switchClock(GET_OSC_SEL_BITS(FNOSC_FRC));
422  _PLLPRE = 0; // Prescale = PLLPRE + 2
423  _PLLDIV = 38; // Multiply = PLLDIV + 2
424  _PLLPOST = 0; // Postscale = 2 * (PLLPOST + 1)
425  switchClock(GET_OSC_SEL_BITS(FNOSC_PRIPLL));
426 }
427 #endif
428 
429 #if IS_CLOCK_CONFIG(PRIPLL_8MHzCrystal_16MHzFCY)
430 # warning "Clock configured for PRIPLL using a 8.0 Mhz primary oscillator, FCY = 16 MHz."
431 #endif
432 #if GET_IS_SUPPORTED(PRIPLL_8MHzCrystal_16MHzFCY)
433 void configClockPRIPLL_8MHzCrystal_16MHzFCY(void) {
434  // To be safe: if this was run by a bootloader that chose FRCPLL mode,
435  // then we can't change the bits below. To do so, first switch to FRC,
436  // change bits, then switch back to FRCPLL.
437  switchClock(GET_OSC_SEL_BITS(FNOSC_FRC));
438  // Two cases:
439  // 1. Non-USB parts just have a FRC postscaler that feeds
440  // the 4x PLL block. Set this postscaler to 1 since the
441  // FRC runs at 8 MHz to get a 32 MHz FOSC = 16 MHz FCY.
442  _RCDIV = 0;
443 # ifdef _CPDIV
444  // The PLL multiplies this 4 MHz input to 96 MHz then
445  // divides it by 3 to 32 MHz. A second PLL prescaler
446  // then selects the final FOSC. Choose a prescale of
447  // 1 so FOSC = 32 MHz, giving FCY = 16 MHz.
448  _CPDIV = 0; // 0 means a prescale of 1
449 # endif
450 # ifdef _PLLDIV
451  // 2. USB parts have a more complex clocking scheme. The
452  // FRC postscaler feeds a PLL prescaler rather than
453  // directly determining FOSC. The
454  // PLL input must be 4 MHz, so choose a PLL prescaler
455  // of 2 since the FRC runs at 8 MHz.
456  _PLLDIV = 1; // 1 means a prescale of 2
457 # elif defined(PLLDIV_NODIV)
458 # warning "Ensure that the PLLDIV value is set to divide by 2 in the configuration bits for PRIPLL_8MHzCrystal_16MHzFCY clock option!!"
459 # endif
460 # ifdef _PLLEN
461  _PLLEN = 1;
462 # warning "PLL Enabled."
463 # endif
464 
465  switchClock(GET_OSC_SEL_BITS(FNOSC_PRIPLL));
466 }
467 #endif
468 
469 #if IS_CLOCK_CONFIG(PRI_8MHzCrystal_4MHzFCY)
470 # warning "Clock configured for PRI using a 8.0 Mhz primary oscillator, FCY = 4 MHz."
471 # warning "Baud rates of 19200 or lower recommended for this clock choice."
472 #endif
473 #if GET_IS_SUPPORTED(PRI_8MHzCrystal_4MHzFCY)
474 void configClockPRI_8MHzCrystal_4MHzFCY(void) {
475  // To be safe: if this was run by a bootloader that chose FRCPLL mode,
476  // then we can't change the bits below. To do so, first switch to FRC,
477  // change bits, then switch back to FRCPLL.
478  switchClock(GET_OSC_SEL_BITS(FNOSC_PRI));
479 }
480 #endif