Synchronizing Interrupts and void loop ??

Dear Forum,
this is my first post because usually I can find easily an answer or some guidance to my Arduino related question but today ... well.

I've tried to make a simple TTL pulse delay circuit whith an Arduino Nano 328. The task is to deliver a pulse after an interrupt has been detected ... so very easy - one would think.

I know that delayMicroseconds is not perfect and one could use some workaround with micros() etc. to wait for a specific amount of time and than do the task but delayMicroseconds() is sufficient in my application.

The problem I've encountered is that the time between the detected interrupt and the pulse varies ( approx 5 µs) which is something we would expect from the accuracy given in the Arduino Reference, HOWEVER, the pulse width itself (20µs) which is also shaped by delayMicroseconds is PERFECT! no variance at all!
Unfortunately the variation between the input pulse and the delayed pulse is not random, for me it looks like something which is just out of sync (like two sine wave with almost the exact frequencies) and that's why my question: can one sync the interrupt calls with the void loop entries?

Here's the code :

int pin = 12;  //pulse output                     
volatile boolean state = false;  //value changed by the pulse interrupt routine
volatile int x = 1;              //value changed by the button interrupt routine
unsigned long delay_val = 1;

volatile unsigned long button_time = 0;  
volatile unsigned long last_button_time = 0; 


void setup()
{
  noInterrupts();
  pinMode(pin, OUTPUT);
  digitalWrite(pin,LOW);
  attachInterrupt(0, pulse, RISING);
  attachInterrupt(1, increment, FALLING);
}

void loop()
{
    delay_val = (x*100)-12; 
    if (state==true){
      noInterrupts();
      delayMicroseconds(delay_val); //Somehow my Arduino has a 12 µs delay with delayMicroseconds set to 0, here we get rid of it !
      digitalWrite(pin, HIGH);
      delayMicroseconds(20);
      digitalWrite(pin, LOW);
      state = ! state;
     }
  
  interrupts();
 
 }

void pulse()
{
  noInterrupts();
  state = ! state;
}

void increment() {
  noInterrupts();
  button_time = millis();
  if (button_time - last_button_time > 250)
  {
    x++;
    last_button_time = button_time;
    if(x>20){x=1;}
  }
}

Best,
glycylalanin

//Somehow my Arduino has a 12 µs delay with delayMicroseconds set to 0, here we get rid of it

Using delayMicroseconds(0) is not valid. Lowest number is 3 or 4.

void loop()
{
    delay_val = (x*100)-12; 
    if (state==true){
      noInterrupts();
      delayMicroseconds(delay_val); //Somehow my Arduino has a 12 µs delay with delayMicroseconds set to 0, here we get rid of it !
      digitalWrite(pin, HIGH);
      delayMicroseconds(20);
      digitalWrite(pin, LOW);
      state = ! state;
  //move interrupts(); to here?
     }
  
  interrupts();  <<<why is this here, instead of up a little farther?
  // here, it is being called constantly,  instead of only after it 
  // has been turned off following state == true
 
 }

Dear CrossRoads,
thanks! Indeed delayMicroseconds(0) is weird but what I meant was basically without it :wink: so no delay between interrupt and the pulse at all gives me ~12 µs difference with the mentioned variance in between.

Stop using noInterrupts() all over the place, the one at the start of setup() is not required. And so are the ones in each of the interrupt handlers!

Interrupts are disabled by the CPU when you enter the interrupt handler they are automatically turned on again at the end of the handler.

Turning off interrupts stops micros(),mills() and there related functions from working it also makes a mess of serial.

There is no need to turn off interrupts when reading/modifying a boolean!

Mark

Dear holmes4,
thanks for your kind advice. Indeed I wasn't too saving with noInterupts(), BUT: the one in the main loop actually helped quite substantially and reduced the variance.
Regards,
glycylalanin

