This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

Interrupt servicing after longjmp instruction

I'm using Keil compiler for 8051Warp uP.
I would like to make a jump from an interrupt
routine back to the main function, this way:

main ()
{
init_function();

if (setjmp(env)!=0)
{
// Recovery procedure here
...
}

while (1)
{
// body of my program
...
}

}

void irq (void) interrupt (12)
{
if (condition) longjmp (env,1);
}

void init_function()
{
IE = 0xA0;
IEN1 = 0xC4;
IP = 0x00;
IP1= 0x40;
}

I'm experiencing the following problem.
Right after the longjmp instruction, the execution actually
restarts at the setjmp instruction point (as i wish), but no more interrupt
can be serviced after this jump.
It looks like the IE.7 bit has been reset in some way. Anyway, even if i set
the IE / IE1 registers after the jump, no more interrupt are serviced
by the uP.
Somebody could help me? Thanx.

Parents
  • Inside init_function(), save the address it will be returning to:

    restartAddrMSB = *(SP - 0);
    restartAddrLSB = *(SP - 1);

    Later, when your ISR wants to return back to the point after the init_function() call in main(), it can replace its return address with the restart address and return. You'll have to look at the code the compiler generates too derive the stack offset to the ISR's return address. Let's say it pushed ACC, B, PSW, and a register bank. The stack would look like:

        |     ...     |
        +-------------+
        | RETADDR_LSB |
        +-------------+
        | RETADDR_MSB |<-- SP - 11
        +-------------+
        |     ACC     |
        +-------------+
        |      B      |
        +-------------+
        |     PSW     |
        +-------------+
        |     R0      |
        +-------------+
        |     R1      |
        +-------------+
        |     R2      |
        +-------------+
        |     R3      |
        +-------------+
        |     R4      |
        +-------------+
        |     R5      |
        +-------------+
        |     R6      |
        +-------------+
        |     R7      |<-- SP
        +-------------+
        |             |

    To replace the ISR's return address:

    *(SP - 11) = restartAddrMSB;
    *(SP - 12) = restartAddrLSB;

    Hopefully, I got the address MSB/LSB in the correct order, but check my work!

Reply
  • Inside init_function(), save the address it will be returning to:

    restartAddrMSB = *(SP - 0);
    restartAddrLSB = *(SP - 1);

    Later, when your ISR wants to return back to the point after the init_function() call in main(), it can replace its return address with the restart address and return. You'll have to look at the code the compiler generates too derive the stack offset to the ISR's return address. Let's say it pushed ACC, B, PSW, and a register bank. The stack would look like:

        |     ...     |
        +-------------+
        | RETADDR_LSB |
        +-------------+
        | RETADDR_MSB |<-- SP - 11
        +-------------+
        |     ACC     |
        +-------------+
        |      B      |
        +-------------+
        |     PSW     |
        +-------------+
        |     R0      |
        +-------------+
        |     R1      |
        +-------------+
        |     R2      |
        +-------------+
        |     R3      |
        +-------------+
        |     R4      |
        +-------------+
        |     R5      |
        +-------------+
        |     R6      |
        +-------------+
        |     R7      |<-- SP
        +-------------+
        |             |

    To replace the ISR's return address:

    *(SP - 11) = restartAddrMSB;
    *(SP - 12) = restartAddrLSB;

    Hopefully, I got the address MSB/LSB in the correct order, but check my work!

Children
  • ...And a high priority ISR needs to know if it interrupted a low priority ISR and if so, don't perform the return address replacement. Let the low priority ISR do that.

  • Sheesh! ...And in main() after the call to init_function(), you'll want to reset SP to its initial value.

  • I know, I'm becoming tedious, but to properly terminate the interrupt posture and ensure that interrupts are disabled upon ISR return, you'd still want to do the ISR return address replacement I described above, but you'd also want to RETI to a CLR EA instruction. The MCU will always execute one instruction after the RETI, the CLR EA in this case. So the init_function() would look something like:

    void init_function( void )
    {
        initialSP      = SP - 2;
        restartAddrMSB = *(SP - 0);
        restartAddrLSB = *(SP - 1);
    
        /* ... */
    }

    And main() would now look something like:

    void main( void )
    {
        init_function();
        EA = 0;             /* CLR EA */
        SP = initialSP;     /* No effect on 1st through */
    
        /* ... */
    
        EA = 1;             /* Ready to rumble */
    
        /* ... */
    }

    I'll shut up now.