Why I would lost 1 millisecond between two millis() call?

Hi Guys,

I found out my code can not gives me out each millisecond tick, which I would like to do some business there. The code is as followed:

unsigned long cLastTick = 0; // Time stamp of last check point of the millis unit
void setup() {
  Serial.begin(115200);
}

void loop() {
  unsigned long noW = millis(); // Right now time in millisecond
  if ((noW - cLastTick) < 1) { return; }
  if ((noW - cLastTick) > 1)
  {
    Serial.print("Over ticked:");
    Serial.println(noW - cLastTick);
  }
  cLastTick =noW;
}

After start Serial Monitor, It gives out

Over ticked:2
Over ticked:2
Over ticked:2
...

Is that caused by the missing of a real time quartz on Arduino? (It is a Arduino Pro mini 5V 16MHz)

Cheers

Su

If the quartz clock on the Arduino was unreliable none of our sketches would ever work. If you want to do something with that frequency, look into timers and interrupts.

Gfast:
Is that caused by the missing of a real time quartz on Arduino? (It is a Arduino Pro mini 5V 16MHz)

No, that behaviour is not related to the accuracy of the oszillator at all.

It is caused by the fact that the frequency of the Timer0 overflow is based on a binary division of the Arduino Frequency
that produces a small fractional part (error) that has to be compensated from time to time.
If the errors adds up enough, millis() skips a value.

Have a look at the source code in wiring.c:

	m += MILLIS_INC;
	f += FRACT_INC;
	if (f >= FRACT_MAX) {
		f -= FRACT_MAX;
		m += 1;
	}

here the whole context for reference.

// the prescaler is set so that timer0 ticks every 64 clock cycles, and the
// the overflow handler is called every 256 ticks.
#define MICROSECONDS_PER_TIMER0_OVERFLOW (clockCyclesToMicroseconds(64 * 256))

// the whole number of milliseconds per timer0 overflow
#define MILLIS_INC (MICROSECONDS_PER_TIMER0_OVERFLOW / 1000)

// the fractional number of milliseconds per timer0 overflow. we shift right
// by three to fit these numbers into a byte. (for the clock speeds we care
// about - 8 and 16 MHz - this doesn't lose precision.)
#define FRACT_INC ((MICROSECONDS_PER_TIMER0_OVERFLOW % 1000) >> 3)
#define FRACT_MAX (1000 >> 3)

volatile unsigned long timer0_overflow_count = 0;
volatile unsigned long timer0_millis = 0;
static unsigned char timer0_fract = 0;

#if defined(TIM0_OVF_vect)
ISR(TIM0_OVF_vect)
#else
ISR(TIMER0_OVF_vect)
#endif
{
	// copy these to local variables so they can be stored in registers
	// (volatile variables must be read from memory on every access)
	unsigned long m = timer0_millis;
	unsigned char f = timer0_fract;

	m += MILLIS_INC;
	f += FRACT_INC;
	if (f >= FRACT_MAX) {
		f -= FRACT_MAX;
		m += 1;
	}

	timer0_fract = f;
	timer0_millis = m;
	timer0_overflow_count++;
}

unsigned long millis()
{
	unsigned long m;
	uint8_t oldSREG = SREG;

	// disable interrupts while we read timer0_millis or we might get an
	// inconsistent value (e.g. in the middle of a write to timer0_millis)
	cli();
	m = timer0_millis;
	SREG = oldSREG;

	return m;
}

Init cLastTick at Setup to millis. It always 0 at your code.

alesam: Init cLastTick at Setup to millis. It always 0 at your code.

millis() starts with zero and setup() rarely takes longer than a millisecond. And even if that were a problem, it would only account for one of the skips.

alesam: Init cLastTick at Setup to millis. It always 0 at your code.

It is initialised to 0. The value is set to a new value at the bottom of loop (when it reaches there).

Metallor: If the quartz clock on the Arduino was unreliable none of our sketches would ever work. If you want to do something with that frequency, look into timers and interrupts.

Hi Guys,

All your Comments here are very helpful for me!

