Skip to content

Commit 2bb5b11

Browse files
authored
Merge pull request #9 from espreng/bugfix-fcpu-corrected
Bug fix: Added checks for correct F_CPU_CORRECTED calculation
2 parents 3a86847 + 9420621 commit 2bb5b11

File tree

2 files changed

+153
-9
lines changed

2 files changed

+153
-9
lines changed

Diff for: cores/arduino/Arduino.h

+10-1
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,9 @@ extern "C"{
5656
#define VDD ADC_REFSEL_VDDREF_gc
5757
#define EXTERNAL ADC_REFSEL_VREFA_gc
5858

59-
extern uint32_t F_CPU_CORRECTED;
59+
#define VCC_5V0 2
60+
#define VCC_3V3 1
61+
#define VCC_1V8 0
6062

6163
#define interrupts() sei()
6264
#define noInterrupts() cli()
@@ -66,7 +68,14 @@ extern uint32_t F_CPU_CORRECTED;
6668
#define _NOP() do { __asm__ volatile ("nop"); } while (0)
6769
#endif
6870

71+
/* Allows performing a correction on the CPU value using the signature row
72+
values indicating oscillator error provided from the device manufacturer */
73+
#define PERFORM_SIGROW_CORRECTION_F_CPU 0
6974

75+
/* Variable containing corrected F_CPU value, after checks for safe operating
76+
frequency vs supply voltage, oscillator fuse setting and MCLK divider.
77+
Also includes the correction from signature row values if above #define
78+
PERFORM_SIGROW_CORRECTION_F_CPU = 1 */
7079
extern uint32_t F_CPU_CORRECTED;
7180

7281
uint16_t clockCyclesPerMicrosecond(uint32_t clk);

Diff for: cores/arduino/wiring.c

+143-8
Original file line numberDiff line numberDiff line change
@@ -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_5V0;
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 = 0xFF;
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_5V0){
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

Comments
 (0)