Digital Power Starter Kit 3 Firmware
dsPIC33C Buck 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  * ************************************************************************************************/
33 
34 
62 volatile uint16_t p33c_OscFrc_DefaultInitialize(volatile enum CPU_SPEED_DEFAULTS_e cpu_speed)
63 {
64  volatile int16_t retval = 0;
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 
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 // Slave cores 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 
192 volatile uint16_t p33c_Osc_Initialize(volatile struct OSC_CONFIG_s osc_config)
193 {
194 
195  volatile uint16_t _n=0;
196  volatile uint16_t err=0;
197 
198  // =============================================================================================
199  // First make sure we are not running from a PLL derived source, when modifying the PLL settings.
200  // This can be accomplished by intentionally switching to a known non-PLL setting, such as FRC.
201  // Switch to FRC (no divider, no PLL), assuming we aren't already running from that.
202  // =============================================================================================
203 
204  if(OSCCONbits.COSC != 0b000)
205  {
206  // NOSC = 0b000 = FRC without divider or PLL
207  __builtin_write_OSCCONH(OSCCON_xOSC_FRC);
208  // Clear CLKLOCK and assert OSWEN = 1 to initiate switch-over
209  __builtin_write_OSCCONL((OSCCON & 0x7E) | 0x01);
210  //Wait for switch over to complete.
211  while((OSCCONbits.COSC != OSCCONbits.NOSC) && (_n++ < OSC_CLKSW_TIMEOUT));
212  if (_n >= OSC_CLKSW_TIMEOUT) err = OSCERR_RST;
213  }
214 
215  // Switch to target oscillator
216  if ((OSCCONbits.COSC != osc_config.osc_type) && (OSCCONbits.CLKLOCK == 0))
217  {
218  // Switch to desired system clock, if clock switching is enabled
219 
220  #if defined (__P33SMPS_CH__) || defined (__P33SMPS_CK__)
221  // Configure PLL pre-scaler, PLL post-scaler, PLL divisor
222 
223  // Clock switch to desired CPU frequency with the PLL enabled in advance
224  // Configure PLL pre-scaler, both PLL post-scalers, and PLL feedback divider
225  PLLDIVbits.VCODIV = osc_config.VCODIV; // FVCO Scaler 1:n
226  CLKDIVbits.PLLPRE = osc_config.N1; // N1 = PLL pre-scaler
227  PLLFBDbits.PLLFBDIV = osc_config.M; // M = PLL feedback divider
228  PLLDIVbits.POST1DIV = osc_config.N2; // N2 = PLL post-scalers #1
229  PLLDIVbits.POST2DIV = osc_config.N3; // N3 = PLL post-scalers #2
230 
231  #elif defined (__P33SMPS_FJ__) || defined (__P33SMPS_FJA__) || defined (__P33SMPS_FJC__) || \
232  defined (__P33SMPS_EP__)
233 
234  CLKDIVbits.PLLPRE = osc_config.N1; // N1 (non zero)
235  PLLFBD = (osc_config.M - 2); // M = PLLFBD
236  CLKDIVbits.PLLPOST = osc_config.N2; // N2 (non zero)
237 
238  #endif
239 
240  // Initiate Clock Switch to FRC Oscillator with PLL (NOSC=0b011)
241  __builtin_write_OSCCONH(osc_config.osc_type);
242  if(OSCCONbits.COSC != OSCCONbits.NOSC)
243  {
244  // Assert OSWEN and make sure CLKLOCK is clear, to initiate the switching operation
245  __builtin_write_OSCCONL((OSCCON & 0x7F) | 0x01);
246  // Wait for clock switch to finish
247  while((OSCCONbits.COSC != OSCCONbits.NOSC) && (_n++ < OSC_CLKSW_TIMEOUT));
248  if ((OSCCONbits.COSC != OSCCONbits.NOSC) || (_n >= OSC_CLKSW_TIMEOUT))
249  { err = OSCERR_CSF; }
250  }
251 
252  }
253  else if ((OSCCONbits.COSC != osc_config.osc_type) && (OSCCONbits.CLKLOCK == 1))
254  { // If clock switching is disabled and the current clock differs from the desired clock,
255  // return error code
256  err = OSCERR_CSD;
257  }
258 
259  // Lock registers against accidental changes
260  OSCCONbits.CLKLOCK = 1;
261 
262  while((OSCCONbits.LOCK != 1) && (_n++ < OSC_CLKSW_TIMEOUT)); // Wait n while loops for PLL to Lock
263  if ((OSCCONbits.LOCK != 1) || (_n >= OSC_CLKSW_TIMEOUT)) // Error occurred?
264  { err = OSCERR_PLL_LCK; } // => If so, return error code
265 
266 // Return Success/Failure
267  if (err == 0)
268  {
269  return((1 - OSCCONbits.CF)); // Return oscillator fail status bit
270  } // (1=Success, 0=Failure)
271  else
272  { return(err); } // Return error code
273 
274 }
275 
276 
301 volatile uint16_t p33c_OscAuxClk_Initialize(volatile struct AUXOSC_CONFIG_s aux_clock_config)
302 {
303 
304  // Set AVCO divider of Auxiliary PLL
305  APLLDIV1bits.AVCODIV = aux_clock_config.AVCODIV; // AVCO Scaler 1:n
306 
307  // Configure APLL pre-scaler, APLL post-scaler, APLL divisor
308  ACLKCON1bits.APLLPRE = aux_clock_config.N1; // N1 (non zero)
309  APLLFBD1bits.APLLFBDIV = aux_clock_config.M; // M = APLLFBD
310  APLLDIV1bits.APOST1DIV = aux_clock_config.N2; // N2 (non zero)
311  APLLDIV1bits.APOST2DIV = aux_clock_config.N3; // N3 (non zero)
312 
313  // Select clock input source (either primary oscillator or internal FRC)
314  ACLKCON1bits.FRCSEL = aux_clock_config.FRCSEL;
315 
316  // Set Enable-bit of Auxiliary PLL
317  ACLKCON1bits.APLLEN = aux_clock_config.APLLEN;
318 
319  // if user has not enabled the APLL module, exit here
320  if(!aux_clock_config.APLLEN)
321  { return(0); }
322 
323  return(ACLKCON1bits.APLLEN);
324 
325 }
326 
327 
352  volatile uint16_t p33c_OscAuxClk_DefaultInitialize(volatile enum AUX_PLL_DEFAULTS_e afpllo_frequency)
353  {
354  volatile uint16_t retval = 1;
355  volatile struct AUXOSC_CONFIG_s aux_clock_config;
356 
357  // Set FRC as clock input to auxiliary PLL module
358  aux_clock_config.FRCSEL = PLLDIV_ACLKCON_FRCSEL_FRC;
359 
360  // Set auxiliary PLL VCO divider to 1:4
361  aux_clock_config.AVCODIV = APLLDIV_AVCODIV_FVCO_DIV_BY_4;
362 
363  // Select PLL dividers and multiplier in accordance with user parameter
364  if(afpllo_frequency <= 800) {
365  aux_clock_config.N1 = ACLKCON_APLLDIV_N1_1; // Default divider of 1
366  aux_clock_config.M = (afpllo_frequency >> 2); // frequency divided by 4
367  aux_clock_config.N2 = APLLDIV_POST2DIV_N2N3_2; // Default divider of 2
368  aux_clock_config.N3 = APLLDIV_POST2DIV_N2N3_1; // Default divider of 1
369  }
370  else {
371  return(0); // When frequency is out of range, return failure
372  // Most recent APLL setting remains unchanged
373  }
374 
375  // Enable auxiliary PLL
376  aux_clock_config.APLLEN = ACLKCON_APLLEN_ENABLED;
377 
378  // Call auxiliary PLL configuration to apply new settings
379  retval &= p33c_OscAuxClk_Initialize(aux_clock_config);
380 
381  return(retval);
382  }
383 
384 
418 volatile uint16_t p33c_Osc_GetFrequencies(volatile uint32_t main_osc_frequency) {
419 
420  volatile int32_t freq=0;
421  volatile uint16_t vbuf=0;
422  volatile OSCCON_xOSC_TYPE_e otype;
423 
424  // Copy oscillator frequency given as unsigned 32-bit integer into signed 32-bit variable
425  freq = (volatile int32_t)main_osc_frequency;
426 
427  // Capture external oscillator frequency
428  if (main_osc_frequency > 0) {
429  system_frequencies.fpri = (volatile uint32_t)freq;
430  }
431 
432  // read currently selected oscillator
433  otype = OSCCONbits.COSC;
434 
435  // Copy Base FRC frequency into data structure
436  system_frequencies.frc = (volatile int32_t)OSC_FRC_FREQ; // Set default FRC oscillator frequency
437 
438  // Depending on detected oscillator type, set/override oscillator frequency
439 
440  // For all modes using the internal Fast RC Oscillator (FRC), check input divider and tuning register
441  if ((otype == OSCCON_xOSC_FRC) || (otype == OSCCON_xOSC_BFRC) || (otype == OSCCON_xOSC_FRCPLL) || (otype == OSCCON_xOSC_FRCDIVN)) {
442 
443  freq = (volatile int32_t)OSC_FRC_FREQ; // Oscillator frequency is 8 MHz
444 
445  #if defined (__P33SMPS_CK__) || defined (__P33SMPS_CH_MSTR__)
446  // FRC tuning register does not affect Backup FRC clock
447  if(otype != OSCCON_xOSC_BFRC) {
448  freq += OSC_TUN_STEP_FREQUENCY * (volatile int32_t)(OSCTUNbits.TUN); // Add oscillator tuning value (is singed)
449  system_frequencies.frc = freq; // Copy updated fast RC oscillator frequency after tuning
450  }
451  #endif
452 
453  // Including FRC divider requires the FRCDIV oscillator to be selected
454  if (otype == OSCCON_xOSC_FRCDIVN) { // If oscillator is using internal divider...
455  vbuf = (CLKDIVbits.FRCDIV & 0x0003); // Copy divider SFR bits
456  freq >>= vbuf; // Right-shift oscillator frequency by divider bits (FRCDIV)
457  }
458 
459  }
460 
461  // Internal Low-Power RC Oscillator is always fixed to 32 kHz
462  else if (otype == OSCCON_xOSC_LPRC) {
463  freq = (volatile int32_t)32000; // Oscillator frequency is 32 kHz
464  }
465 
466  // If external clock modes are set, check if given frequency is non-zero
467  else { // If no oscillator frequency is given, it's assumed FRC oscillator is used
468  if (freq == 0) return(0); // Error: no oscillator frequency given
469  }
470 
471  // Capture system root clock
472  system_frequencies.fclk = (volatile uint32_t)freq;
473 
474  // Check for PLL usage and calculate oscillator frequency based on recent settings
475  if ( (otype == OSCCON_xOSC_FRCPLL) || (otype == OSCCON_xOSC_PRIPLL) ) {
476 
477  // Check if PLL is locked in and stable
478  if (!OSCCONbits.LOCK) return(0); // if incorrect/not valid, return error
479 
480  // Check PLL divider N1 for valid value
481  vbuf = (CLKDIVbits.PLLPRE & 0x000F);
482  if((vbuf > 8) || (vbuf == 0)) return (0); // if incorrect/not valid, return error
483  freq /= vbuf; // Divide frequency by divider N1
484 
485  // Check PLL multiplier M
486  vbuf = (PLLFBDbits.PLLFBDIV & 0x00FF);
487  if((vbuf > 200) || (vbuf < 3)) return (0); // if incorrect/not valid, return error
488  freq *= vbuf; // Multiply frequency by Multiplier M
489 
490  // Capture VCO frequency
491  vbuf = (PLLDIVbits.VCODIV & 0x0003);
492  if(vbuf > 3) return (0); // if incorrect/not valid, return error
493  vbuf = 4-vbuf; // Subtract value from 4 to get divider ratio
494  system_frequencies.fvco = (volatile uint32_t)(freq/vbuf); // Divide frequency by VCO divider
495 
496  // Check PLL divider N2 for valid value
497  vbuf = (PLLDIVbits.POST1DIV & 0x0007);
498  if((vbuf > 8) || (vbuf == 0)) return (0); // if incorrect/not valid, return error
499  freq /= vbuf; // Divide frequency by divider N2
500 
501  // Check PLL divider N3 for valid value
502  vbuf = (PLLDIVbits.POST2DIV & 0x0007);
503  if((vbuf > 8) || (vbuf == 0)) return (0); // if incorrect/not valid, return error
504  freq /= vbuf; // Divide frequency by divider N3
505 
506  }
507 
508  // Capture PLL output frequency
509  system_frequencies.fpllo = (volatile uint32_t)freq;
510 
511  // CPU Clock Divider
512  freq >>= 1; // Divide frequency by 2
513  system_frequencies.fosc = (volatile uint32_t)freq;
514 
515  // Peripheral Bus Divider
516  freq >>= 1; // Divide frequency by 2
517  system_frequencies.fp = (volatile uint32_t)freq;
518 
519  // Reading DOZE setting
520  if (CLKDIVbits.DOZEN) { // If DOZE divider is enabled
521  vbuf = (CLKDIVbits.DOZE & 0x0003); // Copy divider SFR bits
522  freq >>= vbuf; // Right-shift oscillator frequency by divider bits (DOZE)
523  }
524  system_frequencies.fcy = (volatile uint32_t)freq;
525 
526  // Calculate CPU clock period
528 
529  // Calculate peripheral clock period
531 
532  // -----------------------------------------------
533  // Capture APLL frequencies
534  // -----------------------------------------------
535 
536  if (ACLKCON1bits.APLLEN) { // APLL is enabled...
537 
538  // Select input frequency
539  if(ACLKCON1bits.FRCSEL) { freq = system_frequencies.frc; }
540  else { freq = system_frequencies.fpri; }
541 
542  // Check APLL divider N1 for valid value
543  vbuf = (ACLKCON1bits.APLLPRE & 0x000F);
544  if((vbuf > 8) || (vbuf == 0)) return (0); // if incorrect/not valid, return error
545  freq /= vbuf; // Divide frequency by divider N1
546 
547  // Check PLL multiplier M
548  vbuf = (APLLFBD1bits.APLLFBDIV & 0x00FF);
549  if((vbuf > 200) || (vbuf < 3)) return (0); // if incorrect/not valid, return error
550  freq *= vbuf; // Multiply frequency by Multiplier M
551 
552  // Capture VCO frequency
553  vbuf = (APLLDIV1bits.AVCODIV & 0x0003);
554  if(vbuf > 3) return (0); // if incorrect/not valid, return error
555  vbuf = 4-vbuf; // Subtract value from 4 to get divider ratio
556  system_frequencies.afvco = (volatile uint32_t)(freq/vbuf); // Divide frequency by VCO divider
557 
558  // Check PLL divider N2 for valid value
559  vbuf = (APLLDIV1bits.APOST1DIV & 0x0007);
560  if((vbuf > 8) || (vbuf == 0)) return (0); // if incorrect/not valid, return error
561  freq /= vbuf; // Divide frequency by divider N2
562 
563  // Check PLL divider N3 for valid value
564  vbuf = (APLLDIV1bits.APOST2DIV & 0x0007);
565  if((vbuf > 8) || (vbuf == 0)) return (0); // if incorrect/not valid, return error
566  freq /= vbuf; // Divide frequency by divider N3
567  system_frequencies.afpllo = freq; // Capture auxiliary PLL output
568 
569  }
570  else { // APLL is disabled...
571  system_frequencies.afpllo = 0; // Reset auxiliary PLL output
572  system_frequencies.afvco = 0; // Reset auxiliary PLL VCO output
573  }
574 
575  // Return 1=Success, 0=Failure
576  return(1);
577 }
578 
579 
580 // end of file
581 
OSCILLATOR_SYSTEM_FREQUENCIES_s::fpllo
volatile uint32_t fpllo
Definition: p33c_osc.h:132
p33c_OscFrc_DefaultInitialize
volatile uint16_t p33c_OscFrc_DefaultInitialize(volatile enum CPU_SPEED_DEFAULTS_e cpu_speed)
Initializes the major oscillator and the PLL module step by step by using clock switching in software...
Definition: p33c_osc.c:62
OSCILLATOR_SYSTEM_FREQUENCIES_s::frc
volatile uint32_t frc
Definition: p33c_osc.h:126
p33c_Osc_GetFrequencies
volatile uint16_t p33c_Osc_GetFrequencies(volatile uint32_t main_osc_frequency)
This routine reads all oscillator related SFRs recalculating the various frequencies across clock dom...
Definition: p33c_osc.c:418
OSCILLATOR_SYSTEM_FREQUENCIES_s::fcy
volatile uint32_t fcy
Definition: p33c_osc.h:130
OSCILLATOR_SYSTEM_FREQUENCIES_s::fclk
volatile uint32_t fclk
Definition: p33c_osc.h:128
OSCILLATOR_SYSTEM_FREQUENCIES_s::tcy
volatile float tcy
Definition: p33c_osc.h:135
OSCILLATOR_SYSTEM_FREQUENCIES_s::afpllo
volatile uint32_t afpllo
Definition: p33c_osc.h:136
p33c_OscFrc_Initialize
volatile uint16_t p33c_OscFrc_Initialize(volatile enum CLKDIV_FRCDIVN_e frc_div, volatile enum OSCTUN_TUN_e frc_tune)
Initializes the internal RC oscillator divider and tuning register.
Definition: p33c_osc.c:138
OSC_CLKSW_TIMEOUT
#define OSC_CLKSW_TIMEOUT
value to set the timeout for clock switching operations
Definition: p33c_osc.c:23
OSCILLATOR_SYSTEM_FREQUENCIES_s::fpri
volatile uint32_t fpri
Definition: p33c_osc.h:127
OSCILLATOR_SYSTEM_FREQUENCIES_s::fp
volatile uint32_t fp
Definition: p33c_osc.h:131
OSCILLATOR_SYSTEM_FREQUENCIES_s::fosc
volatile uint32_t fosc
Definition: p33c_osc.h:129
OSCILLATOR_SYSTEM_FREQUENCIES_s::fvco
volatile uint32_t fvco
Definition: p33c_osc.h:133
OSCILLATOR_SYSTEM_FREQUENCIES_s
Global data structure holding system frequencies of different clock domains.
Definition: p33c_osc.h:125
p33c_OscAuxClk_DefaultInitialize
volatile uint16_t p33c_OscAuxClk_DefaultInitialize(volatile enum AUX_PLL_DEFAULTS_e afpllo_frequency)
Initializes the auxiliary clock and its PLL module step by step in software. Each step is tested and ...
Definition: p33c_osc.c:352
system_frequencies
volatile struct OSCILLATOR_SYSTEM_FREQUENCIES_s system_frequencies
Definition: p33c_osc.c:32
OSCILLATOR_SYSTEM_FREQUENCIES_s::afvco
volatile uint32_t afvco
Definition: p33c_osc.h:137
p33c_OscAuxClk_Initialize
volatile uint16_t p33c_OscAuxClk_Initialize(volatile struct AUXOSC_CONFIG_s aux_clock_config)
Initializes the auxiliary clock and its PLL module step by step in software. Each step is tested and ...
Definition: p33c_osc.c:301
OSCILLATOR_SYSTEM_FREQUENCIES_s::tp
volatile float tp
Definition: p33c_osc.h:134