Pages: [1]   Go Down
Author Topic: BUG: delayMicroseconds(0) leads to a huge delay  (Read 2094 times)
0 Members and 1 Guest are viewing this topic.
Canada
Offline Offline
Newbie
*
Karma: 0
Posts: 3
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Passing zero to the delayMicroseconds function leads to a huge delay.  If the parameter is calculated inside of a user code, this zero can be unexpected outcome leading to the confusing huge delay.

In the implementation of the function, in the "wiring.c" file, the function parameter is pre-decremented, effectively switching this zero to a large value.

/* Delay for the given number of microseconds.  Assumes a 8 or 16 MHz clock. */
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.
   if (--us == 0)
      return;

   // 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.
   if (--us == 0)
      return;
   if (--us == 0)
      return;

   // 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
   );
}
 
« Last Edit: August 03, 2011, 04:27:20 pm by atetervak » Logged

Centurion RSA
Offline Offline
Full Member
***
Karma: 0
Posts: 166
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Why would one pass a zero to a delay function? Dangerous I would say.
Logged

Rather people think you to be a fool than you open your mouth and confirm it.

0
Offline Offline
Sr. Member
****
Karma: 0
Posts: 360
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

If the function parameter is being calculated as the output of integer division:

delayMicroseconds(1000/1500); would give a 0 value to the parameter unexpectedly.
Logged

Global Moderator
Boston area, metrowest
Offline Offline
Brattain Member
*****
Karma: 549
Posts: 27419
Author of "Arduino for Teens". Available for Design & Build services. Now with Unlimited Eagle board sizes!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

So do the calculation first & test it - if the result is less than 3 don't call the delay:
"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. "

Logged

Designing & building electrical circuits for over 25 years. Check out the ATMega1284P based Bobuino and other '328P & '1284P creations & offerings at  www.crossroadsfencing.com/BobuinoRev17.
Arduino for Teens available at Amazon.com.

Canada
Offline Offline
Newbie
*
Karma: 0
Posts: 3
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Why would one pass a zero to a delay function? Dangerous I would say.
Code:
float my_rate(float x){
    // something here
}
float my_scale = 1.5;

// something here

delayMicroseconds(int(my_scale*my_rate(something_calculated)));

Logged

Canada
Offline Offline
Newbie
*
Karma: 0
Posts: 3
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

So do the calculation first & test it - if the result is less than 3 don't call the delay:
"Caveats and Known Issues
This function works very accurately in the range 3 microseconds and up. We cannot assure that  will perform precisely for smaller delay-times. "
I do not accept your argument. It is well expected and understood that delayMicroseconds(0) won't give literally zero delay  smiley-lol. Nevertheless, it is natural to expect (from the website statement) that the actual delay would be below or somewhat close to delayMicroseconds(3), this would be fine. It is totally unexpected that delayMicroseconds(0) function call produces delayMicroseconds(UINT_MAX) delay.  I insist that it is a BUG.
« Last Edit: August 04, 2011, 09:50:20 am by atetervak » Logged

Global Moderator
UK
Offline Offline
Brattain Member
*****
Karma: 310
Posts: 26626
I don't think you connected the grounds, Dave.
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Code:
delayMicroseconds(int(my_scale*my_rate(something_calculated)));
I'd INSIST that that code doesn't compensate correctly for the amount of processing necessary to perform those float-point operations.     smiley-grin
Logged

"Pete, it's a fool looks for logic in the chambers of the human heart." Ulysses Everett McGill.
Do not send technical questions via personal messaging - they will be ignored.

Global Moderator
Netherlands
Offline Offline
Shannon Member
*****
Karma: 227
Posts: 14024
In theory there is no difference between theory and practice, however in practice there are many...
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset


A great deal of the arduino interface is to make it easy to write code: x = digitalRead(pin);     instead of low(er) level code.

People expect a certain behavior of these functions, and  atetervak  is expecting behavior of delayMicroseconds() which is in my opinion 100% reasonable. I also agree with his statement:
Quote
Nevertheless, it is natural to expect (from the website statement) that the actual delay would be below or somewhat close to delayMicroseconds(3), this would be fine.

On the other hand his example of the "calculated delay" is not strong, partly because it uses float math that is "slow" compared to micros (as AWOL states). But one could have a similar problem when using integer math, which is in the same order of speed.

But whether you call it a bug or not, it is unexpected behavior, that can easily be fixed by changing a few lines in the code of delayMicroseconds():

(original code)
Code:
if (--us == 0)
  return;

==>

(proposed code)
Code:
if (us < 2)
  return;
us--;

where the first would only capture us =  { 1 } the second captures both us = { 0,1 }

Not measured as I don't have an arduino at hand, but I expect the accuracy and speed will be similar.
Footprint is increased with 2 bytes (for 328, I do have a compiler smiley .

Can't report it as a bug/fix at the moment as the site - http://code.google.com/p/arduino/issues/list - is not available ...


Logged

Rob Tillaart

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

Global Moderator
Netherlands
Offline Offline
Shannon Member
*****
Karma: 227
Posts: 14024
In theory there is no difference between theory and practice, however in practice there are many...
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

addition

for the 8 Mhz version of delayMicrosecond()

Code:
if (us < 3)
  return;
us -=2;
Logged

Rob Tillaart

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

Global Moderator
Netherlands
Offline Offline
Shannon Member
*****
Karma: 227
Posts: 14024
In theory there is no difference between theory and practice, however in practice there are many...
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset


Problem reported as an issue  - http://code.google.com/p/arduino/issues/detail?id=576
Logged

Rob Tillaart

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

Pages: [1]   Go Up
Jump to: