delayMicroseconds(0)

delayMicroseconds(0) appears to malfunction and return (delay really) a much larger value, instead of returning asap.

I discovered this experimenting with a sound synthesis program with a variable for the delayMicroseconds param.

Maybe this should be in the docs?

I'm pretty sure we don't want to fuss with the function or put a conditional statement in it, and change all the timing.

You can hear this if you set up a speaker or headphone connected to ground on one side and two 5k resistors on the other wire, the other ends of the resistors go to pins 3 & 4 .

void loop() // delayMicroseconds bug?
{

for (int x = 1; x < 20; x++){
pinMode(3, OUTPUT);
pinMode(4, OUTPUT);
digitalWrite(3, HIGH);
delayMicroseconds(1); // change this to 0 and listen
digitalWrite(4, HIGH);
delayMicroseconds(1000);
digitalWrite(3, LOW);
delayMicroseconds(1); // change this to 0 and listen
digitalWrite(4, LOW);
delayMicroseconds(1000);
pinMode(4, INPUT);
pinMode(3, INPUT);
}
delay(100);

}

OK hooking up a speaker is way too much work.

Here's the code

/* Test delayMicroseconds() with param of zero
*/

int tdelay;
unsigned long i, hz;
unsigned long time, timeForOnes, timeForZeros;
int outPin = 11;

void setup(){
pinMode(outPin, OUTPUT);
Serial.begin(9600);
Serial.println("start");
}

void loop() {
time = millis(); // get start time of loop
for (i = 0; i < 4000; i++){ // time 100,000 cycles through the loop
delayMicroseconds(1);
}
timeForOnes = millis() - time; // compute time through inner loop in milliseconds

time = millis(); // get start time of loop
for (i = 0; i < 4000; i++){ // time 100,000 cycles through the loop
delayMicroseconds(0);
}
timeForZeros = millis() - time; // compute time through inner loop in milliseconds

Serial.print("ones = ");
Serial.print(timeForOnes, DEC);
Serial.print(" zeros = ");
Serial.println(timeForZeros, DEC);

}

Here's the result:

ones = 11 zeros = 4118

ones = 11 zeros = 4120

ones = 13 zeros = 4119

DelayMicroseconds(0) appears to take about 1026 us.

not sure if it matters, but are you running this on an atmega8 or 168 ?

your test output might be wrong though. this is the function definition of delayMicroseconds takne from libs/targets/arduino/wiring.c:

/* Delay for the given number of microseconds.  Assumes a 16 MHz clock. 
 * Disables interrupts, which will disrupt the millis() function if used
 * too frequently. */
void delayMicroseconds(unsigned int us)
{
      uint8_t oldSREG;

      // calling avrlib's delay_us() function with low values (e.g. 1 or
      // 2 microseconds) gives delays longer than desired.
      //delay_us(us);

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

      // disable interrupts, otherwise the timer 0 overflow interrupt that
      // tracks milliseconds will make us delay longer than we want.
      oldSREG = SREG;
      cli();

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

      // reenable interrupts.
      SREG = oldSREG;
}

i did not get into it too deep. but the lines
if (--us == 0)
return;

(BTW: what are the two minus doing there?)
look as if they'd serve a close to zero delay. on the other hand, your real short delays (1µs) might disable interrupts to often which affects the millis() function. see the note at the beginning.

still it would be cool to get a real answer to your question.

delayMicroseconds(0) does in fact, wrap around and give you a long delay, and, yes, we might want to mention that in the docs. Feel free to include it, Paul.

hi mellis,
why is that? i'd really like to see where this is happening. could you briefly explain the function to help me get it?

Sure.

if (--us == 0)
            return;