By the way ... what if I would send the board to sleep very briefly ... might this help?
I've played a little with the instructions given on this page Arduino, Zigbee and Embedded Development: Sleeping Arduino - Part 4 Wake Up Via Internal Timer but with little success ... as I mess around with interrupts again, the pulses are not initiated .... but every help is appreciated ...
Regards,
glycylalanin

I thought maybe it would help to upload a little movie. What you see in Ch1 is the output of my function generator triggering the scope and the Arduino at about 1 kHz. 100µs later there's the pulse which is not exactly 20 µs but who cares. But what you also see is the shifting of the rectangular pulse of a couple of µs.
Regards,
glycylalanin

MOV_0090.mp4 (1.44 MB)

glycylalanin:
The problem I've encountered is that the time between the detected interrupt and the pulse varies ( approx 5 µs) which is something we would expect from the accuracy given in the Arduino Reference, HOWEVER, the pulse width itself (20µs) which is also shaped by delayMicroseconds is PERFECT! no variance at all!

Others have mentioned the unnecessary use of noInterrupts() which may well affect the values in micros().

If all you want is to start a pulse when an interrupt occurs why not manipulate the pulse pin in the ISR. You can use digitalWrite() which is relatively slow or you can use PORTn (direct port manipulation) which is a little trickier to code but will change the pin in a single instruction. You could also use the ISR to record the micros() value of when the pulse starts and then use code in loop() to check when the pulse should end.

...R

Thanks Robin2,
I'll give it a try immediately!
Regards,
glycylalanin

Now I am using this code:

volatile unsigned long button_time = 0;  
volatile unsigned long last_button_time = 0; 
volatile int x = 1;
int pulse_delay = 0;

void setup()
{
  attachInterrupt(0, pulse, RISING);
  attachInterrupt(1, increment, FALLING);
  DDRB = (1<<PB4); 
  PORTB = (0<<PB4);
}

void loop()
{
  pulse_delay= x*100; 
}

void pulse()
{
  delayMicroseconds(pulse_delay);
  PORTB = (1<<PB4);                                                                                                                                                                                                                                                                                                                                 
  delayMicroseconds(20);
  PORTB = (0<<PB4);
}


void increment() {
  button_time = millis();
  if (button_time - last_button_time > 200){
    x++;
    last_button_time = button_time;
    if(x>20){x=1;}
  }
}

Unfortunately I had no luck to include the micros(). But it works much better though not perfect. There are still those jumps ... Well for me its good enough.
Thank you !
Regards,
glycylylanin

Interrupts are disabled in an ISR.
I don't see how delayMicroseconds can work here:

void pulse()
{
  delayMicroseconds(pulse_delay);
  PORTB = (1<<PB4);                                                                                                                                                                                                                                                                                                                                 
  delayMicroseconds(20);
  PORTB = (0<<PB4);
}

I think you need to set a flag, check for it in loop:

void pulse()
{
  PORTB = (1<<PB4);                                                                                                                                                                                                                                                                                                                                 
}

void loop(){
if ((PINB & 0b00010000) >= 0){ // PB4 set by ISR
delayMicroseconds(20);  // wait 20uS
PORTB = PORTB & 0b11101111; // clear PB4
}
}

I don't know what you're doing with the other one, counting for some reason that you don't use.

Sorry, maybe I wasn't clear enough. I only intended that you would start the pulse and record the time in the ISR. Something like this

void pulse() {
   PORTB = (1<<PB4);
   pulseStartMicros = micros();
   pulseStarted = true;
}

and then in loop() you would call another function perhaps called endPulse() - like this

void endPulse() {
   if (micros() - pulseStartMicros >= pulseLengthMicros && pulseStarted) {
      PORTB = (0<<PB4);
      pulseStarted = false;
   }
}

If that doesn't give sufficiently accurate pulses you could use the first ISR to start one of the hardware Timers which would trigger another interrupt when the pulse duration expires.

...R