Electronic – Avoiding global variables when using interrupts in embedded systems

avrcinterruptsmicrocontroller

Is there a good way of implementing communication between an ISR and the rest of the program for an embedded system which avoids global variables?

It seems that the general pattern is to have a global variable which is shared between the ISR and the rest of the program and used as a flag, but this use of global variables goes against the grain to me. I've included a simple example using avr-libc style ISRs:

volatile uint8_t flag;

int main() {
    ...

    if (flag == 1) {
        ...
    }
    ...
}

ISR(...) {
    ...
    flag = 1;
    ...
}

I can't see away around what is essentially a scoping issue; any variables accessible by both the ISR and the rest of the program must inherently be global, surely? Despite this, I've often seen people say things along the lines of "global variables are one way of implementing communication between ISRs and the rest of the program" (emphasis mine), which seems to imply that there are other methods; if there are other methods, what are they?

Best Answer

There is a de facto standard way to do this (assuming C programming):

  • Interrupts/ISRs are low-level and should therefore only be implemented inside the driver related to the hardware that generates the interrupt. They should not be located anywhere else but inside that driver.
  • All communication with the ISR is done by the driver and the driver only. If other parts of the program needs access to that information, it has to request it from the driver through setter/getter functions or similar.
  • You should not declare "global" variables. Global meaning file scope variables with external linkage. That is: variables that could be called upon with extern keyword or simply by mistake.
  • Instead, to force private encapsulation inside the driver, all such variables shared between the driver and the ISR shall be declared static. Such a variable is not global but restricted to the file where it is declared.
  • To prevent compiler optimization issues, such variabels should also be declared as volatile. Note: this does not give atomic access or solve re-entrancy!
  • Some manner of re-entrancy mechanism is often needed in the driver, in case the ISR writes to the variable. Examples: interrupt disable, global interrupt mask, semaphore/mutex or guaranteed atomic reads.