How to get 100ns delay in arduino uno

I was thinking about this too. But me personal I have zero knowledge about the Raspberry Pi pico rp2040 PIO
programmable Input Output. I did some googling about it. But I didn't find yet a really good tutorial about it. @westfw do you know a tutorial or a library that has things like
super-high-speed PIO-based

  • monoflop
  • nano-secpnds delay
  • super-high-speed bitshifting
    ?

best regards Stefan

How i can resolve it?

I am not sure you have read the responses carefully.
Just NOP instruction takes 62.5ns (F_CPU = 16MHz, 1s/16 000 000 = 62.5ns for 1 cycle). NOP is 1 cycle instruction. There is no time for the loop.
This:

    LED_PORT &= ~(1 << LED_BIT);
    asm volatile("NOP");
    LED_PORT |= (1 << LED_BIT);

is compiled to this:

  90:	2d 98       	cbi	0x05, 5	; 5
  92:	00 00       	nop
  94:	2d 9a       	sbi	0x05, 5	; 5

On ATmega328P the CBI, SBI instructions take 2 cycles each. NOP takes 1 cycle. Without NOP instruction it would be approximately 125ns pulse (if we suppose the pulse edge somewhere in the middle of the instruction). This is the minimum like @westfw wrote above.

This snippet of code demonstrates how you can generate pulses of 187.5ns (3 clock cycles), 125ns (2 clock cycles) and 62.5ns (1 clock cycle):

  LED_PORT |= (1 << LED_BIT);
  asm volatile("NOP");
  LED_PORT &= ~(1 << LED_BIT);
  
  ledOn();
  ledOff();
  
  PORTB = B00100000;
  PORTB = B00000000;

And the result is:

But as has been mentioned several times, you can't generate a 100ns pulse using a Uno.

2 Likes

@JohnLincoln: Try it without NOP and disable interrupts before, enable after.

This could work if 125ns is good enough for you.

    //...
    delay(8);       // 8ms
    delayMicroseconds(799); // 0.799ms
 
    noInterrupts(); // 1 cycle
    // Turn on the LED (High time: 100 ns)
    ledOn(); // 2 cycles
    ledOff(); // 2 cycles, turn off immediately for cca 125ns
    interrupts(); // 1 cycle
    //delayNanoseconds(100); 
    //     no any delay 
    // Re-attach interrupt to chopper pin (INT0) for detecting next falling edges
    attachInterrupt(digitalPinToInterrupt(2), handleChopperFallingEdge, FALLING);
   // ...

I'll give it a try.

The next problem is going to be that delay:

delay(8);       // 8ms
delayMicroseconds(799); // 0.799mstype or paste code here

