Perhaps the code in context woukd afford some clues. The snippet you listed looks like it should be 200 us, so it isn't clear and I'm too lazy to think about what you are doing there.
Write a minimum it compiles we can try it ourselves sketch and post it next on this thread.
Did you know that we say on this from: The problem is in the part that you are not showing.
There is even a website for that: https://snippets-r-us.com/
A sketch to try the other delays as well:
// Test delays with delays being delays or something like that.
// 30 August 2023, by Koepel, Public Domain.
// Forum: https://forum.arduino.cc/t/delaymicroseconds-is-off-by-an-order-of-magnitude/1163575
// This Wokwi sketch: https://wokwi.com/projects/374490739256465409
//
const int pulsePin = 13;
void setup()
{
pinMode(pulsePin, OUTPUT);
}
void loop()
{
// -------------------------------------------------
// Exactly 100 µs by using the clock cycles
// Result: always 100.000000 µs
// -------------------------------------------------
noInterrupts();
bitSet(PORTB, 7);
__builtin_avr_delay_cycles(1600-2); // minus 2 for setting the pin
bitClear(PORTB, 7);
interrupts();
delay(1);
// -------------------------------------------------
// The common AVR delay
// Result: 105.313 µs
// -------------------------------------------------
noInterrupts();
digitalWrite(pulsePin, HIGH);
_delay_us(100);
digitalWrite(pulsePin, LOW);
interrupts();
delay(1);
// -------------------------------------------------
// The Arduino delayMicroseconds
// Result: 104.062 µs
// -------------------------------------------------
noInterrupts();
digitalWrite(pulsePin, HIGH);
delayMicroseconds(100);
digitalWrite(pulsePin, LOW);
interrupts();
delay(1);
delay(10); // extra delay to easily identify the three pulses
}
It results in pulses of 100, 105.313 and 104.062 µs. The pulse is never longer, never shorter, there is never any deviation.
Timer0 runs the interrupt for millis(). You can keep the interrupts enabled if you want to add that now and then to the pulse, but only if you have a very good reason to include that in the pulse.
Run it in the Wokwi simulation:
Using the Wokwi simulation with the simulated Logic Analyzer: Install PulseView on your computer (no need to install sigrok or Zadig, only PulseView). Read how to use it with Wokwi. Start the simulation, stop it as you see it running. Open the file in PulseView via "Import Value Change Dump data". Add the "Timing" decoder. Have fun
Every program instruction-step takes some time and one C++ statement may result in more than one machine code instruction. ...Even with no "delay" there will be a delay.
Interesting. There does seem to be an issue with delayMicroseconds() assuming you haven't done anything else in you sketch that mucks with the timer h/w. (i.e. it would helpful to see the full sketch)
Also, do you have the proper F_CPU clock value? i.e. is your clock frequency on your board running at the same speed as the F_CPU define?
I'm traveling but I'll have to try that when I get back home.
As far as variation goes, that can happen from the Arduino core code using the timer interrupt to maintain its millis clock.
As far as the extra 4us that is due to the crappy code in the digital code in the Arduino AVR core library. The way it is implemented has quite a bit of overhead and the typical amount of time on an AVR is about 4us to set/clear a pin using digitalWrite().
// Somewhere before in the code
pinMode(resetPin, OUTPUT);
// other code...
digitalWrite(resetPin, LOW);
delayMicroseconds(100);
digitalWrite(resetPin, HIGH);
and the resetPin line is on HIGH by default because of a pull up, the code before "extends" my pulse window, since the pinMode call pulls my line to LOW, thus extending the interval.
Explicitly setting it to HIGH beforehand like this solves it.