Due delay() and micros()

Hi

I ran a test code on Arduino Due to test the micros() and Millis() timing accuracy.

See the sample code below:

unsigned long t;

void setup() {
Serial.begin(9600);
for (uint8_t i = 0; i < 10; i++) {
t = micros();
delay(1);
t = micros() - t;
Serial.print("1ms in micros="); Serial.println(t);
}
}

void loop() {
// Measure x ms delay in micros
for (uint8_t i = 0; i < 10; i++) {
t = micros();
delay(15);
t = micros() - t;
Serial.print("15ms in micros="); Serial.println(t);
}
for (uint8_t i = 0; i < 10; i++) {
t = micros();
delay(5);
t = micros() - t;
Serial.print("5ms in micros="); Serial.println(t);
}
for (uint8_t i = 0; i < 10; i++) {
t = micros();
delay(1);
t = micros() - t;
Serial.print("1ms in micros="); Serial.println(t);
}

Code is basically counting micros() while delaying in millis.

Result I got:

// Setup test
1ms in micros=932
1ms in micros=963
1ms in micros=964
1ms in micros=963
1ms in micros=963
1ms in micros=964
1ms in micros=949
1ms in micros=189
1ms in micros=429
1ms in micros=669
// Main loop test
15ms in micros=14190
15ms in micros=14309
15ms in micros=14429
15ms in micros=14549
15ms in micros=14670
15ms in micros=14790
15ms in micros=14910
15ms in micros=14029
15ms in micros=14149
15ms in micros=14269
5ms in micros=4389
5ms in micros=4589
5ms in micros=4789
5ms in micros=4989
5ms in micros=4189
5ms in micros=4389
5ms in micros=4589
5ms in micros=4789
5ms in micros=4989
5ms in micros=4189
1ms in micros=389
1ms in micros=629
1ms in micros=869
1ms in micros=109
1ms in micros=349
1ms in micros=589
1ms in micros=829
1ms in micros=69
1ms in micros=349
1ms in micros=589

The above is one iteration tried several saved in the attached XLS file.

Can you explain the reason for the inaccuracy of the count?

Does the delay() is good for +- 0.5ms? OR the main problem is the micros() timer?

My project requires accurate timing generation for millis under ISR and meanwhile poll an spi device in 15Khz accurately. Any suggestions?

Thanks in advance

DUE_test.ino (3.66 KB)

Due_micros.zip (9.91 KB)

Remember that Arduino is a hobby platform intended to be (able to be) self-built. The component specifications tend to lead to the least expensive options. If I'm not mistaken, the spec on the crystal/oscillator is +/- 2%. The Arduino timing functions are based on a definition of F_CPU and in the case of the Due this would be #define F_CPU 84000000UL.

The onus is on the developer to check how well selected components meet specs and make adjustments as necessary. Testing on my UNO showed the F_CPU to be 15,880,000 and once I made the change, everything else fell into place. When you do make the change to F_CPU you'll get a compiler warning about the re-definiton which you can ignore or trace back to the library and comment it out.

Another approach which could be applied more generically, is to have a function, called during setup, that did your 10 or 50 iterations and averaged and adjustment factor to compensate for the oscillator freq. This function could also be called periodically to keep the timing tuned as the oscillator freq will vary according to temperature, different at night than during the day.

OdedA:
Can you explain the reason for the inaccuracy of the count?

Well, for starters, it would help if you didn't chew up at least 12 milliseconds each time you print a result. I suggest that you first collect the results in an array and then, at the end, print them all.

Weird results observed with your code may come from Serial.print because Serial uses interrupts and because of the way an ARM chip reads and process instructions.

Whenever you need a very precise timing in a loop with this uc, you can use a timer handler.

An example sketch to toggle an LED every 1 Hz:

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
  tc_setup();
}

void loop() {
}

