Question

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.
}
Was it helpful?

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.

OTHER TIPS

Sorry to say this, but here the completely wrong answer was accepted as the best. In the same time TurboJ has given the correct answer (more then one actually) which is visible only as a comment.

The 8051 micro (or any other with more than one interrupt priority level) WILL NOT execute the interrupt until completition, if higher level interrupt was pending, if all relevant interrupts were enabled, and priorities correctly set. The higher priority interrupt handler will execute. The same priority interrupts will be scanned in the predetermined order, and those could not interrupt the one at the same level. The 8051 by default has all interrupts set at the same level, and that was the problem in the exampel shown (as @TurboJ has correctly noted)

What would be the point of having several interrupt priority levels if this would not be the case? Concept of higher priority interrupt handler interrupting the lower priority one is called 'interrupt nesting'.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top