How to avoid global variables when using interrupt handlers?

16.5k views Asked by At

I'm mostly self taught in C. I program embedded micro controllers. (dsPIC33fj128gp804 for example) I generally use global variable and everything I've ever read denounces using global variables like they are a plague. I've been working on using less but there is a scenario that i don't know how not to use global variables.

The micro controller is equipped with interrupts. An interrupt is an event triggered externally in hardware. When the interrupts is triggered the execution of the main code stops, the current working variables are saved, a preassigned function is executed and then the main code picks back up where it left off. Because the interrupt is a stand alone function that can trigger at any time nothing can be passed into or out of the function.

For example when the UART hardware receives a byte of data, that data needs moved out of the hardware buffer before it gets over written.

void __attribute__((interrupt, no_auto_psv)) _U2RXInterrupt(void)
{
    GlobalVariable = U2RXREG; // Move data to global variable
    IFS4bits.U2RXIF = 0; // Clear the UART2 Receive Interrupt Flag
}

Is there a way to do this without global variables or is this an exception?

2

There are 2 answers

0
Clifford On BEST ANSWER

You should distinguish between a global variable with external linkage, and a file scope static variable. You can solve your problem with the latter.

static volatile int shared_variable ;

int getShared(){ return shared_variable ; }

static void isr_handler()
{
    shared_variable++ ;
}

So in the above example, the only access to the shared variable external to the translation unit is via the access function getShared(). This method of course relies on using separate compilation, but that is no bad thing for many reasons.

For other techniques for avoiding globals, and explanations on why you should do so, see Jack Ganssle's A Pox on Globals

Another thing to consider, and a reason why globals are particularly problematic in this instance is that the shared variable must be either atomic or accessed in a critical section. For example, on a 16 bit dsPIC, 32 bit accesses are not atomic, in which case the necessary protection can be placed in the access function, whereas if it were global, every access would have to be individually protected:

For example:

static volatile uint32_t shared_variable ;

int getShared()
{ 
    uint32_t ret ;

    _disable_interrupts() ;
    ret = shared_variable ;
    _enable_interrupts() ;

    return ret ;
}
1
bassplayer142 On

Use static globals as much as possible so that the variable is only in scope in that particular source file. Use static variables declared in functions that use them for even better isolation.

Use volatile for all variables used in both Interrupt routines and main code loop.

Note that being volatile does NOT mean that you are "safely" sharing this variable between the ISR and main code. It is NOT guaranteed to be Atomic access which is when the variable is accessed with a single CPU instruction. For instance a 16-bit variable on an 8 bit micro will take multiple read instructions to read the value. If the interrupt fires in between you will have corrupted 16-bit data because only half of the variable has been read. The first 8bit before the ISR and the other 8bit after the ISR returns. This is bad data which can cause huge problems if pointers are involved rather than just passing a ADC data value for example. This can lead to stackoverflow.

A simple access should quickly disable interrupts, read the value, and renable them to ensure serialized access.

With small embedded systems using static globals is in my opinion a great way to go as long as you keep it to a minimum and straight to the point! Use structures also to further break down globals into less.

Globals are only "evil" when you have too many and are accessing them back and forth over many files. It just gets very messy and you can easily create another variable that has the same name as another existing global. Not good.