1

I'm working with Arduino's libraries. And my interest in the possibility to change the call stack of the SPI interrupt, this because I want: after the interrupt happen, the program could jump to other function and not to the interrupted address.

I mean: This is the Main code.

void loop ()
{
    func();
}

void func()
{
   // code
   // here happens the ISR interrupt
}

ISR happens inside func()

ISR(SPI_STC_vect)
{
    // code
    // SPDR = 0x00; // MY ANSWER to SPI
    // MODIFY CALL STACK TO GO TO LOOP()
}

Now, after answering the SPI, I want to go back to loop() instead of continue func().

How can I do this?

Thanks for your help.

  • 2
    For what reason (or purpose) you don't want to return to the interrupted code? – Nick Alexeev Apr 11 '15 at 0:01
  • Since the ISR entry code itself is probably pushing stuff on the stack, on top of the return address, you will have to write your ISR in assembly. This way you can pop the return address first thing in the ISR. Also, make sure func() is not using the stack and not touching the callee-saved registers. You may want to write that one in assembly too. – Edgar Bonet May 12 '15 at 6:00
1

Arduino is open source, so just modify the ISR to set a global flag when ran. Within func() just check for this flag and return when the condition is met. Little can be offered as little contextual information is given. To modify the stack you would have to work with avr assembly, but either way you must modify the ISR

Something like the following would work, but performance depends heavily on your application.

#define SPI_ISR_RAN 0x00
static volatile uint8_t flagByte;

void loop(){
    func();
}

void func(){
    uint8_t indicator;
    do {
        // Do something

        //Check for SPI condition
        ATOMIC_BLOCK(ATOMIC_RESTORESTATE){ //Atomic access is required
            indicator = flagByte;
        }
    }while( !(indicator & 1<<SPI_ISR_RAN) );

    ATOMIC_BLOCK(ATOMIC_RESTORESTATE){
        flagByte &= ~(1<<SPI_ISR_RAN); //Don't forget to clear the flag!
    }
}

ISR(SPI_STC_vect){
    // Do ISR necessities

    // Set flag when appropriate            
    flagByte & 1<<SPI_ISR_RAN;            
}    
| improve this answer | |
1

You can achieve almost what you are describing with the setjmp() and longjmp() functions. Here is an example, using INT0 to interrupt a function calling delay():

/*
 * Test non local return from ISR:
 *   - wire a pushbutton between digital 2 and GND
 *   - monitor the serial port at 9600/8N1
 *   - pressing the button should skip "func() ending" and immediately
 *     write "func() returned"
 */

#include <setjmp.h>

jmp_buf saved_context;

/*
 * ISR triggered by low level of INT0 = PD2 = Arduino digital 2.
 * Should only be allowed to trigger while inside loop().
 */
ISR(INT0_vect) {
    EIMSK &= ~_BV(INT0);        // disable further interrupts
    longjmp(saved_context, 1);  // return to loop()
}

void func() {
    Serial.println("  func() beginning");
    EIMSK |= _BV(INT0);   // enable INT0 interrupt
    delay(3e3);           // wait 3 s for an interrupt
    EIMSK &= ~_BV(INT0);  // disable INT0 interrupt
    Serial.println("  func() ending");
}

void setup() {
    pinMode(2, INPUT_PULLUP);  // pin 2 is INT0
    Serial.begin(9600);
}

void loop() {
    Serial.println("Calling func()");
    if (setjmp(saved_context) == 0) func();
    Serial.println("func() returned");
    Serial.println();
    delay(2e3);
}
| improve this answer | |
0

Now, after answering the SPI, I want to go back to loop() instead of continue func().

This is an extremely bad idea, and not what ISRs are for.

It is all very well wanting to go back to loop, but you would also have to discard whatever is on the stack, since loop started. The suggested method of using setjmp / longjmp may possibly achieve that, but almost certainly you are doing this for the wrong reason.

Jumping back to loop is like saying:

  • I don't care what was happening when the interrupt occurred
  • I don't care if it is half-way through sending some message
  • I don't care if it is half-way through turning on or off some device

In addition to this memory which was allocated (eg. by String objects) might not be freed. In fact the interrupt might occur during memory allocation in which case variables used by the memory allocation routine could be in an indeterminate state.

I strongly disagree with any suggested work-arounds. You simply should not be "jumping out of" an ISR and going somewhere else. Re-think the design.

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.