1

I'm generating a square wave with my Arduino UNO using the following:

#define pin 10 // data pin to control servo

// pulse width (in microseconds)
int pulsewidth = 2500; // 400 Hz

// wave period in microseconds
int period = 20000; // 50 Hz

void setup() {
  pinMode(pin, OUTPUT);
}

void loop() {

  // turn pulse on for pulsewidth duration
  digitalWrite(pin, HIGH);
  delayMicroseconds(pulsewidth);

  // turn pulse off
  digitalWrite(pin, LOW);
  delayMicroseconds(period - pulsewidth);

}

When I check the wave freq on the scope it's reading something like 115Hz instead of 50Hz; however, the pulsewidth is correct (4Hz).

Why is this exactly? Is it because digitalWrite() is slow?

2
  • If you want precise timing use the PWM pins. E.g. Using the Timer1 library
    – Gerben
    Commented Dec 4, 2016 at 19:31
  • Your comment on the pulse width line is not correct. 2500 usec would be 400 Hz if it were a period. Commented Dec 4, 2016 at 23:18

2 Answers 2

1

Why is this exactly? Is it because digitalWrite() is slow?

There is "hidden" code in the main() function which is calling loop(). Also digitalWrite() is slow compared to what is possible. Last, the millis() ISR is executing in the background.

Try the below:

void loop() {

  while (1) {
    // turn pulse on for pulsewidth duration
    digitalWrite(pin, HIGH);
    delayMicroseconds(pulsewidth);

    // turn pulse off
    digitalWrite(pin, LOW);
    delayMicroseconds(period - pulsewidth);
  }
}

And then try turning off interrupts.

Cheers!

4

From Arduino reference on delayMicroseonds:

Currently, the largest value that will produce an accurate delay is 16383. This could change in future Arduino releases. For delays longer than a few thousand microseconds, you should use delay() instead.

Although the reference does not specify further what happens if delay > 16383, a look at the actual code explains this (in wiring.c):

/* Delay for the given number of microseconds.  Assumes a 1, 8, 12, 16, 20 or 24 MHz clock. */
void delayMicroseconds(unsigned int us)
{
...
#elif 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 takes 14 (16) cycles, which is 1us
    if (us <= 1) return; //  = 3 cycles, (4 when true)

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

    // account for the time taken in the preceeding commands.
    // we just burned 19 (21) cycles above, remove 5, (5*4=20)
    // us is at least 8 so we can substract 5
    us -= 5; // = 2 cycles,
...

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

The line us <<= 2; explains why us should not exceed 16383, otherwise the left-shifted value will overflow (unsigned int is 16 bits on AVR).

In your situation, (20000-2500) << 2 will not be 70000 or 0x11170 in hexa (that does not fit into unsigned int), hence the result will be truncated to 0x1170 i.e. 4464, which will make the last part of the function (the busy wait) wait for the wrong duration.

In order to fix it, you have to split your second delayMicroseconds in one delay in milliseconds plus one delayMicroseconds:

delay(17);
delayMicroseconds(500);

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.