Automatically exit from interrupt

Hi,
Im trying to learn a few about the external interrupts, I thought about a program in which a green led would light and when I pressed a button, that led would go out and a red led would light. Briefly, a green led lights up, press the button once, the green led goes out, the red led lights up for 10 seconds or as long as I want, then automatically turns off and the green led lights up again. But when I press the button, the red led lights up, the green one still lights, and thats all. I don’t know how to make the interrupt to execute for 5 seconds, then automatically exit.

const uint8_t button_pin = 2; // INT0
int green = 6;
int red = 5;
long previousMillis = 0;
void setup() {
  Serial.begin(9600);
  pinMode(green, OUTPUT);
  pinMode(red, OUTPUT);
  attachInterrupt(digitalPinToInterrupt(button_pin), toggle, RISING);
}
void loop() {
  digitalWrite(green, HIGH);
}
void toggle(){
  for(int m=0; m<=3000; m++){
    digitalWrite(green, LOW);
    digitalWrite(red, HIGH);
    if(m==3000)
      break;
      }// for
}//toggle

You want the interrupt to simply set a flag that the main program sees and executes your sequence.

cristian10001:
Hi,
up, the green one still lights, and that`s all. I don’t know how to make the interrupt to execute for 5 seconds, then automatically exit.

volatile bool MesaIsaFlaga = false; 
const uint8_t button_pin = 2; // INT0
int green = 6;
int red = 5;
long previousMillis = 0;
void setup() {
  Serial.begin(9600);
  pinMode(green, OUTPUT);
  pinMode(red, OUTPUT);
  attachInterrupt(digitalPinToInterrupt(button_pin), toggle, RISING);
}
void loop() {
  if( MesaIsaFlaga  )
{
// this be the point you want to do your thing do's
// and even more thing do's
// and finally get ready to do the thing do with the flag
MesaIsaFlaga = false;
}
}
void toggle(){

MesaIsaFlaga = true;

}//toggle

As per the above suggestion, set a flag

Idahowalker:

volatile bool MesaIsaFlaga = false; 

const uint8_t button_pin = 2; // INT0
int green = 6;
int red = 5;
long previousMillis = 0;
void setup() {
  Serial.begin(9600);
  pinMode(green, OUTPUT);
  pinMode(red, OUTPUT);
  attachInterrupt(digitalPinToInterrupt(button_pin), toggle, RISING);
}
void loop() {
  if( MesaIsaFlaga  )
{
// this be the point you want to do your thing do's
// and even more thing do's
// and finally get ready to do the thing do with the flag
MesaIsaFlaga = false;
}
}
void toggle(){

MesaIsaFlaga = true;

}//toggle




As per the above suggestion, set a flag

Thanks for trying to give me an answer, but I don't get it at all, its like chinese for me. So, to exit from the for loop I have to write: if (m == 3000) flag = true ? Or Im too dumb?

Does this work (compiles, not tested) and does it make sense to you?

#define TIME_BLINK_RED      10000ul     //mS    10-sec red LED blink

const byte button_pin = 2; // INT0
const byte green = 6;
const byte red = 5;

volatile bool bInterruptFlag;

void setup() 
{
    Serial.begin(9600);
    pinMode(green, OUTPUT);
    pinMode(red, OUTPUT);
    attachInterrupt(digitalPinToInterrupt(button_pin), toggle, RISING);

    digitalWrite( green, HIGH );
    digitalWrite( red, LOW );

    bInterruptFlag = false;
    
}

void loop() 
{
    LightsStateMachine();
    
    //can do other stuff in loop() while LightsStateMachine() reacts to interrupt flag and times LEDs
    //...
    
}//loop

// state names
#define STATE_0     0   //waiting for ISR flag
#define STATE_1     1   //timing LED blink
//
void LightsStateMachine( void )
{
    bool
        bFlg;
    unsigned long
        timeNow;
    static unsigned long
        timeLED;
    static byte
        stateLEDs = STATE_0;

    timeNow = millis();
    switch( stateLEDs )
    {
        case    STATE_0:
            noInterrupts();
            bFlg = bInterruptFlag;
            interrupts();

            if( bFlg )
            {
                digitalWrite( green, LOW );
                digitalWrite( red, HIGH );
                timeLED = timeNow;
                stateLEDs = STATE_1;
                
            }//if
            
        break;

        case    STATE_1:
            if( timeNow - timeLED >= TIME_BLINK_RED )
            {
                digitalWrite( green, HIGH );
                digitalWrite( red, LOW );
                stateLEDs = STATE_0;
                noInterrupts();
                bInterruptFlag = false;
                interrupts();
                
            }//if

        break;
        
    }//switch
    
}//LightsStateMachine

void toggle()
{
    //just set a flag indicating to loop that interrupt occured
    bInterruptFlag = true;
    
}//toggle

cristian10001:
I don't know how to make the interrupt to execute for 5 seconds,

You don't EVER want an interrupt to execute for 5 seconds. An interrupt that takes 100 microsecs to complete would be slow. 10 microsecs would be good.

Learning about interrupts is good. However you would not normally need to use an interrupt to detect a button pressed by a human because humans are very slllooooowwwwww. Interrupts are normally used to detect something that exists for a very short time or something that happens at high frequency, Using a button press for learning is fine.

...R

Blackfin:
Does this work (compiles, not tested) and does it make sense to you?

#define TIME_BLINK_RED      10000ul     //mS    10-sec red LED blink

const byte button_pin = 2; // INT0
const byte green = 6;
const byte red = 5;

volatile bool bInterruptFlag;

void setup()
{
    Serial.begin(9600);
    pinMode(green, OUTPUT);
    pinMode(red, OUTPUT);
    attachInterrupt(digitalPinToInterrupt(button_pin), toggle, RISING);

digitalWrite( green, HIGH );
    digitalWrite( red, LOW );

bInterruptFlag = false;
   
}

void loop()
{
    LightsStateMachine();
   
    //can do other stuff in loop() while LightsStateMachine() reacts to interrupt flag and times LEDs
    //...
   
}//loop

// state names
#define STATE_0    0  //waiting for ISR flag
#define STATE_1    1  //timing LED blink
//
void LightsStateMachine( void )
{
    bool
        bFlg;
    unsigned long
        timeNow;
    static unsigned long
        timeLED;
    static byte
        stateLEDs = STATE_0;

timeNow = millis();
    switch( stateLEDs )
    {
        case    STATE_0:
            noInterrupts();
            bFlg = bInterruptFlag;
            interrupts();

if( bFlg )
            {
                digitalWrite( green, LOW );
                digitalWrite( red, HIGH );
                timeLED = timeNow;
                stateLEDs = STATE_1;
               
            }//if
           
        break;

case    STATE_1:
            if( timeNow - timeLED >= TIME_BLINK_RED )
            {
                digitalWrite( green, HIGH );
                digitalWrite( red, LOW );
                stateLEDs = STATE_0;
                noInterrupts();
                bInterruptFlag = false;
                interrupts();
               
            }//if

break;
       
    }//switch
   
}//LightsStateMachine

void toggle()
{
    //just set a flag indicating to loop that interrupt occured
    bInterruptFlag = true;
   
}//toggle

Thanks sir, it really works, I thought I only needed a loop, but I remembered that it is not a good idea to use the delay inside the interruptions. So the program works like this: I set green to HIGH and red to LOW. When I press the button, the interruptflag will change to true then the Machine function will be called. When you type stateLEDs = STATE_1; means that it jumps automatically in case 2, where it stays 10 seconds and then breaks.

Robin2:
You don't EVER want an interrupt to execute for 5 seconds. An interrupt that takes 100 microsecs to complete would be slow. 10 microsecs would be good.

Learning about interrupts is good. However you would not normally need to use an interrupt to detect a button pressed by a human because humans are very slllooooowwwwww. Interrupts are normally used to detect something that exists for a very short time or something that happens at high frequency, Using a button press for learning is fine.

...R

Thank you for the advice.
I want to display temp & hum on a tm1637, and I was thinking of displaying temp & hum for 5 seconds after I pressed the button. Initially I did not want to use the hardware interrupts, I tried to do this automatically with a software interrupt (from 5 to 5 minutes I switch from clock mode to temp & hum mode).

What you’ve done is a long-winded way to achieve your toggle goal, but perfectly valid to learn about interrupts.

As Robin said, normally buttons would be tested in the not mal looping of the code, except for special cases or high precision events.

The other thing to look at is ‘debouncing’ your button...
Currently, you can’t see it, but it’s quite likely the state is changing maybe 2-10 times each time the switch changes.

cristian10001:
Thanks sir, it really works, I thought I only needed a loop, but I remembered that it is not a good idea to use the delay inside the interruptions. So the program works like this: I set green to HIGH and red to LOW. When I press the button, the interruptflag will change to true then the Machine function will be called. When you type stateLEDs = STATE_1; means that it jumps automatically in case 2, where it stays 10 seconds and then breaks.

Sort of. The program never spends very long -- more than a few microseconds -- in any part of the state machine. Think of it more like peeking in very often to see if something needs doing.

In state '0', this would be indicated by the interrupt flag being set. If that's not set, the function simply exits.

In state '1', this is indicated by this expression being true:

if( timeNow - timeLED >= TIME_BLINK_RED )

The check takes microseconds; if it's not true, the function exits. It happens that this condition won't be 'true' for ~10-seconds after the interrupt flag is set. Only then will the code inside the if() execute -- turn off the LEDs and set up to return to state '0' to wait for the interrupt flag to become true again.

The key is that the code doesn't spend a significant amount of time -- let alone 10-seconds -- doing any one thing. You can put a lot of other code inside loop() or in functions called from loop() and not really affect the timing of the LEDs unless that code blocks or is otherwise poorly written so that the state machine isn't called often.

Just as an example, I have code running about 30 separate millis() timers plus two serial ports and a lot of other logic running on an AVR processor.
Nothing is busy for any longer than a couple of mS at the most.

cristian10001:
I want to display temp & hum on a tm1637, and I was thinking of displaying temp & hum for 5 seconds after I pressed the button.

That's perfectly reasonable. But the interrupt should just be used to identify the start of the period. The decision about the end of the period should be figured out in the ordinary body of the code.

Initially I did not want to use the hardware interrupts, I tried to do this automatically with a software interrupt (from 5 to 5 minutes I switch from clock mode to temp & hum mode).

I don't know what you mean by a software interrupt. Have a look at how the code is organized in Several Things at a Time and how millis() is used to manage timing without blocking. Note how each function runs very briefly and returns to loop() so the next one can be called. None of the functions tries to complete a task in one call. And there may be dozens of calls to a function before it is actually time for it to do anything.

...R

Robin2:
Interrupts are normally used to detect something that exists for a very short time or something that happens at high frequency, Using a button press for learning is fine.

The main purpose of adding interrupt capability within a processor is to allow the processor to serve a peripheral asynchronously. There is no need for the processor to poll the peripherals if they need services; rather, the peripheral requiring services interrupts the processor. There is a great saving of times that increases the throughput of the processor.