8.799ms after the input pulse goes low is not the right place to start the pulse if we are wanting to start 200ns before the 9ms is over. (i know we can't do 200ns exactly).

There are a million nanoseconds in a millisecond, not a thousand.

The correct figure is going to be closer to 8.999ms.

Thank you
Yes, both the SBI and CBI instructins take 2 cycles

@JohnLincoln
I think, delayMicroseconds(0) takes 4 cycles so it would be 250ns.

50ns per cycle is with 20MHz which is perfectly good with 328P. Years ago I made an experiments with various frequencies, mainly with 1284P but also 328P. ATmega works perfectly up to 25MHz.

I think delayMicroseconds takes MORE that 4 cycles, and the timer used only has a resolution of 4us, so it's not very useful for delays less than 4us...

Ehm, I forgot call 3 cycles and return 4 cycles.

  a0:	0c 94 9d 00 	jmp	0x13a	; 0x13a <delayMicroseconds>
...
0000013a <delayMicroseconds>:

/* 16 MHz clock on most Arduino boards */
#elif F_CPU >= 16000000L

	// zero delay fix
	if(!us) return;				// 3 cycles, 4 cycles when true
 13a:	00 97       	sbiw	r24, 0x00	; 0
 13c:	49 f0       	breq	.+18     	; 0x150 <delayMicroseconds+0x16>
...
 150:	08 95       	ret

EDIT:
Routine call may vary from 2 to 7 cycles in dependence of type call which depends on program size too.
RJMP - 2 cycles,
JMP - 3
RCALL - 5 or 7

Here is what I noted for myself years ago.
Function call: 2 + 3 cycles for constant parameter (2xLDI + RCALL)
4 + 3 cycles for variable parameter (2xLDS + RCALL).
If the program is small or simply near of the function can use RJMP (only 2 cycles)
instead of RCALL.

I'm rusty!

Some time ago, I did some tests to measure what the delays generated by delayMicroseconds() were.

The results were as follows:

1us should not be 0.625us in result?

By a process of trial and error it seemed that :

delay(8);       // 8ms
delayMicroseconds(980); // 0.980ms

gave the best results, however there was a lot of jitter (about 4 microseconds or more).

Here is the best trace I captured, roughly correct:

but then another pulse might be too soon:

or even too late:


This doesn't seem to be a completely satisfactory solution.

1 Like

Interrupts?

I've recorded it not as 0.625µs, but as 0.0625µs (i.e. 1 clock cycle).
Could it be? It was the only one that was sub one microsecond.

All the subsequent measurements were each1.2µs too short.

Maybe, does the noInterupts() need to come earlier? or does that stop delay() and delayMicroseconds() working?

yes, it does
If I put the noInterrupts() before the delay(8), then the pulse is generated just a few microseconds after the falling edge of the chopper signal.

delayMicroseconds() doesn't need interrupts, delay() does.
If it supposed to be periodic signal it have to run with interrupts disabled so no delay().
Some time ago I played with R-R2 ladder. Here is a sine signal sample:


Interrupts are disabled.

Rig up a "one-shot" (?).

Turning interrupts off and just using delayMicroseconds(), has got the jitter down to around 500ns.

// Define the registers and bit masks for manipulating pin 13 (PB5)
#define LED_PORT    PORTB
#define LED_DDR     DDRB
#define LED_BIT     5

// Function to set the LED pin high
void inline ledOn() {
  LED_PORT |= (1 << LED_BIT);
}
// Function to set the LED pin low
void inline ledOff() {
  LED_PORT &= ~(1 << LED_BIT);
}
// Function to initialize the LED pin as output
void inline setupLed() {
  LED_DDR |= (1 << LED_BIT);
}

volatile bool chopperFallingEdgeDetected = false;

void setup() {
  setupLed();
  pinMode(2, INPUT_PULLUP);   // Chopper pin with internal pull-up resistor enabled

  // Attach interrupt to chopper pin (INT0) for detecting falling edges
  attachInterrupt(digitalPinToInterrupt(2), handleChopperFallingEdge, FALLING);
}

void loop() {
  if (chopperFallingEdgeDetected) {
    // Clear the interrupt flag
    detachInterrupt(digitalPinToInterrupt(2));
    noInterrupts(); // 1 cycle

    delayMicroseconds(8991); // 8.991ms
    asm volatile("NOP");
    asm volatile("NOP");
    asm volatile("NOP");
    asm volatile("NOP");
    asm volatile("NOP");
    asm volatile("NOP");
    asm volatile("NOP");
    asm volatile("NOP");
    asm volatile("NOP");

    // Turn on the LED (High time: 100 ns)
    ledOn(); // 2 cycles
    ledOff(); // 2 cycles, turn off immediately for cca 125ns
    interrupts(); // 1 cycle
    //     no any delay
    // Re-attach interrupt to chopper pin (INT0) for detecting next falling edges
    attachInterrupt(digitalPinToInterrupt(2), handleChopperFallingEdge, FALLING);
    // ...

    chopperFallingEdgeDetected = false;
  }
}

// ISR to handle chopper falling edge
void handleChopperFallingEdge() {
  chopperFallingEdgeDetected = true;
}

I had to use delayMicroseconds(8991), plus an extra 9 NOPs to get the correct delay.
That is the value needed by my Uno and my function generator. The actual value used will need tuning.

2 Likes