At least millis() is badly-implemented. I need to dynamically change the clock frequency. Well under 1MHz for normal operation (powered by a coin cell). Up frequency to 2MHz if I need to write to the EEPROM. But never, ever, using a fixed clock frequency. Why doesn't millis() handle this properly? Where can I find the sources so I can build a millis() that actually works when the clock frequency is not fixed?
Where can I find the sources so I can build a millis() that actually works when the clock frequency is not fixed?
Which core?
Edit: Wait a minute:
Retired Ph.D. in Computer Science
Seriously?
Use grep/grepwin and search the Arduino installation directory to find the implementation of millis()'
There's a variable called F_CPU that's predefined for the processor speed specified in your bootloader. It's possible you could change that value to whatever you select, such as 1000000, and the millis() function would notice. But I don't remember whether the Timer-0 ISR goes beyond the 8MHz and 16MHz settings. Anyway, I think it's all in wiring.c.
Of course you could always write your own. Here's one I did. I think you would just need to adjust the prescaler for different F_CPU values.
/*
This is a replacement for the ISR(TIMER0_OVF_vect)
interrupt that drives millis(). It disables the OVF
interrupt, enables the COMPA interrupt, sets OCR0A to 249,
and changes Timer0 to CTC mode. This results in an
interrupt every 250 timer clock cycles, which is exactly 1ms
for a 16MHz crystal, or 2ms for an 8MHz crystal. The new ISR
increments millis, but since the interrupt rate is exactly
correct, no periodic double increment of millis is needed.
Using this code probably means you can't do any analog
writes that use Timer0, which would include pins 5 and 6.
*/
extern volatile unsigned long timer0_millis; //these defined in wiring.c
extern volatile unsigned long timer0_overflow_count;
volatile unsigned long MILLIS_INCB = (64 * 250) / (F_CPU / 1000); // ms between timer overflows
const int cycleTime = 500; // flash LED every second
int LEDcount = cycleTime;
unsigned long oldMillis = millis();
unsigned long newMillis = 0;
void setup() { //Set up alternate interrupt
// at 249 on timer0
cli(); // disable interrupts while doing this
TCCR0A = 0; // set entire TCCR0A register to 0
TCCR0B = 0; // same for TCCR0B
TCNT0 = 0; // initialize timer0 count to 0
OCR0A = 249; // set top of counter
TIMSK0 &= ~bit(TOIE0); // disable overflow interrupt
TCCR0A |= bit(WGM01); // turn on CTC mode
TCCR0B |= (bit(CS01)+bit(CS00)); // Set CS00&CS01 bits for prescaler = 64
TIMSK0 |= bit(OCIE0A); // enable timer compare interrupt
sei(); // enable interrupts
pinMode(13,OUTPUT);
digitalWrite(13,HIGH);
}
void loop() { // flashes LED for 1/2 second
// every second
newMillis = millis();
if ((newMillis - oldMillis) > 0) {
oldMillis = newMillis;
LEDcount -= 1;
if (LEDcount == 0) {
digitalWrite(13,!digitalRead(13)); // invert pin 13 state
LEDcount = cycleTime;
}
}
}
ISR(TIMER0_COMPA_vect) { // this is the new ISR - much
// simpler than original
timer0_millis += MILLIS_INCB;
timer0_overflow_count++;
}
Why doesn't millis() handle this properly
It wasn't part of the design.
Since the Arduino libraries do not provide a mechanism for changing the clock speed, they do not "need" a millis() implementation that supports changing the clock speed.
Where can I find the sources so I can build a millis() that actually works when the clock frequency is not fixed?
(that's for an AVR. The exact location on your system depends on your OS and exactly which versions of the IDE and Core you have installed. But it should always be in "wiring.c" - you can find the exactly location by looking at the build log after enabling "verbose" for compilation in the preferences dialog.
The megaAVR core ("Nano Every", "Uno WiFi 2") actually contains an example that handles clock rates determined at runtime (and perhaps variable?)
(at the expense of making the timer ISR rather slow and bloated )