Search code examples
cembeddedglobal-variablesmicrochip

How to avoid global variables when using interrupt handlers?


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?


Solution

  • 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 ;
    }