The Arduino core uses hardware Timer0 with its ISR (Interrupt Service Routine) for timekeeping. While delayMicroseconds() directly uses the value of the hardware timer, delay() and millis() are handled by the ISR. It will be called regularly.
If the ISR is getting executed during your measurement, then the execution time of the ISR will add to the execution time of your measured code. Thus you get a higher count occasionally.
To prevent this you can either deactivate all interrupts (via noInterrupts()) or deactivate this specific interrupt, either by stopping Timer0:
TCCR0B &= ~( (1 << CS02) | (1 << CS01) | (1 << CS00) );
or by letting the timer run, but deactivating its interrupts:
TIMSK0 = 0;
For details about this have a look at the datasheet of the Atmega328p, which is the microcontroller on the Arduino Uno.
Answer from chrisl on Stack ExchangeRepeating delayMicroseconds(max_value) to get a longer delay?
arduino mega - Can I make delayMicroseconds more accurate? - Arduino Stack Exchange
EEVblog Captcha
Delay microseconds wit Atmega328P barebones working at 1MHz 1.8V
Can I pass a variable to delayMicroseconds()?
delayMicroseconds(pulseWidth). Make sure the variable is unsigned int or a value that fits within the valid range.What is the difference between delay() and delayMicroseconds()?
delay() takes milliseconds. delayMicroseconds() takes microseconds. 1 ms = 1,000 µs. Use delayMicroseconds() when you need pauses shorter than 1 ms.Why does delayMicroseconds(1) not actually delay 1 µs?
Hello!
Noob to Arduino and C here.
I'm working with a stepper motor to control the Right Ascension axis on my telescope.
I've found that between two steps, I need a delay of 25.882 milliseconds. Now, delay() only takes integers, but I need a higher resolution. Looking at delayMicroseconds(), it states that for this delay, a maximum of 16383 microseconds is possible for accurate timing. Obviously, I need 25882 microseconds, which is above that limit.
What I'm wondering, is: Can I do two consecutive delayMicroseconds() with half my target delay? This means that I'll do
delayMicroseconds(target_half) // target_half = 12941 < 16383 delayMicroseconds(target_half)
Here's a code exerpt:
float millisbetweenSteps = 25.882; // milliseconds - calculated - lower is faster
int microsbetweenSteps = millisbetweenSteps * 1000; // microseconds
...
void runStepper(boolean run) {
if (run == true) {
digitalWrite(enablePin, HIGH);
digitalWrite(directionPin, LOW);
digitalWrite(stepPin, HIGH);
delayMicroseconds(pulseWidthMicros); // needed?
digitalWrite(stepPin, LOW);
delayMicroseconds(microsbetweenSteps/2); //Because delayMicroseconds max value is 16383micros
digitalWrite(ledPin, !digitalRead(ledPin));
delayMicroseconds(microsbetweenSteps/2);
}
}
void loop() {
runStepper(true);
}Looking at the stepper, it looks as if I'm getting the desired result, but I'm not sure.
Cheers!
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().
This a very partial answer. I do not know your requirements, whether or not PWM can do the job, etc... I just wanted to point out that you can make a pulse exactly 8 µs long with this:
#include <util/delay.h>
#include <util/atomic.h>
// Pulse PD3 (digital 18 on Mega 2560)
// for exactly 8 us (128 clock cycles @ 16 MHz)
static void pulse() {
ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
PORTD |= 1 << 3;
_delay_us(8 - 0.125);
PORTD &= ~(1 << 3);
}
}
The _delay_us() function from avr-libc is actually cycle-accurate
inline assembly. The 0.125 µs I subtracted is the time needed by
the actual port writes: both the sbi and cbi instructions take two cycles
(0.125 µs), but that delay should only be counted once.
digitalWrite() should always be avoided in time-critical code.
The 242 µs delay between pulses would be harder to generate without PWM. A timer interrupt at 4 kHz seems like the simplest option, but it is not cycle-accurate.
Edit: here is the scope trace, showing a quite accurate timing:

Unless you want program PWM timer as suggested in comment then for short delays you need calculate how many instruction cycles are needed to make such a delay based on clock/crystal frequency and make that delay in inline assembler to have code under control. To manipulate digital output use writing data to PINx (which is equal to xoring PORTx).
To satisfy exact 250ms you need compensate errors because of rounding. Arduino's delay/tick functions do it. For the long delays you need a simple scheduler not to spend all the CPU time for pulse generating.