delayMicroseconds function provides unstable pulse durations

As a part of my project I need to generate 10 microseconds pulses with 10 milliseconds intervals. I was using a separate Arduino Micro and a simplest code with delayMicroseconds() and delay() functions.
To get the 10us pulse width I needed to use 8us argument for delayMicroseconds function, but the actual problem is that randomly but pretty often (roughly 10% of the time) the pulse width jumps to 18us which is completely unacceptable for my project. I tried to use a little faster Nano Every board but virtually with the same result just with a bit less occurrences of 18us pulses (3~5%). When I tried to play with longer pulses like 20us and 30us I was having similar instabilities with random 28us and 38us pulses. I’ve read somewhere that delayMicroseconds function is stable for arguments 3us and greater. Should I try to use even faster board like Nano 33 IoT or there is a more intelligent way to force Micro or Every to do the proper job?

You could turn off interrupts in the code you didn’t post, just while you’re sending your pulses.

There is no interrupts in the code for this board. The only code is permanent "Blink". This ARDUINO board is auxiliary for my project. Its only purpose is to generate pulses! You can try it by connecting digital output pin to the scope and watch pulses! I tried few boards with the same results.

Interrupts are how the micros() and millis() time is kept, the interrupts run in the background.

You can try it this way to reduce the background code jitter

void setup(){
pinMode (8, OUTPUT);
}
void loop(){
while (1){ // this skips the loop() processing in the background
PORTB = 0b00000001; // write D8 (on Uno) to a 1
delayMicroseconds (8); 8us delay, I believe it needs to be multiples of 4 on an Uno
PORTB = 0b00000000; // D8 to a 0
delay (10); // 10 ms delay
} // end while()
} // end loop()

If all you need is a rectangular pulse train, Google the ‘Timer1 library’.

Thank you,CrossRoads and larryd for your prompt response!

Crossroads: I just tried your code on Uno and I’ve got a similar instability. To get a 10us pulse I needed to set delayMicroseconds to 11, and rogue pulses are 6us longer.

larryd: I will try Timer1 library later today.

Although I cannot help you solve your problem, I think it is described in

https://www.youtube.com/watch?v=648Tx5N9Zoc

good luck

Thank you, GRuser! It is EXACTLY my problem!

larryd: If all you need is a rectangular pulse train, Google the ‘Timer1 library’.

Or, read the ATmega328P's datasheet and learn how to control the timers yourself.

gfvalvo: Thank you for the most generous and comprehensive advice!

Possibly of use: precise one shot on AVR8

For a short duration, precision pulse width requirement, I'd consider using a one-shot like the LTC6993-1:

https://www.analog.com/media/en/technical-documentation/data-sheets/LTC6993-6993-1-6993-2-6993-3-6993-4.pdf

and use a pin on the Arduino to trigger it with an edge.

#include <TimerOne.h>

const int outPin = 9; // the physical pin,  9 and 10 are valid options


//************************************************************************************************
void setup()
{
  Serial.begin(9600);
  pinMode(outPin, OUTPUT);

  Timer1.initialize(10000); //initialize timer1, 10000 microseconds = 10ms >>>>----> 100Hz 
  Timer1.pwm(outPin, 1);    //1=9us, 2=19us, 3=29us, 4=39us, 5=48us   . . . . . 
}

//************************************************************************************************
void loop()
{

  //your loop() stuff

}

//************************************************************************************************

Thank you, larryd for the code! I looks it's not gonna work for me though. I need this ~10us pulses to calibrate the main part of my project. It is like pass/no pass situation. I need 10us pulse for "no pass" and ~9us pulse for "pass". But thank you very much again for your time and consideration! I may need a timer1 library in a future!

Thank you Blackfin and dougp! I looks like both approaches will work for me! Not sure yet which one to implement!

Let’s see… Using a prescale of 8 you get a counter speed of 2 MHz. Set a PWM interval of 20000 (Set ‘TOP’ to 19999) to get your 10 millisecond interval. Set OCR1A to 19 to get a 10 microsecond pulse every 10 milliseconds and set OCR1B to 17 to get a 9 microsecond pulse every 10 milliseconds.

void setup()
{
  TCCR1A = 0;
  TCCR1B = 0;


  // WGM 14: FastPWM, TOP in ICR1
  // Note that two of the WGM bits are in TCCR1A and two are in TCCR1B
  TCCR1A |= (1 << WGM11);
  TCCR1B |= (1 << WGM12);
  TCCR1B |= (1 << WGM13);
  ICR1 = 19999; // Set TOP to 20,000 counts (10 milliseconds)

  // Turn on the PWM output pins in 'non-inverted mode
  TCCR1A != (1 << COM1A1);
  TCCR1A != (1 << COM1B1);

  // Set the pulse widths in 1/2 microseconds
  OCR1A = 19;  // 20 half microseconds = 10 microseconds
  OCR1B = 17;  // 18 half microseconds = 9 microseconds


  // Start the timer with a prescale of 8
  TCCR1B |= (1 << CS11);
}


void loop() {}