Is kind of tricky. --us decrements us by 1, then returns the new value. So this will only return if us was 1. If us was 0, it will wrap around to a large positive number (since it's unsigned), meaning that it will take a long time for it to get back to 0.

yeah :slight_smile:

thank you.

Apparently --us has the same result as us--, but when it is used in a comparison, the -- before us means that C will decrement the value FIRST, before the comparison, while -- after us means that C will decrement the value AFTER the comparison. If you use it us--; or --us; alone, both give the same result. You may say why the creators of C placed such ambiguous operations in the language. Well, once you learn it deeply, it is a language that allows you to write a very compact and intelligently optimized code. 20 years ago, there used to be C programming contests, where you could do very very complex things in just one line of code. Of course the resulting code was write only, and not meant to be read by mere mortals.

I consider wiring implementation of delayMicroseconds() a bug... the programmer should have used

if (us-- <= 1)
return;

Probably he/she assumed that nobody wants to invoke a delay routine to wait 0 units of time... but it was a wrong assumption after all.

So why don't we fix this ___ instead of documenting it?

As I see it, it would require only one more comparison, which would execute only in the case of 0. And the code should function the same way it does now.

Am I missing something?

PB

It should probably be fixed, but I was reluctant to change the function (and possibly its timing) and introduce slight incompatibilities for timing sensitive code. jims has done some nice work on redoing the whole timing system to make it much more precise and flexible; I'm hoping to incorporate his work soon. It seemed better to wait until then to change things.

So why don't we fix this ___ instead of documenting it?

As I see it, it would require only one more comparison, which would execute only in the case of 0. And the code should function the same way it does now.

Am I missing something?

PB

Using the code I posted before you don't need one extra comparison.

Postdecrement (value--) returns the value of the variable before the operation, and thus requires a temporary value to be created to hold that different value.

think of the operation as a function:

int postdec(int& val)
{
  int temp = val;
  val = val -1;
  return temp;
}

Predecrement doesn't require the use of a compiler generated temporary and is therefore quicker, less resource hungry.

int predecrement(int& val)
{
  val = val - 1;
  return val;
}

If there was no overhead to calling a function then delay(0) might make some sense. As it is, the function would have to execute in negative time in order to return at precisely the same moment it was called :slight_smile:

I think the designers got it right. Delay(0) is pointless, and coding for it would only waste space.

Just my £0.01.

Charlie.

i guess hard-coding a zero doesn't make sense here. still there's a big chance to invoke delayMicroseconds(0) if the parameter is dynamic, like "howManyButtonsPressed" to make it simple.

Then that's where the check needs to be - not in the library!
:smiley:

i agree, now that someone found out we should keep this in mind, i just meant to underline that.
peace,kuk

"Then that's where the check needs to be - not in the library!"

Except that the language is supposed to be easy to use for beginners. The function dumbs down the resolution to 1 us from .25 us (ostensibly to make it easier to use) so what the heck is one more check. (.25 us?).

Anyone using the language could easily expect delayMicroseconds(0) to generate as-close-to-zero-as-possible a delay not 1023 us.

BTW - negative numbers are also going to generate long delays I think.
Checking for errors in this function makes for one more place the code can't screw up. If you want to argue for efficient machine code fine - but you've got to explain all the other inefficiencies, of which the 1 us resolution is only one. And also the stated philosohpy of making something for beginner coders.

It's been traumatic for certain parties to even see documentation of C and AVR arcana in the reference section.

I repeat - fix this ______ instead of documenting it.

if (--us < 2)
return;

Doesn't that fix it? Predecrement and one comparison.
You've got your predecrement to bring to the party!

Would making the parameter a signed long also fix negative numbers this way?
How does that playout with the right bitshift 2 dumbdown (er ease of use feature)

Anyway the rumor is that we're due to get a whole new timing system for Arduino 0011. Is this still on track?

Yours in tightly balanced tradeoffs,

Paul

Postscript:

I did some testing of the code change and (surprisingly) it seems to make no difference in timing.

Here's the relevant code in delayMicroseconds function

// code A
// if (--us == 0) // decrement to offset function overhead
// return;

Here's the code fix

// code B
if ((int)us < 2 ) // cast cures negative number rollover
return;
us--; // decrement to offset function overhead

code A is the original and code B is the fix.

I can detect absolutely no difference in the testing I did except code B fixes the issues.

Here's the test setup (I can see your eyes glazing over - snap out of it!)

/* Test delayMicroseconds() with param of zero
*/

int tdelay;
unsigned long i, hz;
unsigned long start, TotalLoopTime;
int outPin = 11;

void setup(){
pinMode(outPin, OUTPUT);
Serial.begin(9600);
Serial.println("start");
}

void loop() {
start = millis(); // get start time of loop
for (i = 0; i < 1000000; i++){ // a million times through the loop
delayMicroseconds(2);
}
TotalLoopTime = millis() - start; // compute time through inner loop in milliseconds

Serial.print("time = ");
Serial.println(TotalLoopTime, DEC);

}

Here's the results

value of param 0 1 2 4


Code A rolls over 3011 3953 5959 results in milliseconds

Code B 3011 3011 3953 5959

All of the numbers jitter randomly by one or two, presumably because of interrupt firings or serial.print or some other unknown (to me) phenomenon.

Please replicate my results on this if you care, then can we finally fix the code and move on?

A more patient math-head than me might also want to see if the function is as advertised, with about 1 us going into the function overhead.

There is another decrement farther down in the code that could be combined with the decrement to tweak timing. Should anyone care to get into it. I imagine it's only going to be possible to confirm this through some patient work with an oscilloscope or through sorting through the machine code and counting instructions, though. I'm not up for either at this point.

Yours in easy-to-use microcontroller code,

Paul Badger

I'm writing to see if I can get some of the amazing coders who are currently reading this list to test this proposed fix and confirm my results. It's possible that the fix works differently now since it was last tested with Arduino 10/11.

According to my results this fix is FREE and works EXACTLY LIKE THE ORIGINAL function except without the nasty surprise when using zero or neg numbers for a parameter.

Perhaps someone could decompile and look at the machine code to confirm that the two are equivalent.

If this is the case, then the reason for leaving in this irritating bug in a language / project whose stated goals is to make programming easy for beginners is perplexing. Or at least it tends to make one wonder if there are other reasons....

PaulB