RT LOOP - also for Arduinolinks to codeOften you do not have any operation system and maybe do even not need it. Still you might need to run precise periodic code - aka realtime. You can
Timers based on increment of a counter every xx millisecond or second will do a wrap around some time in the future. unsigned char x=0; while (1) x++; x will be 0 1 2 3 4 ... 250 251 252 253 254 255 0 1 2 ^-- wrap around Unsigned integer math has some - in this case - positive sideffects. The below handles nicely wrap around without any problem :-) First example is just pure C you can test on a std PC Next is a Arduino implementation using millis Leap millisecondsIn some systems leap milli seconds is added to time to compensate for that the timer interrupt differs from the wanted time. In an AVR arduino beware that millis from time to time add lead milliseconds from https://forum.arduino.cc/t/when-does-millis-increment/144482/5 The interrupt fires every 1.024 mS, so millis() increments that often.
Because of the slight discrepancy (the 0.024 part) the interrupt will Jens: Every 41,6 interrupt millis counter is incremented by 2 and not 1. So approx every 42 msec is a leap millisecond added In vrs 1.18.19 the file wiring.c holds the interupt code <your install>hardwarearduinoavrcoresarduinowiring.c millis code in timer interrupt ISRHere is a fragment of Arduinos timer interrupt. You can see leap milliseconds is added from time to time. from arduino source .. MICROSECONDS_PER_TIMER0_OVERFLOW = (64*256)/16 = 1024 So an interrupt every 1.024msec not every millisecond So for every tick we are 24 micro sec more behind We therefor need to add a millisec leap. So every 1000 usec/24usec [tick] we must add this leap millis every 1000/24 = 41,6666.. tick. This is implemented in the Arduino timer ISR But you must live we an extra millisecond every approx 42 "millissecond" wiring.c:#define FRACT_INC ((MICROSECONDS_PER_TIMER0_OVERFLOW % 1000) >> 3) == 24 >> 3 = 3 wiring.c:#define FRACT_MAX (1000 >> 3) == 1000/ (2^3) = 125 Approx every 42(125/3) intr a leap millisecond is added ISR(TIMxxOVF_vect) m += MILLIS_INC; f += FRACT_INC; if (f >= FRACT_MAX) { f -= FRACT_MAX; m += 1; } timer0_fract = f; timer0_millis = m; timer0_overflow_count++; Arduino leap test programFor chekking how often it occurs just mail a test program like this: unsigned long tLast, tNow; void setup() { // last line tNow = tLast = millis(); Serial.begin(115200); } void loop() { noInterrupts(); tNow = millis(); interrupts(); if (2 <= tNow - tLast) { //lead millisecond Serial.println(millis()); } tLast = tNow; } /* output ... ... delta 3243 3286 43 3328 42 3371 43 3414 43 3456 42 3499 43 3542 43 3584 42 41,66 */ The wrap around problem reaching max UINTStraight forward wrap around code example for testning. // small test program // use unsigned char instead of unsigned to reach wrap around with 256 steps #include <stdio.h> #include <math.h> void main() { int c =0; unsigned char x,x0,y,z; // char -> int in real life x0 = x = 12; y = 3; z = x; for (int i=0; i < 500; i++) { x0 = x; x++; c++; if (x < x0) { // wrap z+=y ; x+=y ; } if (x-z >= y) { z+=y; printf("ping %i\n",c); c = 0; } printf(" x%i y %i z %i\n",x,y,z); } } Arduino implVanilla Arduino code where you can add your code at now its time … You will have a leap millisecond approx 42 times a second const unsigned long tPeriod = 100; // 100 msec periode unsigned long tLastRun; void setup() { TLastRun = millis(); } void loop() { //busy waiting if (millis() - tLastRun >= tPeriod) { tLastRun += tPeriod; delay (tPeriod/2); // emulate running critical code 50% of time } } No wrap problem solution presented here: millisrt.pdf Thanks to Nick Gammon for this idea code snippets |