void tc_setup() {

  PMC->PMC_PCER0 |= PMC_PCER0_PID29;                       // TC2 power ON : Timer Counter 0 channel 2 IS TC2

  TC0->TC_CHANNEL[2].TC_CMR = TC_CMR_TCCLKS_TIMER_CLOCK1   // MCK/2, clk on rising edge
                              | TC_CMR_WAVE                // Waveform mode
                              | TC_CMR_WAVSEL_UP_RC;       // UP mode with automatic trigger on RC Compare

  TC0->TC_CHANNEL[2].TC_RC = 2100;                         // Frequency = (Mck/2)/TC_RC  Hz = 20 KHz

  TC0->TC_CHANNEL[2].TC_IER = TC_IER_CPCS;                 // Interrupt on RC compare match
  NVIC_EnableIRQ(TC2_IRQn);                                // Enable interrupts

  TC0->TC_CHANNEL[2].TC_CCR = TC_CCR_SWTRG | TC_CCR_CLKEN; // Software trigger TC2 counter and enable
}

void TC2_Handler() {

  static uint32_t Count;

  TC0->TC_CHANNEL[2].TC_SR;         // Read and clear status register;

  if (Count++ == 20000) {
    PIOB->PIO_ODSR ^= PIO_ODSR_P27; // Toggle LED_BUILTIN every 1 Hz
    Count = 0;
  }
  // Some stuff, as short as possible
}

Hello OdedA

I have tried your code on arduino UNO.
The error is 16µs max, most of the time 8µs (here with UNO).

I'am very surprised by this so huge inaccuracy you got,like (as you wrote) :

// Setup test

1ms in micros=189
1ms in micros=429

// Main loop test

15ms in micros=14029

5ms in micros=4189

1ms in micros=389
1ms in micros=629
1ms in micros=869
1ms in micros=109

1ms in micros=69

Are you sure of your power supply ?

Regards,
bidouilleelec

Hello

May be this :

could be usefull for you (SysTick timer)??

Regards,
bidouilleelec

bidouilleelec:
I have tried your code on arduino UNO.
The error is 16µs max, most of the time 8µs.

I'am very surprised by this so huge inaccuracy.

If you would read the documentation on micros, it wouldn't surprise you nearly as much. Remember, micros on an UNO only has a 4us resolution. So you are talking about one two ticks of the clock. That could be the result of how your timing code works. The print statements are eating that up.

I can't speak to the results on the Due as I haven't played with that one much.

Hello Delta_G

Delta_G:
If you would read the documentation on micros, it wouldn't surprise you nearly as much. Remember, micros on an UNO only has a 4us resolution. So you are talking about one two ticks of the clock. That could be the result of how your timing code works. The print statements are eating that up.

Yes : I'm aware of that.
I'm surprised by so much inaccuracy in Oded_A's exemple.

Regards,
bidouilleelec

Ok. I thought you meant on the UNO code. Carrion.

Delta_G:
Ok. I thought you meant on the UNO code. Carrion.

Excuse me.
My English is not so fluent !

Regards,
bidouilleelec

bidouilleelec:
Excuse me.
My English is not so fluent !

In that case I'll clarify. That last word was a word play on the words "carry on". It sounds the same but means something very different. Was used here to mean, "Carry on" as in "forget I said anything"

Delta_G:
In that case I'll clarify. That last word was a word play on the words "carry on". It sounds the same but means something very different. Was used here to mean, "Carry on" as in "forget I said anything"

In this case , that was clear for me.

Best Regards,
bidouilleelec

If I'm not mistaken, the spec on the crystal/oscillator is +/- 2%.

It shouldn't matter in this case - since both micros() and millis() run from the same clock signal, they should be consistent. millis() is pretty straightforward - I'm betting on a bug in micros() ...

Actually, this makes sense.
delay(n) waits until the millisecond counter has increased by n ticks. For a completely random distribution, it will tend to be about 500us short of n milliseconds, because part of the current millisecond will already have gone by. Regular patterns of activity will have more regular patterns of error, subject to occasional larger errors due to interrupts.

I believe that this was "fixed" in the AVR cores quite some time ago, by having delay() use micros() instead of the millisecond tick count, but I guess the change didn't make it into Due (hmm. Or Zero, either...)
(although, the SAM/SAMD chips count exact milliseconds, so they don't have the "double increment of millis()" that plagues the AVR.)

delay() is inaccurate. [imported] · Issue #237 · arduino/Arduino · GitHub (hey! I submitted that bug; you'd think I would have remembered it sooner!)

Hi guys

Thanks a lot for the support

Will try it as soon as I am available and update.

Thanks a lot