@@ -271,17 +271,152 @@ void init()
271271{
272272 // this needs to be called before setup() or some functions won't
273273 // work there
274+
275+ /*************************** GET VCC & FUSE SETTING ***************************/
276+
277+
278+ /* Measure VDD using ADC */
279+ uint8_t supply_voltage ;
280+
281+ /* Initialize AC reference (what we are measuring) - 1.5V known */
282+ VREF .CTRLA |= VREF_AC0REFSEL_1V5_gc ;
283+
284+ /* Enable AC reference */
285+ VREF .CTRLB |= VREF_AC0REFEN_bm ;
286+
287+ /* DAC to max -- output reference voltage */
288+ AC0 .DACREF = 0xFF ;
289+
290+ /* Enable DAC REF by selecting it as input and enabling AC */
291+ AC0 .MUXCTRLA |= AC_MUXNEG_DACREF_gc ;
292+ AC0 .CTRLA |= ADC_ENABLE_bm ;
293+
294+ /* Initialize ADC reference (VDD) */
295+ ADC0 .CTRLC = ADC_REFSEL_VDDREF_gc ;
296+
297+ /* Initialize MUX (DAC/AC reference from VREF) */
298+ ADC0 .MUXPOS = ADC_MUXPOS_DACREF_gc ;
299+
300+ /* Enable ADC */
301+ ADC0 .CTRLA |= ADC_ENABLE_bm ;
302+
303+ /* Start a conversion */
304+ ADC0 .COMMAND |= ADC_STCONV_bm ;
305+
306+ /* Wait until result is ready */
307+ while (!(ADC0 .INTFLAGS & ADC_RESRDY_bm ));
308+
309+ /* Result ready */
310+ /* supply_voltage = (VIN * 1024)/result where VIN = 1.5V from VREF */
311+ uint16_t adc_result = ADC0 .RES ;
312+
313+ uint16_t voltage = (15 * 1024 ) / adc_result ; /* using 1.5 << 1 to avoid using float */
314+
315+ /* Only for the purposes of staying within safe operating range -- approximate */
316+ if (voltage >= 48 ){ /* 4.8V+ -> 5V */
317+ supply_voltage = VCC_5V ;
318+ } else if (voltage >= 30 ){ /* 3V-4V7 -> 3V3 */
319+ supply_voltage = VCC_3V3 ;
320+ } else { /* < 3V -> 1V8 */
321+ supply_voltage = VCC_1V8 ;
322+ }
323+
324+ /* Fuse setting for 16/20MHz oscillator */
325+ uint8_t fuse_setting = FUSE .OSCCFG & FUSE_FREQSEL_gm ;
326+
327+ /* Deinitialize ADC, AC & VREF */
328+ ADC0 .CTRLA = 0x00 ;
329+ ADC0 .MUXPOS = 0x00 ;
330+ ADC0 .CTRLC = 0x00 ;
331+
332+ AC0 .CTRLA = 0x00 ;
333+ AC0 .MUXCTRLA = 0x00 ;
334+ AC0 .DACREF = 0x00 ;
335+
336+ VREF .CTRLB = 0x00 ;
337+ VREF .CTRLA = 0x00 ;
274338
275339/******************************** CLOCK STUFF *********************************/
276340
277- /* Disable system clock prescaler - F_CPU should now be ~16MHz */
278- _PROTECTED_WRITE (CLKCTRL_MCLKCTRLB , 0x00 );
279-
280- /* Calculate actual F_CPU with error values from signature row */
281- int8_t sigrow_val = SIGROW .OSC16ERR5V ;
282- int64_t cpu_freq = F_CPU ;
283- cpu_freq *= (1024 + sigrow_val );
284- cpu_freq /= 1024 ;
341+ int64_t cpu_freq ;
342+
343+ #if (PERFORM_SIGROW_CORRECTION_F_CPU == 1 )
344+ int8_t sigrow_val = 0 ;
345+ #endif
346+
347+ /* Initialize clock divider to stay within safe operating area */
348+
349+ if (supply_voltage >= VCC_5V ){
350+
351+ /* Disable system clock prescaler - F_CPU should now be ~16/20MHz */
352+ _PROTECTED_WRITE (CLKCTRL_MCLKCTRLB , 0x00 );
353+
354+ /* Assign cpu_freq value and sigrow_val depending on fuse setting */
355+ if (fuse_setting == FREQSEL_20MHZ_gc ){
356+ cpu_freq = 20000000 ;
357+
358+ #if (PERFORM_SIGROW_CORRECTION_F_CPU == 1 )
359+ sigrow_val = SIGROW .OSC20ERR5V ;
360+ #endif
361+
362+ } else { /* fuse_setting == FREQSEL_16MHZ_gc */
363+ cpu_freq = 16000000 ;
364+
365+ #if (PERFORM_SIGROW_CORRECTION_F_CPU == 1 )
366+ sigrow_val = SIGROW .OSC16ERR5V ;
367+ #endif
368+
369+ }
370+
371+ } else if (supply_voltage == VCC_3V3 ) {
372+
373+ /* Enable system clock prescaler to DIV2 - F_CPU should now be ~8/10MHz */
374+ _PROTECTED_WRITE (CLKCTRL_MCLKCTRLB , (CLKCTRL_PEN_bm | CLKCTRL_PDIV_2X_gc ));
375+
376+ /* Assign cpu_freq value and sigrow_val depending on fuse setting */
377+ if (fuse_setting == FREQSEL_20MHZ_gc ){
378+ cpu_freq = 10000000 ;
379+
380+ #if (PERFORM_SIGROW_CORRECTION_F_CPU == 1 )
381+ sigrow_val = SIGROW .OSC20ERR3V ;
382+ #endif
383+
384+ } else { /* fuse_setting == FREQSEL_16MHZ_gc */
385+ cpu_freq = 8000000 ;
386+
387+ #if (PERFORM_SIGROW_CORRECTION_F_CPU == 1 )
388+ sigrow_val = SIGROW .OSC16ERR3V ;
389+ #endif
390+ }
391+
392+ } else {
393+ /* Shouldn't get here but just in case... */
394+
395+ /* Enable system clock prescaler to DIV4 - F_CPU should now be ~4/5MHz */
396+ _PROTECTED_WRITE (CLKCTRL_MCLKCTRLB , (CLKCTRL_PEN_bm | CLKCTRL_PDIV_4X_gc ));
397+
398+
399+ if (fuse_setting == FREQSEL_20MHZ_gc ){
400+ cpu_freq = 5000000 ;
401+ #if (PERFORM_SIGROW_CORRECTION_F_CPU == 1 )
402+ sigrow_val = SIGROW .OSC20ERR3V ;
403+ #endif
404+
405+ } else { /* fuse_setting == FREQSEL_16MHZ_gc */
406+ cpu_freq = 4000000 ;
407+ #if (PERFORM_SIGROW_CORRECTION_F_CPU == 1 )
408+ sigrow_val = SIGROW .OSC16ERR3V ;
409+ #endif
410+ }
411+ }
412+
413+ #if (PERFORM_SIGROW_CORRECTION_F_CPU == 1 )
414+ /* Calculate actual F_CPU with error values from signature row */
415+ cpu_freq *= (1024 + sigrow_val );
416+ cpu_freq /= 1024 ;
417+ #endif /* (CORRECT_F_CPU == 1) */
418+
419+ /* Apply calculated value to F_CPU_CORRECTED */
285420 F_CPU_CORRECTED = (uint32_t )cpu_freq ;
286421
287422/***************************** TIMERS FOR PWM *********************************/
0 commit comments