to Metallor: My specific Problem is that I need to in a certain mount of time (Unit: Millisecond) do a certain mount of ticks as even as possible. So I thought I can implementate bresenham algorithm. My X-Axis is timeline (millisecond), the Y-Axis the Ticks.


I did a at lease 1 hour google-search before this post. IMHO What I found really may useful ist This one. But interestingly the source-code is empty according to that Post. Any suggestion according to my design requirement?

Cheers

Gfast2

Gfast: My specific Problem is that I need to in a certain mount of time (Unit: Millisecond) do a certain mount of ticks as even as possible. ... Any suggestion according to my design requirement?

Use micros() for the timing.

Whandall:
Use micros() for the timing.

Hi Whandall,

Thanks for the suggestion, I will check this out today.

BTW:

Is that means, the micro() do not have such a effect?

Cheers

Gfast2

It's even worse with micros() (in units of micros) it even steps by four always IIRC, but if you want millis accuracy that does not matter.

You will never get 1 because of a slight error in the coding:

if ((noW - cLastTick) > 1)

should be

if ((noW - cLastTick) >= 1)

But even with that, the output is occasionally 2.

Also, shouldn't you be setting cLastTick inside the IF statement, instead of every time through the loop?

I notice that the output is more consistently 1 if I only print the number without the text, the time it takes to send the serial data could be a problem.

david_2018: You will never get 1 because of a slight error in the coding:

You missed the point: the purpose of the sketch is to demonstrate that millis() can skip a value.

Just use micros() and be done with it. Indeed, micros() does not do every number (it jumps in increments of 4 or 8); however, it is better to lose 4 microseconds than 1 millisecond.

Indeed, sometimes millis() skips numbers. https://ucexperiment.wordpress.com/2012/03/16/examination-of-the-arduino-millis-function/

oqibidipo: You missed the point: the purpose of the sketch is to demonstrate that millis() can skip a value.

I see that now. I was distracted a bit by the first IF statement testing for 《 1, and no test for = 1.

An alternative if you really need a 1ms milli, and don’t need timer0 for anything else, is to disable the interrupt currently used for the timer0 0xFF overflow, enable your own interrupt for a compare register and change the timer0 mode so it rolls over at the compare value rather than at 0xFF, and set the compare value to 249 (0xF9), which would produce an interrupt every 250 clock cycles, which actually would be 1 ms.

Then I think the ISR for your new interrupt would just be a duplicate of the code in wiring.c for the existing millis interrupt, but minus all the FRACT stuff.

I have done the first part on a Nano, and it works fine, but didn’t need millis at all so didn’t try to put the old millis code in the new ISR. Has anyone else done their own millis this way?

Su,

You are printing the difference (noW - cLastTick) when it is > 1. Since the next integer > 1 is 2, that's what you get!

Cheers, Ben

benek: Su,

You are printing the difference (noW - cLastTick) when it is > 1. Since the next integer > 1 is 2, that's what you get!

Cheers, Ben

You are making the same mistake I did. The intent is to only print when millis() increments by 2 instead of 1, which happens occasionally because of the way the millis() function is written.

It does seem to work ok if you replace millis() with ( micros() / 1000 ), at least as far as not skipping a millisecond count. Still going to have a slight error in each millisecond not being exactly the correct length of time, and the timer will rollover every 70 minutes or so instead of every 49 days.

I do not currently have a working Arduino to test this, but here is a Nano sketch that disables the original timer0 overflow interrupt, and enables a new interrupt that fires at 250 timer clocks, instead of 256, which makes millis increment at exactly 1ms, with no double increment, at least for a 16MHz part. If anyone tries this, please let me know. It compiles ok, but I can't guarantee it will run. If it does, the D13 LED will flash on for 1/2 second every second.

/*
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;

#define 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++;

}

Hello Guys, Thank you very much for all your suggestions, erratas & idea inspirations. Because of my own expirence & time BG. I can't afford to dig into this topic very deep as some of you guys did. This problem is get solved through the optimization on other parts of my code. Now I can leave with the small offsets made by millis().

But thank you very much again. :)