Search code examples
cmicrocontrollerinterrupt8051

8051 μCU - How are concurrent interrupts handled?


I've been programming on the 8051 micro-controller and I found something strange when dealing with interrupts. Suppose an interrupt has occurred. While servicing that interrupt, another one with a higher priority is occurring. Shouldn't the controller jump into servicing the higher priority interrupt and then return to the lower one?

The following illustrates my question. A keypad is wired to trigger external interrupt 1 (lower priority) and timer 0 interrupt (higher priority) is enabled.


// With this snippet, the LED-s are blinking as expected.
#include <8051.h>
#include <stdint.h>
#include <stdbool.h>

__xdata __at (0x9000) uint8_t KEYPAD;
__xdata __at (0xA000) uint8_t LED;

uint8_t LedState = 0x00;
bool Running = false;

void StopperIsr() __interrupt TF0_VECTOR
{
    LedState = ~LedState;
    LED      = LedState;
    TR0      = 0;   // Prevent the timer restating right away.
    Running = false;
}

void StopperStart()
{
    TL0 = 0;
    TH0 = 0;
    TR0 = 1;      // Start timer 0
    Running = true;
}

void main()
{
    ET0  = 1;     // Enable timer 0 interrupt.
    EA   = 1;     // Enable global interrupts.
    TMOD = T0_M0; // Set timer 0 to 16-bit mode.

    while(1) {
      if (false == Running) {
        StopperStart();
      }
    }
}

// The stopper is used inside external interrupt 1 ISR and since timer 0 has
// higher priority, the LED-s should be blinking just like in the previous snippet.
// This is not the case. Instead, on keypress, the ISR is called (LED gets 0xFF),
// but timer 0 ISR is never entered.
#include <8051.h>
#include <stdint.h>
#include <stdbool.h>

__xdata __at (0x9000) uint8_t KEYPAD;
__xdata __at (0xA000) uint8_t LED;

uint8_t LedState = 0x00;
bool Running = false;

void StopperStart()
{
    TL0 = 0;
    TH0 = 0;

    TR0 = 1;      // Start timer 0.
    Running = true;
}

void StopperIsr() __interrupt TF0_VECTOR
{
    LedState = ~LedState;
    LED      = LedState;

    TR0      = 0;  // Stop the timer.
    Running = false;
}

void KeypadIsr() __interrupt IE1_VECTOR
{
    LedState = 0xFF;
    LED      = LedState;

    while(1) {
      if (!Running) {
        StopperStart();
      }
    }
}

void main()
{
    EX1  = 1;     // Enable keypad interrupt on external interrupt 1.
    ET0  = 1;     // Enable timer 0 interrupt.
    TMOD = T0_M0; // Set timer 0 to 16-bit mode.
    EA   = 1;     // Enable global interrupts.
    KEYPAD = 0;   // Reset the keypad to its initial state.
}

Solution

  • Perhaps "simultaneous" is the wrong word here, because that implies to me that they start at the same time. Given that, the one of higher priority would win. I think "concurrent" might be a better word. You would think that the processor would jump to the one of higher priority, but no, that's not quite how it works. There is only one interrupt line coming into the actual processor. So as far as the processor is concerned, it's either handling an interrupt, or not (the line is signaled or not signaled). There is an interrupt controller who helps multiplex multiple interrupts onto that single line, and it's the interrupt controller that helps determine priority. In a bigger microcontroller, there's a little more separation between the interrupt controller and the processor, but it's still there in the 8051... just a tad more hidden. At any rate, once interrupt processing starts, the microcontroller will handle that interrupt until completion before being notified of the next one, even if the pending one is of higher priority.

    For details on how this works, you can look at Atmel's Hardware Guide for the 8051. While it's specific to their implementation, most implementations are pretty close (they all try to keep the original semantics of the original 8051 micro). On page 2-112 they talk about interrupts are handled. Section 2.16.1 says:

    Execution proceeds from that location until the RETI instruction is encountered. The RETI instruction informs the processor that this interrupt routine is no longer in progress, then pops the top two bytes from the stack and reloads the Program Counter. Execution of the interrupted program continues from where it left off

    It's that first line that's important here. Until that RETI instruction is executed, the next interrupt will not be handled.