Digital Power Starter Kit 3 Firmware
dsPIC33C Boost Converter Voltage Mode Control Example
p33c_osc.c
1 /*
2  * File: p33SMPS_oscillator.c
3  * Author: M91406
4  *
5  * Created on October 27, 2017, 11:24 AM
6  */
7 
8 /* ************************************************************************************************
9  * PRIVATE DEFINES
10  * ************************************************************************************************/
11 
12 // Include Header Files
13 #include "p33c_osc.h"
14 
15 /* ************************************************************************************************
16  * PRIVATE DEFINES
17  * ************************************************************************************************/
18 
23 #define OSC_CLKSW_TIMEOUT 50000
24 
25 /* ************************************************************************************************
26  * PRIVATE VARIABLES
27  * ************************************************************************************************/
32 volatile struct OSCILLATOR_SYSTEM_FREQUENCIES_s SystemFrequencies;
33 
34 /*************************************************************************************************
35  * @fn uint16_t p33c_OscFrc_DefaultInitialize(volatile enum CPU_SPEED_DEFAULTS_e cpu_speed)
36  * @ingroup lib-layer-pral-functions-public-osc
37  * @param cpu_speed CPU speed setting of type enum CPU_SPEED_DEFAULTS_e
38  * @return unsigned integer
39  * 0 = unspecified clock failure detected
40  * 1 = clock switch successful
41  * 2 = clock switch failed
42  * 4 = currently selected clock differs from selected clock source
43  * 8 = PLL didn't lock in within given time frame
44  * @brief Initializes the major oscillator and the PLL module step by step by using clock switching
45  * in software. Each step is tested and verified. PLL settings here are pulled from pre-defined
46  * settings set for default CPU speeds from 40 MIPS to 100 MIPS
47  *
48  * @details
49  * Microchip's 16-Bit devices offer a safe 2-step start-up mode, using the internal FRC during power up,
50  * followed by a user defined switch-over to the desired oscillator.
51  * Though this can also be done in hardware automatically, this software-version of the switch-over offers
52  * a better solution to verify each step and enables the user to implement some error handling in the case
53  * of failure.
54  * This function can be used to select a new oscillator at runtime. Each configuration step
55  * will be verified before the next step is performed.
56  *
57  * @note
58  * If a oscillator switch-over is performed, additional settings in the _FOSCSEL and _FOSC
59  * registers of the configuration bits may be required
60  * ************************************************************************************************/
61 
62 volatile uint16_t p33c_OscFrc_DefaultInitialize(volatile enum CPU_SPEED_DEFAULTS_e cpu_speed)
63 {
64  volatile int16_t retval = 1;
65  volatile struct OSC_CONFIG_s osc;
66 
67  osc.osc_type = OSCCON_xOSC_FRCPLL;
68  osc.N1 = CLKDIV_PLLDIV_N1_1;
69 
70  switch(cpu_speed)
71  {
72  case CPU_SPEED_20_MIPS:
73  osc.M = PLLFBD_PLLFBDIV_M_20;
74  break;
75  case CPU_SPEED_30_MIPS:
76  osc.M = PLLFBD_PLLFBDIV_M_30;
77  break;
78  case CPU_SPEED_40_MIPS:
79  osc.M = PLLFBD_PLLFBDIV_M_40;
80  break;
81  case CPU_SPEED_50_MIPS:
82  osc.M = PLLFBD_PLLFBDIV_M_50;
83  break;
84  case CPU_SPEED_60_MIPS:
85  osc.M = PLLFBD_PLLFBDIV_M_60;
86  break;
87  case CPU_SPEED_70_MIPS:
88  osc.M = PLLFBD_PLLFBDIV_M_70;
89  break;
90  case CPU_SPEED_80_MIPS:
91  osc.M = PLLFBD_PLLFBDIV_M_80;
92  break;
93  case CPU_SPEED_90_MIPS:
94  osc.M = PLLFBD_PLLFBDIV_M_90;
95  break;
96  case CPU_SPEED_100_MIPS:
97  osc.M = PLLFBD_PLLFBDIV_M_100;
98  break;
99  default:
100  return(0);
101  break;
102  }
103  osc.N2 = PLLDIV_POST2DIV_N2N3_2;
104  osc.N3 = PLLDIV_POST2DIV_N2N3_1;
105 
106  retval &= p33c_Osc_Initialize(osc);
107 
108  return(retval);
109 }
110 
111 /*************************************************************************************************
112  * @fn uint16_t p33c_OscFrc_Initialize(volatile enum CLKDIV_FRCDIVN_e frc_div, volatile enum OSCTUN_TUN_e frc_tune)
113  * @ingroup lib-layer-pral-functions-public-osc
114  * @brief Initializes the internal RC oscillator divider and tuning register
115  * @param frc_div Internal RC Oscillator frequency divider of type enum CLKDIV_FRCDIVN_e
116  * @param frc_tune Internal RC Oscillator tuning register value of type enum OSCTUN_TUN_e
117  * @return
118  * 0 = unspecified clock failure detected
119  * 1 = clock switch successful
120  * 2 = clock switch failed
121  * 4 = currently selected clock differs from selected clock source
122  * 8 = PLL didn't lock in within given time frame
123  *
124  * @details
125  * Microchip's 16-Bit devices offer a safe 2-step start-up mode, using the internal FRC during power up,
126  * followed by a user defined switch-over to the desired oscillator.
127  * Though this can also be done in hardware automatically, this software-version of the switch-over offers
128  * a better solution to verify each step and enables the user to implement some error handling in the case
129  * of failure.
130  * This function can be used to select a new oscillator at runtime. Each configuration step
131  * will be verified before the next step is performed.
132  *
133  * @note
134  * If a oscillator switch-over is performed, additional settings in the _FOSCSEL and _FOSC
135  * registers of the configuration bits may be required
136  *************************************************************************************************/
137 
138 volatile uint16_t p33c_OscFrc_Initialize(volatile enum CLKDIV_FRCDIVN_e frc_div, volatile enum OSCTUN_TUN_e frc_tune)
139 {
140 #if defined (__P33SMPS_CH_MSTR__) || defined (__P33SMPS_CK__)
141 
142 // Secondary cores of multi-core devices do not have access to the FRC oscillator tuning register
143 
144  volatile uint16_t err=0;
145 
146  // Set oscillator tuning
147  // => FRC Oscillator tuning is only available on single core devices and
148  // the master core of dual core devices
149  OSCTUNbits.TUN = frc_tune; // Set Tuning Register for FRC Oscillator
150 
151  // Set FRC divider
152  CLKDIVbits.FRCDIV = frc_div; // Set FRC frequency divider
153 
154  return(err);
155 
156 #else
157  return(1);
158 #endif
159 
160 }
161 
162 /*************************************************************************************************
163  * @fn uint16_t p33c_Osc_Initialize(volatile struct P33C_OSC_CONFIG_s osc_config)
164  * @ingroup lib-layer-pral-functions-public-osc
165  * @brief
166  * Initializes the major oscillator and the PLL module step by step by using clock switching
167  * in software. Each step is tested and verified
168  *
169  * @param osc_config Main oscillator and PLL configuration of type struct P33C_OSC_CONFIG_s
170  *
171  * @return unsigned integer
172  * 0 = unspecified clock failure detected
173  * 1 = clock switch successful
174  * 2 = clock switch failed
175  * 4 = currently selected clock differs from selected clock source
176  * 8 = PLL didn't lock in within given time frame
177  *
178  * @details
179  * Microchip's 16-Bit devices offer a safe 2-step start-up mode, using the internal FRC during power up,
180  * followed by a user defined switch-over to the desired oscillator.
181  * Though this can also be done in hardware automatically, this software-version of the switch-over offers
182  * a better solution to verify each step and enables the user to implement some error handling in the case
183  * of failure.
184  * This function can be used to select a new oscillator at runtime. Each configuration step
185  * will be verified before the next step is performed.
186  *
187  * @note
188  * If a oscillator switch-over is performed, additional settings in the _FOSCSEL and _FOSC
189  * registers of the configuration bits may be required
190  *************************************************************************************************/
191 
192 volatile uint16_t p33c_Osc_Initialize(volatile struct OSC_CONFIG_s osc_config)
193 {
194  volatile uint16_t retval=0;
195  volatile uint16_t _n=0;
196 
197  // =============================================================================================
198  // First make sure we are not running from a PLL derived source, when modifying the PLL settings.
199  // This can be accomplished by intentionally switching to a known non-PLL setting, such as FRC.
200  // Switch to FRC (no divider, no PLL), assuming we aren't already running from that.
201  // =============================================================================================
202 
203  if(OSCCONbits.COSC != 0b000)
204  {
205  // NOSC = 0b000 = FRC without divider or PLL
206  __builtin_write_OSCCONH(OSCCON_xOSC_FRC);
207  // Clear CLKLOCK and assert OSWEN = 1 to initiate switch-over
208  __builtin_write_OSCCONL((OSCCON & 0x7E) | 0x01);
209  //Wait for switch over to complete.
210  while((OSCCONbits.COSC != OSCCONbits.NOSC) && (_n++ < OSC_CLKSW_TIMEOUT));
211  if (_n >= OSC_CLKSW_TIMEOUT) retval = OSCERR_RST;
212  }
213 
214  // Switch to target oscillator
215  if ((OSCCONbits.COSC != osc_config.osc_type) && (OSCCONbits.CLKLOCK == 0))
216  {
217  // Switch to desired system clock, if clock switching is enabled
218 
219  #if defined (__P33SMPS_CH__) || defined (__P33SMPS_CK__)
220  // Configure PLL pre-scaler, PLL post-scaler, PLL divisor
221 
222  // Clock switch to desired CPU frequency with the PLL enabled in advance
223  // Configure PLL pre-scaler, both PLL post-scalers, and PLL feedback divider
224  PLLDIVbits.VCODIV = osc_config.VCODIV; // FVCO Scaler 1:n
225  CLKDIVbits.PLLPRE = osc_config.N1; // N1 = PLL pre-scaler
226  PLLFBDbits.PLLFBDIV = osc_config.M; // M = PLL feedback divider
227  PLLDIVbits.POST1DIV = osc_config.N2; // N2 = PLL post-scalers #1
228  PLLDIVbits.POST2DIV = osc_config.N3; // N3 = PLL post-scalers #2
229 
230  #elif defined (__P33SMPS_FJ__) || defined (__P33SMPS_FJA__) || defined (__P33SMPS_FJC__) || \
231  defined (__P33SMPS_EP__)
232 
233  CLKDIVbits.PLLPRE = osc_config.N1; // N1 (non zero)
234  PLLFBD = (osc_config.M - 2); // M = PLLFBD
235  CLKDIVbits.PLLPOST = osc_config.N2; // N2 (non zero)
236 
237  #endif
238 
239  // Initiate Clock Switch to FRC Oscillator with PLL (NOSC=0b011)
240  __builtin_write_OSCCONH(osc_config.osc_type);
241  if(OSCCONbits.COSC != OSCCONbits.NOSC)
242  {
243  // Assert OSWEN and make sure CLKLOCK is clear, to initiate the switching operation
244  __builtin_write_OSCCONL((OSCCON & 0x7F) | 0x01);
245  // Wait for clock switch to finish
246  while((OSCCONbits.COSC != OSCCONbits.NOSC) && (_n++ < OSC_CLKSW_TIMEOUT));
247  if ((OSCCONbits.COSC != OSCCONbits.NOSC) || (_n >= OSC_CLKSW_TIMEOUT))
248  { retval = OSCERR_CSF; }
249  }
250 
251  }
252  else if ((OSCCONbits.COSC != osc_config.osc_type) && (OSCCONbits.CLKLOCK == 1))
253  { // If clock switching is disabled and the current clock differs from the desired clock,
254  // return error code
255  retval = OSCERR_CSD;
256  }
257 
258  // Lock registers against accidental changes
259  OSCCONbits.CLKLOCK = 1;
260 
261  while((OSCCONbits.LOCK != 1) && (_n++ < OSC_CLKSW_TIMEOUT)); // Wait n while loops for PLL to Lock
262  if ((OSCCONbits.LOCK != 1) || (_n >= OSC_CLKSW_TIMEOUT)) // Error occurred?
263  { retval = OSCERR_PLL_LCK; } // => If so, return error code
264 
265 // Return Success/Failure
266  if (retval == 0) {
267  return((1 - OSCCONbits.CF)); // Return oscillator fail status bit
268  } // (1=Success, 0=Failure)
269  else
270  { return(retval); } // Return error code
271 
272 }
273 
274 /*************************************************************************************************
275  * @fn uint16_t p33c_OscAuxClk_Initialize(volatile struct AUXOSC_CONFIG_s aux_clock_config)
276  * @ingroup lib-layer-pral-functions-public-osc
277  * @brief
278  * Initializes the auxiliary clock and its PLL module step by step
279  * in software. Each step is tested and verified
280  *
281  * @param aux_clock_config Auxiliary oscillator configuration of type struct AUXOSC_CONFIG_s
282  *
283  * @return
284  * 0 = unspecified clock failure detected
285  * 1 = clock switch successful
286  * 2 = clock switch failed
287  * 4 = currently selected clock differs from selected clock source
288  * 8 = PLL didn't lock in within given time frame
289  *
290  * @details
291  * The auxiliary clock is generated by a separate PLL module, which is driven
292  * from one of the main clock signals or PLL outputs. This auxiliary clock can
293  * be used to drive ADCs, PWM, DACs and other peripherals. Each of them might
294  * have individual requirements. Please refer to the specific peripheral sections
295  * in the device data sheet to learn how to configure the auxiliary clock.
296  *
297  *************************************************************************************************/
298 
299 volatile uint16_t p33c_OscAuxClk_Initialize(volatile struct AUXOSC_CONFIG_s aux_clock_config)
300 {
301 
302  // Set AVCO divider of Auxiliary PLL
303  APLLDIV1bits.AVCODIV = aux_clock_config.AVCODIV; // AVCO Scaler 1:n
304 
305  // Configure APLL pre-scaler, APLL post-scaler, APLL divisor
306  ACLKCON1bits.APLLPRE = aux_clock_config.N1; // N1 (non zero)
307  APLLFBD1bits.APLLFBDIV = aux_clock_config.M; // M = APLLFBD
308  APLLDIV1bits.APOST1DIV = aux_clock_config.N2; // N2 (non zero)
309  APLLDIV1bits.APOST2DIV = aux_clock_config.N3; // N3 (non zero)
310 
311  // Select clock input source (either primary oscillator or internal FRC)
312  ACLKCON1bits.FRCSEL = aux_clock_config.FRCSEL;
313 
314  // Set Enable-bit of Auxiliary PLL
315  ACLKCON1bits.APLLEN = aux_clock_config.APLLEN;
316 
317  // if user has not enabled the APLL module, exit here
318  if(!aux_clock_config.APLLEN)
319  { return(0); }
320 
321  return(ACLKCON1bits.APLLEN);
322 
323 }
324 
325 /*************************************************************************************************
326  * @fn uint16_t p33c_OscAuxClk_DefaultInitialize(volatile enum AUX_PLL_DEFAULTS_e afpllo_frequency)
327  * @ingroup lib-layer-pral-functions-public-osc
328  * @brief
329  * Initializes the auxiliary clock and its PLL module step by step
330  * in software. Each step is tested and verified
331  *
332  * @param afpllo_frequency Auxiliary PLL output frequency of type enum AUX_PLL_DEFAULTS_e
333  *
334  * @return
335  * 0 = unspecified clock failure detected
336  * 1 = clock switch successful
337  * 2 = clock switch failed
338  * 4 = currently selected clock differs from selected clock source
339  * 8 = PLL didn't lock in within given time frame
340  *
341  * @details
342  * The auxiliary clock is generated by a separate PLL module, which is driven
343  * from one of the main clock signals or PLL outputs. This auxiliary clock can
344  * be used to drive ADCs, PWM, DACs and other peripherals. Each of them might
345  * have individual requirements. Please refer to the specific peripheral sections
346  * in the device data sheet to learn how to configure the auxiliary clock.
347  *
348  *************************************************************************************************/
349 
350  volatile uint16_t p33c_OscAuxClk_DefaultInitialize(volatile enum AUX_PLL_DEFAULTS_e afpllo_frequency)
351  {
352  volatile uint16_t retval = 1;
353  volatile struct AUXOSC_CONFIG_s aux_clock_config;
354 
355  // Set FRC as clock input to auxiliary PLL module
356  aux_clock_config.FRCSEL = PLLDIV_ACLKCON_FRCSEL_FRC;
357 
358  // Set auxiliary PLL VCO divider to 1:4
359  aux_clock_config.AVCODIV = APLLDIV_AVCODIV_FVCO_DIV_BY_4;
360 
361  // Select PLL dividers and multiplier in accordance with user parameter
362  if(afpllo_frequency <= 800) {
363  aux_clock_config.N1 = ACLKCON_APLLDIV_N1_1; // Default divider of 1
364  aux_clock_config.M = (afpllo_frequency >> 2); // frequency divided by 4
365  aux_clock_config.N2 = APLLDIV_POST2DIV_N2N3_2; // Default divider of 2
366  aux_clock_config.N3 = APLLDIV_POST2DIV_N2N3_1; // Default divider of 1
367  }
368  else {
369  return(0); // When frequency is out of range, return failure
370  // Most recent APLL setting remains unchanged
371  }
372 
373  // Enable auxiliary PLL
374  aux_clock_config.APLLEN = ACLKCON_APLLEN_ENABLED;
375 
376  // Call auxiliary PLL configuration to apply new settings
377  retval &= p33c_OscAuxClk_Initialize(aux_clock_config);
378 
379  return(retval);
380  }
381 
382  /**************************************************************************************************
383  * @fn uint16_t p33c_Osc_GetFrequencies(volatile uint32_t main_osc_frequency)
384  * @ingroup lib-layer-pral-functions-public-osc
385  * @brief
386  * This routine reads all oscillator related SFRs recalculating the various frequencies
387  * across clock domains. These frequencies are broadcasted in the global data structure
388  * 'system_frequencies'.
389  *
390  * @param main_osc_frequency Frequency of external oscillator frequency in [Hz] of type unsigned integer.
391  * Set to '0' if no external oscillator is used.
392  *
393  * @return
394  * unsigned integer
395  * 0 = reading oscillator settings failed
396  * 1 = reading oscillator settings successfully
397  *
398  * @details
399  * Microchip's 16-Bit devices offer multiple clock sources to clock the CPU. This routine
400  * reads the most recent, oscillator related Special Function Registers (SFR) and determines
401  * the recently active main clock and its frequency and calculates the resulting clock frequencies
402  * of other clock domains.
403  *
404  * The results are broadcasted through the 'system_frequencies' data structure which is globally
405  * accessible in user code and can be used to calculate other oscillator dependent settings such as
406  * timer periods or baud rates of communication interfaces
407  *
408  * Please note:
409  * The contents of data structure 'system_frequencies' is NOT updated automatically. It is
410  * recommended to call the function 'osc_get_frequencies' in user code every time after
411  * clock settings have been modified.
412  *
413  **************************************************************************************************/
414 volatile uint16_t p33c_Osc_SetExtFrequency(volatile int32_t ext_osc_frequency)
415 {
416  volatile uint16_t retval=1;
417 
418  SystemFrequencies.fpri = ext_osc_frequency;
419 
420  return(retval);
421 }
422 
423 /**************************************************************************************************
424  * @fn uint16_t p33c_Osc_GetFrequencies(volatile uint32_t main_osc_frequency)
425  * @ingroup lib-layer-pral-functions-public-osc
426  * @brief
427  * This routine reads all oscillator related SFRs recalculating the various frequencies
428  * across clock domains. These frequencies are broadcasted in the global data structure
429  * 'system_frequencies'.
430  *
431  * @return
432  * unsigned integer
433  * 0 = reading oscillator settings failed
434  * 1 = reading oscillator settings successful
435  *
436  * @details
437  * Microchip's 16-Bit devices offer multiple clock sources to clock the CPU and peripherals. This
438  * routine reads the most recent, oscillator related Special Function Registers (SFR) and determines
439  * the recently active main clock and its frequency and calculates the resulting clock frequencies
440  * of other clock domains.
441  *
442  * The results are broadcasted through the 'system_frequencies' data structure which is globally
443  * accessible in user code and can be used to calculate other oscillator dependent settings such as
444  * timer periods or baud rates of communication interfaces-
445  *
446  * @note
447  * The content of data structure 'system_frequencies' is NOT updated automatically. It is
448  * recommended to call the function 'p33c_Osc_GetFrequencies' in user code every time after
449  * clock settings have been modified.
450  *
451  * @note
452  * If external oscillators are used, users have to specify the clock frequency by calling
453  * the function 'p33c_Osc_SetExtFrequency(volatile int32_t ext_osc_frequency)' before the
454  * function 'p33c_Osc_GetFrequencies' will return accurate frequency information.
455  *
456  * If the recent oscillator is set to "External Clock" and the primary clock frequency
457  * equals zero, this function will return an error code and no frequency information
458  * update will occur.
459  *
460  **************************************************************************************************/
461 volatile uint16_t p33c_Osc_GetFrequencies(void)
462 {
463  volatile int32_t freq=0;
464  volatile uint16_t vbuf=0;
465  volatile enum OSCCON_xOSC_TYPE_e otype;
466 
467  // read currently selected oscillator
468  otype = OSCCONbits.COSC;
469 
470  // Copy Base FRC frequency into data structure
471  SystemFrequencies.frc = (volatile int32_t)OSC_FRC_FREQ; // Set default FRC oscillator frequency
472 
473  // Depending on detected oscillator type, set/override oscillator frequency
474 
475  // For all modes using the internal Fast RC Oscillator (FRC), check input divider and tuning register
476  if ((otype == OSCCON_xOSC_FRC) || (otype == OSCCON_xOSC_BFRC) || (otype == OSCCON_xOSC_FRCPLL) || (otype == OSCCON_xOSC_FRCDIVN)) {
477 
478  freq = (volatile int32_t)OSC_FRC_FREQ; // Oscillator frequency is 8 MHz
479 
480  #if defined (__P33SMPS_CK__) || defined (__P33SMPS_CH_MSTR__)
481  // FRC tuning register does not affect Backup FRC clock
482  if(otype != OSCCON_xOSC_BFRC) {
483  freq += OSC_TUN_STEP_FREQUENCY * (volatile int32_t)(OSCTUNbits.TUN); // Add oscillator tuning value (is singed)
484  SystemFrequencies.frc = freq; // Copy updated fast RC oscillator frequency after tuning
485  }
486  #endif
487 
488  // Including FRC divider requires the FRCDIV oscillator to be selected
489  if (otype == OSCCON_xOSC_FRCDIVN) { // If oscillator is using internal divider...
490  vbuf = (CLKDIVbits.FRCDIV & 0x0003); // Copy divider SFR bits
491  freq >>= vbuf; // Right-shift oscillator frequency by divider bits (FRCDIV)
492  }
493 
494  }
495 
496  // Internal Low-Power RC Oscillator is always fixed to 32 kHz
497  else if (otype == OSCCON_xOSC_LPRC) {
498  freq = (volatile int32_t)32000; // Oscillator frequency is 32 kHz
499  }
500 
501  // If external clock modes are set, check if given frequency is non-zero
502  else { // If no oscillator frequency is given, it's assumed FRC oscillator is used
503  if (freq == 0) return(0); // Error: no oscillator frequency of external clock given
504  }
505 
506  // Capture system root clock
507  SystemFrequencies.fclk = (volatile uint32_t)freq;
508 
509  // Check for PLL usage and calculate oscillator frequency based on recent settings
510  if ( (otype == OSCCON_xOSC_FRCPLL) || (otype == OSCCON_xOSC_PRIPLL) ) {
511 
512  // Check if PLL is locked in and stable
513  if (!OSCCONbits.LOCK) return(0); // if incorrect/not valid, return error
514 
515  // Check PLL divider N1 for valid value
516  vbuf = (CLKDIVbits.PLLPRE & 0x000F);
517  if((vbuf > 8) || (vbuf == 0)) return (0); // if incorrect/not valid, return error
518  freq /= vbuf; // Divide frequency by divider N1
519 
520  // Check PLL multiplier M
521  vbuf = (PLLFBDbits.PLLFBDIV & 0x00FF);
522  if((vbuf > 200) || (vbuf < 3)) return (0); // if incorrect/not valid, return error
523  freq *= vbuf; // Multiply frequency by Multiplier M
524 
525  // Capture VCO frequency
526  vbuf = (PLLDIVbits.VCODIV & 0x0003);
527  if(vbuf > 3) return (0); // if incorrect/not valid, return error
528  vbuf = 4-vbuf; // Subtract value from 4 to get divider ratio
529  SystemFrequencies.fvco = (volatile uint32_t)(freq/vbuf); // Divide frequency by VCO divider
530 
531  // Check PLL divider N2 for valid value
532  vbuf = (PLLDIVbits.POST1DIV & 0x0007);
533  if((vbuf > 8) || (vbuf == 0)) return (0); // if incorrect/not valid, return error
534  freq /= vbuf; // Divide frequency by divider N2
535 
536  // Check PLL divider N3 for valid value
537  vbuf = (PLLDIVbits.POST2DIV & 0x0007);
538  if((vbuf > 8) || (vbuf == 0)) return (0); // if incorrect/not valid, return error
539  freq /= vbuf; // Divide frequency by divider N3
540 
541  }
542 
543  // Capture PLL output frequency
544  SystemFrequencies.fpllo = (volatile uint32_t)freq;
545 
546  // CPU Clock Divider
547  freq >>= 1; // Divide frequency by 2
548  SystemFrequencies.fosc = (volatile uint32_t)freq;
549 
550  // Peripheral Bus Divider
551  freq >>= 1; // Divide frequency by 2
552  SystemFrequencies.fp = (volatile uint32_t)freq;
553 
554  // Reading DOZE setting
555  if (CLKDIVbits.DOZEN) { // If DOZE divider is enabled
556  vbuf = (CLKDIVbits.DOZE & 0x0003); // Copy divider SFR bits
557  freq >>= vbuf; // Right-shift oscillator frequency by divider bits (DOZE)
558  }
559  SystemFrequencies.fcy = (volatile uint32_t)freq;
560 
561  // Calculate CPU clock period
562  SystemFrequencies.tcy = 1.0/((float)SystemFrequencies.fcy);
563 
564  // Calculate peripheral clock period
565  SystemFrequencies.tp = 1.0/((float)SystemFrequencies.fp);
566 
567  // -----------------------------------------------
568  // Capture APLL frequencies
569  // -----------------------------------------------
570 
571  if (ACLKCON1bits.APLLEN) { // APLL is enabled...
572 
573  // Select input frequency
574  if(ACLKCON1bits.FRCSEL) { freq = SystemFrequencies.frc; }
575  else { freq = SystemFrequencies.fpri; }
576 
577  // Check APLL divider N1 for valid value
578  vbuf = (ACLKCON1bits.APLLPRE & 0x000F);
579  if((vbuf > 8) || (vbuf == 0)) return (0); // if incorrect/not valid, return error
580  freq /= vbuf; // Divide frequency by divider N1
581 
582  // Check PLL multiplier M
583  vbuf = (APLLFBD1bits.APLLFBDIV & 0x00FF);
584  if((vbuf > 200) || (vbuf < 3)) return (0); // if incorrect/not valid, return error
585  freq *= vbuf; // Multiply frequency by Multiplier M
586 
587  // Capture VCO frequency
588  vbuf = (APLLDIV1bits.AVCODIV & 0x0003);
589  if(vbuf > 3) return (0); // if incorrect/not valid, return error
590  vbuf = 4-vbuf; // Subtract value from 4 to get divider ratio
591  SystemFrequencies.afvco = (volatile uint32_t)(freq/vbuf); // Divide frequency by VCO divider
592 
593  // Check PLL divider N2 for valid value
594  vbuf = (APLLDIV1bits.APOST1DIV & 0x0007);
595  if((vbuf > 8) || (vbuf == 0)) return (0); // if incorrect/not valid, return error
596  freq /= vbuf; // Divide frequency by divider N2
597 
598  // Check PLL divider N3 for valid value
599  vbuf = (APLLDIV1bits.APOST2DIV & 0x0007);
600  if((vbuf > 8) || (vbuf == 0)) return (0); // if incorrect/not valid, return error
601  freq /= vbuf; // Divide frequency by divider N3
602  SystemFrequencies.afpllo = freq; // Capture auxiliary PLL output
603 
604  }
605  else { // APLL is disabled...
606  SystemFrequencies.afpllo = 0; // Reset auxiliary PLL output
607  SystemFrequencies.afvco = 0; // Reset auxiliary PLL VCO output
608  }
609 
610  // Return 1=Success, 0=Failure
611  return(1);
612 }
613 
614 
615 // end of file
616 
volatile uint32_t fvco
Definition: p33c_osc.h:132
volatile uint32_t fosc
Definition: p33c_osc.h:128
#define OSC_CLKSW_TIMEOUT
value to set the timeout for clock switching operations
Definition: p33c_osc.c:23
volatile uint32_t fpri
Definition: p33c_osc.h:126
volatile uint32_t fclk
Definition: p33c_osc.h:127
volatile uint32_t fpllo
Definition: p33c_osc.h:131
volatile uint32_t afpllo
Definition: p33c_osc.h:135
volatile uint32_t afvco
Definition: p33c_osc.h:136