Pages: [1] 2   Go Down
Author Topic: delayMicroseconds(0)  (Read 2113 times)
0 Members and 1 Guest are viewing this topic.
0
Offline Offline
Sr. Member
****
Karma: 0
Posts: 267
dinosaur cork
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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

0
Offline Offline
Sr. Member
****
Karma: 0
Posts: 267
dinosaur cork
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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.

Logged

berlin
Offline Offline
Sr. Member
****
Karma: 0
Posts: 293
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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

Logged

Forum Administrator
Cambridge, MA
Offline Offline
Faraday Member
*****
Karma: 11
Posts: 3538
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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

berlin
Offline Offline
Sr. Member
****
Karma: 0
Posts: 293
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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?

Logged

Forum Administrator
Cambridge, MA
Offline Offline
Faraday Member
*****
Karma: 11
Posts: 3538
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Sure.

Code:
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.
Logged

berlin
Offline Offline
Sr. Member
****
Karma: 0
Posts: 293
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

yeah  smiley

thank you.
Logged

USA
Offline Offline
Sr. Member
****
Karma: 0
Posts: 452
Freeduino rocks
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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:
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.
« Last Edit: October 12, 2007, 08:26:00 pm by nkcelectronics » Logged

0
Offline Offline
Sr. Member
****
Karma: 0
Posts: 267
dinosaur cork
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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
« Last Edit: October 12, 2007, 09:03:36 pm by paulb » Logged

Forum Administrator
Cambridge, MA
Offline Offline
Faraday Member
*****
Karma: 11
Posts: 3538
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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

USA
Offline Offline
Sr. Member
****
Karma: 0
Posts: 452
Freeduino rocks
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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

0
Offline Offline
Jr. Member
**
Karma: 1
Posts: 79
Combat, please!
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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

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

Just my £0.01.

Charlie.
Logged

berlin
Offline Offline
Sr. Member
****
Karma: 0
Posts: 293
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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

0
Offline Offline
Jr. Member
**
Karma: 1
Posts: 79
Combat, please!
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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

berlin
Offline Offline
Sr. Member
****
Karma: 0
Posts: 293
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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

Pages: [1] 2   Go Up
Jump to: