"For" statement runs twice in ISR

I have encountered a problem that I can't figure out. Here are the particulars:

  1. My board is a Chinese Arduino Nano. There is no additional circuitry attached to it. I am applying +12v and ground to the upper right-most pins on the board, and am applying a pulse to INT0.

  2. I do not have an emulator--I am doing my diagnosing with a Tektronix 2465B scope, using digitalWrite statements to bracket the period when the routine is running. This is described in more detail below.

  3. My routine is very simple: the ISR is triggered by a RISING EDGE pulse on INT0. The ISR consists of a nested FOR loop that performs a digitalWrite out to a pin, first HIGH for a count of 128000, and then a LOW for 64000. It should do this 6 times. The problem is that the routine runs through the loop twice as many times as it should. The loop right now is set to run through 6 times. It actually runs through 12 times. If I reduce the "6" to "4" it runs 8 times, etc.

  4. The routine is NOT returning to the main loop and being re-triggered. How do i know this? Because I put a digitalWrite statement into the main loop to trigger another of the pins HIGH and then LOW. During the time when when the program is not running the ISR that pin just toggles HIGH and then LOW. The moment I apply a pulse to INT0 that pin stops toggling and remains stopped until the ISR returns from interrupt.

  5. Here is the entire routine:

// Test_Project.ino
// Board is a Chinese clone of the Arduino Nano
// REMEMBER TO USE THE PROCESSOR WITH THE OLD BOOT LOADER SETTING!

int sensorSignalActivePin = 2; // INT0/D2 INTERRUPT INPUT active HIGH HEADER NO. L5
int firstLevelMonitorPin = 7; // PD7 HEADER NO. L10
int localbuzzerPin = 13; // PB5 BUZZER DRIVE HEADER NO. R16

void setup()
{ // HEADERS NUMBERED LIKE AN IC LEFT: TOP > BOTTOM; RIGHT: BOTTOM > TOP
// GROUND HEADER NO. L4
pinMode(sensorSignalActivePin, INPUT); // INT0/PD2 INTERRUPT INPUT active HIGH HEADER NO. L5 BLUE
pinMode(localbuzzerPin, OUTPUT); // PB5 initialize the digital pin as an output HEADER NO. R16
pinMode(firstLevelMonitorPin, OUTPUT); // PD7 HEADER NO. L10

// **********************INITIALIZATION ***********************************
digitalWrite(localbuzzerPin, LOW); // initialize local buzzer state
for (int start_beep_cnt = 0; start_beep_cnt < 3; start_beep_cnt++) // Three beeps for initialization
{
digitalWrite(localbuzzerPin, HIGH); // turn local buzzer ON
delay (500); // delay 1/2 second
digitalWrite(localbuzzerPin, LOW); // turn local buzzer OFF
delay (250); // delay 1/4 second
}
delay (5000); // delay 5 seconds after initialization

EIFR = 0x01; // Hopefully clear any pending interrupts
attachInterrupt (digitalPinToInterrupt(sensorSignalActivePin),TestISR, RISING);
}

void loop()
// ****** JUST SLEEP HERE AND WAIT FOR THE SENSOR TO INITIATE A CALL TO ACTION ***********
{
// The loop is empty because the device is just lying in wait for an interrupt from the SensorSignalActive pin
}

// *************** INTERRUPT SERVICE ROUTINE ************************

void TestISR()

{ // start of ISR

digitalWrite(firstLevelMonitorPin,HIGH); // L10 Scope monitor point

for (int L1_buzz_cnt = 0; L1_buzz_cnt < 3; L1_buzz_cnt++)
{
for (long L1_buzz_time_H = 0; L1_buzz_time_H < 128000; L1_buzz_time_H++)
{
digitalWrite(localbuzzerPin, HIGH); // turn the local buzzer ON
}
for (long L1_buzz_time_L = 0; L1_buzz_time_L < 64000; L1_buzz_time_L++)
{
digitalWrite(localbuzzerPin, LOW);
}
}
digitalWrite(firstLevelMonitorPin,LOW);

} // end of ISR

Hello
The contact of the button will bounce and generate multiple interrupts.

Please follow the advice given in the link below when posting code , use code tags and post the code here to make it easier to read and copy for examination

Do you mean that both for loops run twice each time that the ISR is triggered ?
What is triggering the ISR

Thank you for (both of) your incredibly quick replies. Sorry for not following the guide-I'll do better next time.

To your question, the entire nested FOR loop runs twice as long as it should, that is the loop that is supposed to run six times actually runs twelve times.

Hello Paul, it appears that the ISR is running twice as long as it should before returning from interrupt.

Hello
Take some times and study the usage of interrupt technology and the best way to design smart ISR´s.

Apologies for missing the second part of your question: A sensor that can indeed put out multiple pulses under certain conditions is providing the interrupt. It's my understanding that interrupts are disabled once the ISR is entered. is this not true?

Yes, but then again, no. Interrupts are indeed disabled, but as soon as the ISR is over, they come back on and if an interrupt happened during the ISR, it will trigger. If more than one interrupt of the same type occurred during the ISR, the additional ones are lost.

OHHHH, I didn't know that. That's probably the issue...I'll look into it. Thank you for taking the time to respond

