Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 731
vini_i Avatar asked Nov 29 '14 16:11

vini_i


People also ask

How do you avoid global variables?

The simplest way to avoid globals all together is to simply pass your variables using function arguments. As you can see, the $productData array from the controller (via HTTP request) goes through different layer: The controller receives the HTTP request. The parameters are passed to the model.

Which of the following should be avoided in interrupt handler?

Regardless of the model, I advise you to be parsimonious and to avoid stack allocations inside your interrupt handler. In most cases, there should be little-to-no processing inside of the handler.

How can parameters be used to avoid the use of global variables?

Parameter passing - allows the values of local variables within the main program to be passed to sub-programs without the need to use global variables. The value of these variables (or a copy of the value of these variables) is passed as a parameter to and from sub-programs as necessary.

When a global variable may be modified by an exception handler it should be declared as?

If the reason you have a global variable is that some routine other than the interrupt handler uses the value, then you need to declare the global volatile (as well as extern).


2 Answers

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 ;
}
like image 70
Clifford Avatar answered Oct 03 '22 09:10

Clifford


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.

like image 43
bassplayer142 Avatar answered Oct 03 '22 10:10

bassplayer142