Go Down

Topic: delayMicroseconds(0) (Read 2663 times) previous topic - next topic

paulb

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

}

paulb

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.


kuuk

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:

Code: [Select]

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


mellis

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.

kuuk

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?


mellis

Sure.

Code: [Select]
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.

kuuk


nkcelectronics

#7
Oct 13, 2007, 03:24 am Last Edit: Oct 13, 2007, 03:26 am by nkcelectronics Reason: 1
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
Code: [Select]
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.

paulb

#8
Oct 13, 2007, 04:02 am Last Edit: Oct 13, 2007, 04:03 am by paulb Reason: 1
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

mellis

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.

nkcelectronics

Quote
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.

sirmorris

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:

Code: [Select]

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.

Code: [Select]

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 :)

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

Just my £0.01.

Charlie.

kuuk

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.

sirmorris

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

kuuk

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

Go Up