/*
Creating an endless timer that defeats C/C++ overflow.
In my project to create a MIDI/ADINsync/tap tempo sync box, I need a
master clock for the cases when the sync box is running in master/stripe or
master/all mode. The trouble is, any binary number used as a counter will
overflow when it reaches its maximum value. In milis(), this cause a rollover
every 49 to 50 days, and your code will probably hang. In micros, it happens
after 2 and a half hours! Not useful for a sync box!
To overcome this, this prototype for my master clock function for my timing
and switching class library uses type casting and differences the previous
time test from the current time test. This is a proof-of-concept sketch as
I progress through R&D for a universal class library for software timing,
edge detection, time division (frequency multiplying) and frequency division.
*/
uint32_t period = 500; // clock period in mS
void setup() {
pinMode(13, OUTPUT); // set LED pin as output
}
void loop() {
bool state = clock(period); // get clock state
digitalWrite(13,state); // set LED to clock state
}
/* // Alternatively, even more elegant code...
void loop(){
digitalWrite(13,clock(period)); // you can call clock(period) directly
}
*/
bool clock(unsigned int period) { // declare the clock function
static bool state = false; // a place to locally store state var
static uint32_t backThen = 0; // a place to store the previous time
uint32_t rightNow = 0; // a place to store the current time
(uint32_t)(rightNow = millis()); // get the current time
// test if now is period millis after
if((uint32_t)(rightNow - backThen) > period){ // current time
state = !state; // if it is, change the clock state
(uint32_t)(backThen = rightNow); // set previous time to current time
} // for next test
return state; // return the current clock state
}
/*
NB: All "(type)" casts ensure the system returns a byte, not a default
integer. When using this technique in real life you would be using unsigned
longs or integers for greater timing accuracy. The bytes used in this demo
are to show the system rolling over on overflow. IRL you'd use unsigned
long vars, that's what millis and micros are, unsigned longs.
Why this won't freeze at overflow...
By subtracting the previous time (backThen) from the latest (rightNow), the
result will always be a small number, never much more than the period and
usually less, so the test condition will always be valid for a true or false
condition. Run this to see how it works...
void setup() {
Serial.begin(115200);
unsigned long a = 1;
unsigned long b = 4294967295; //unsigned long maximum value
Serial.println(a-b);
}
void loop(){
// nothing needed here
}
You can accurately time half-wavelengths with periods up to the maximum
value of your variable. For instance, you can time a flash half wavelength
up to 2x255mS (510mS per on/off) per half wavelength if you use bytes
(which are inherently unsigned), 2x65535mS if you use unsigned ints
(or uint16_t) and 2*24294967295mS if you use unsigned longs (or uint32_t)
for your timing variables, but your timer will never stall at overflow
if you subtract backThen from rightNow.
Many sincere thanks to Norwegian Creations for their clear and
understandable explanation of how counting time without rollovers fails.
Their detailed explanation is here:-
https://www.norwegiancreations.com/2018/10/arduino-tutorial-avoiding-the-overflow-issue-when-using-millis-and-micros/
*/