On entering the ISR set a Flag then leave.

Then in loop( ) check the flag, if it is set, deal with it then reset the Flag.

I'm unsure why you are writing all your code into an ISR.
The notion of an iterrupt is that your code is running dealing with the mundane work (eg doing your laundry), and an interrupt comes along that demands a higher level of priority for a short time (call on telephone). It gets handled, and your program continues.

Unless there is a VERY important reason to manage this in an interrupt you should just - as @LarryD says, use the ISR to set a flag, and handle it properly within your loop();

1 Like

Good morning:
I do realize that and my answer is that this is a very unusual case where the main loop exists only to wait for an interrupt to occur--there is nothing else for it to do. The reason that I wrote it the way that I did was to minimize power consumption--the mcu can essentially sleep while nothing is happening, which is most of the time.

The code that I posted was actually only an example of the type of problem that I was having. And for better or worse, I figured out how to fix it: I simply did a EIFR = 0x01 before returning. That ensures a clean return back into loop() and effectively debounces my sensor.

This (modified) example shows how you can use an interrupt to control sleep modes.

I'm not sure of this but I suspect your code does nothing to reduce power consumption. Why?
Because when the interrupt is not called the arduino will be running the empty loop continuously, with all the normal functions (Serial port, ADC, etc) taking power.

The code is a bit tricky to test because it puts the arduino to sleep - then you cant reprogram it until its reset and another sketch (eg blink) uploaded.

/**
 * Author:Ab Kurk
 * version: 1.0
 * date: 24/01/2018
 * Description: 
 * This sketch is part of the beginners guide to putting your Arduino to sleep
 * tutorial. It is to demonstrate how to put your arduino into deep sleep and
 * how to wake it up.
 * Link To Tutorial http://www.thearduinomakerman.info/blog/2018/1/24/guide-to-arduino-sleep-mode
 * 
 * Micro, Leonardo, other 32u4-based boards support interrupts on pins 0, 1, 2, 3, 7
 * Sleep modes For the Atmega328P (Nick Gammon http://www.gammon.com.au/power
 * SLEEP_MODE_IDLE: 15 mA
 * SLEEP_MODE_ADC: 6.5 mA
 * SLEEP_MODE_PWR_SAVE: 1.62 mA
 * SLEEP_MODE_EXT_STANDBY: 1.62 mA
 * SLEEP_MODE_STANDBY : 0.84 mA
 * SLEEP_MODE_PWR_DOWN : 0.36 mA
 */

#include <avr/sleep.h>//this AVR library contains the methods that controls the sleep modes
const byte interruptPin = 7; //Pin we are going to use to wake up the Arduino
const byte LEDPin = 6; //connected to led & resisitor on pin 6


void setup() {
  Serial.begin(9600);//Start Serial Comunication
  pinMode(LEDPin,OUTPUT);//indicate when Arduino is Asleep
  pinMode(interruptPin,INPUT_PULLUP);//Set interruptPin as input
  digitalWrite(LEDPin,HIGH);//turning LED on - its awake
  delay(100); //allow time for serial comms to start
  Serial.println("Initialisation complete");
  delay(10000);// allow 10 sec to write above message or reprogram before it can sleep 
  Serial.println("Setup completed");
  delay(1000);
}

void loop() {
  Serial.println("Going_To_Sleep in 5 sec");
 delay(5000);//wait 5 seconds before going to sleep
  Going_To_Sleep();
}

void Going_To_Sleep(){
    sleep_enable();//Enabling sleep mode
    //attachInterrupt(0, wakeUp, LOW);//attaching a interrupt to pin d2
    //https://www.arduino.cc/reference/en/language/functions/external-interrupts/attachinterrupt/
    //changed above line to use recommended syntax, and trigger on rising edge
    attachInterrupt(digitalPinToInterrupt(interruptPin), wakeUp, RISING ); 
    //set_sleep_mode(SLEEP_MODE_PWR_DOWN);//Setting the sleep mode, in our case full sleep
    set_sleep_mode(SLEEP_MODE_ADC);
    digitalWrite(LEDPin,LOW);//turning LED off
    delay(1000); //wait a second to allow the led to be turned off before going to sleep
    sleep_cpu();//activating sleep mode
    Serial.println("just woke up!");//next line of code executed after the interrupt 
    digitalWrite(LEDPin,HIGH);//turning LED on
  }

void wakeUp(){
  Serial.println("Interrupt Fired");//Print message to serial monitor
   sleep_disable();//Disable sleep mode
  detachInterrupt(digitalPinToInterrupt(interruptPin)); //Removes the interrupt from interruptPin;
}

Hello and thank you for taking the time to reply.

To your message on 8/7 where you said "Unless there is a VERY important reason to manage this in an interrupt..."

My reply is that yes, there is a very important reason: This program exists for one reason only: To perform a particular function upon receipt of an interrupt. There are no (non-sleeping) housekeeping or other types of operations required that might be best served in loop().

And to your second message regarding putting the Arduino to sleep: I did precisely that. As you might expect nothing exists in loop() except for the housekeeping tasks required for enabling sleep. I followed Nick Gammon's example for putting the Arduino to sleep and waking it via interrupt. It works well.

Thanks to you and to every one of you for taking the time to help me with this.