Can I make delayMicroseconds more accurate?

As explained in the previous answers, your actual problem is not the accuracy of delayMicroseconds(), but rather the resolution of micros().

However, to answer your actual question, there is a more accurate alternative to delayMicroseconds(): the function _delay_us() from the AVR-libc is cycle-accurate and, for example

_delay_us(1.125);

does exactly what it says. The main caveat is that the argument has to be a compile-time constant. You have to #include <util/delay.h> in order to have access to this function.

Note also that you have to block the interrupts if you want any kind of accurate delay.

Edit: As an example, if I were to generate a 4 µs pulse on PD2 (pin 19 on the Mega), I would proceed as follows. First, notice that the following code

noInterrupts();
PORTD |= _BV(PD2);   // PD2 HIGH
PORTD &= ~_BV(PD2);  // PD2 LOW
interrupts();

makes a 0.125 µs long pulse (2 CPU cycles), because that's the time it takes to execute the instruction that sets the port LOW. Then, just add the missing time in a delay:

noInterrupts();
PORTD |= _BV(PD2);   // PD2 HIGH
_delay_us(3.875);
PORTD &= ~_BV(PD2);  // PD2 LOW
interrupts();

and you have a cycle-accurate pulse width. It is worth noting that this cannot be achieved with digitalWrite(), as a call to this function takes about 5 µs.


Your test results are misleading. delayMicroseconds() actually delays fairly precisely (for delays of more than 2 or 3 microseconds). You can examine its source code in file /usr/share/arduino/hardware/arduino/cores/arduino/wiring.c (on a Linux system; or on some similar path on other systems).

However, the resolution of micros() is four microseconds. (See, eg, the garretlab page about micros().) Hence, you will never see a reading between 4 microseconds and 8 microseconds. The actual delay might be just a few cycles over 4 microseconds, but your code will report it as 8.

Try doing 10 or 20 delayMicroseconds(4); calls in a row (by duplicating the code, not by using a loop) and then report the result of micros().


I'm checking to see how good the Arduino is at delaying... Seems to be pretty terrible at it.

micros() has a well-documented resolution of 4 µs.

You could improve the resolution by changing the prescaler for Timer 0 (of course that throws out the figures, but you can compensate for that).

Alternatively use Timer 1 or Timer 2 with a prescaler of 1, which gives you a resolution of 62.5 ns.


 Serial.begin(9600);

That's going to be slow anyway.


8 4 8 4 4 4 4 4 8 8 8 8 4 8 8 4 4 8 4 4 4 8 8 8 4 8 4

Your output is exactly consistent with the 4 µs resolution of micros() coupled with the fact that sometimes you would get two "ticks" and sometimes one, depending on exactly when you started the timing.


Your code is an interesting example of measurement error. delayMicroseconds(4); will delay for close to 4 µs. However your attempts to measure it are at fault.

Also, if an interrupt occurs then it will stretch the interval a bit. You need to turn interrupts off if you want an exact delay.