Go Down

Topic: found bug in delayMicroSeconds when called with 0; (Read 2 times) previous topic - next topic

robtillaart

Nov 18, 2012, 11:18 am Last Edit: Nov 18, 2012, 11:26 am by robtillaart Reason: 1
The delayMicroseconds function fails when called with a value of 0.
Although this is not done normally, it can happen when it is called with a variable which is the result of some expression/calculation.

The bug can be traced back to the if (--us ==0) statement, which causes an overflow when the param us is 0. The delay is then far longer (about 16384 micros) .

BUG seen in IDE 0.22 + 1.0.0 + 1.0.2

Find test program and patch below : NOTE the patch does not include the 1.0.2 20Mhz addition as I cannot test it .

The patched version of the delayMicroseconds() will return asap when called with 0 . This still takes about 1.2 uSec but that is way better than 16K uSec :)

Q: can anybody confirm this bug in a 20Mhz duino?

Program to show bug
Code: [Select]
//
//    FILE: delayMicrosecondsBUG.pde
//  AUTHOR: Rob Tillaart
//    DATE: 2012-11-18
//
// PUPROSE: test delayMicroseconds()
//

void setup()
{
 Serial.begin(9600);
 Serial.println("start...");
 
 unsigned long m = micros();
 for (uint8_t i=0; i<100; i++)
 {
   delayMicroseconds(0);
 }
 Serial.println(micros()- m);

 m = micros();
 for (uint8_t i=0; i<100; i++)
 {
   delayMicroseconds(1);
 }
 Serial.println(micros()- m);

 m = micros();
 for (uint8_t i=0; i<100; i++)
 {
   delayMicroseconds(2);
 }
 Serial.println(micros()- m);
 
 m = micros();
 delayMicroseconds(20);
 Serial.println(micros()- m);
 m = micros();
 delayMicroseconds(200);
 Serial.println(micros()- m);
 m = micros();
 delayMicroseconds(2000);
 Serial.println(micros()- m);
}

void loop()
{}


====================
Find below a patch for the function (for 0.22 - 1.0.0 version):

Code: [Select]
void delayMicroseconds(unsigned int us)
{
// calling avrlib's delay_us() function with low values (e.g. 1 or
// 2 microseconds) gives delays longer than desired.
//delay_us(us);

#if F_CPU >= 16000000L
// for the 16 MHz clock on most Arduino boards

// for a one-microsecond delay, simply return.  the overhead
// of the function call yields a delay of approximately 1 1/8 us.
//  PATCH
// if (--us == 0)
// return;
if (us < 2) return;
us--;

// the following loop takes a quarter of a microsecond (4 cycles)
// per iteration, so execute it four times for each microsecond of
// delay requested.
us <<= 2;

// account for the time taken in the preceeding commands.
us -= 2;
#else
// for the 8 MHz internal clock on the ATmega168

// for a one- or two-microsecond delay, simply return.  the overhead of
// the function calls takes more than two microseconds.  can't just
// subtract two, since us is unsigned; we'd overflow.
// PATCHED LINES
// if (--us == 0)
// return;
// if (--us == 0)
// return;
if (us < 3) return;
us -= 2;

// the following loop takes half of a microsecond (4 cycles)
// per iteration, so execute it twice for each microsecond of
// delay requested.
us <<= 1;
   
// partially compensate for the time taken by the preceeding commands.
// we can't subtract any more than this or we'd overflow w/ small delays.
us--;
#endif

// busy wait
__asm__ __volatile__ (
"1: sbiw %0,1" "\n\t" // 2 cycles
"brne 1b" : "=w" (us) : "0" (us) // 2 cycles
);
}


TODO: post on the issue list yet ...
Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

robtillaart

Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

robtillaart

a more generic patch might work for the 20Mhz too    (values smaller than 2 are exceeded by function call overhead)

Code: [Select]

void delayMicroseconds(unsigned int us)
{
  if (us <2) return;
...
Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

retrolefty



Reported as #1121 on  - https://github.com/arduino/Arduino/issues?state=open -


It will be interesting to see if the arduino developers will consider this a bug or a feature as they already note the minimum value on can use will accurate results. I the reference for delayMicroseconds:

Quote

Caveats and Known Issues
This function works very accurately in the range 3 microseconds and up. We cannot assure that delayMicroseconds will perform precisely for smaller delay-times.



Lefty

robtillaart

Hi Lefty,

Rationale for that statement is the fact that a function call takes approx 1 usec. Checking the param takes some clock cycles too. So before one can start to count down, too much time may have passed already (for small values). The root cause is that one is near the cpu speed.

But mapping 0 usec on 16K usec is a serious Bug - with capital B ;)

For the Due which is 84(?) Mhz I expect a re-implementation for this function. However no experience yet with the Due.
Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

Go Up