Avoiding global variables when using interrupts?

Maybe more of a general C++ question. In general it’s supposed to be best practice to avoid global variables when possible. I’m usually the only person writing code for projects so I always thought a global here or there was fine if it made things easier. As projects have gotten more complicated I’ve found ways to avoid globals, but there’s one glaring exception.

When I use ticker or pin interrupts they are always communicating with the main function. Are there common strategies to do this without global variables?

2 Likes

Using callbacks is the best way I’ve seen so far. One particular use of them is to allow you to call class functions from interrupt handlers, allowing you to use the class resources in the resulting function.
A very minimal example of the format I use commonly:

class someClass {
  public:
    void attachTicker();
    ...
  private:
    void _readCB();
    Ticker _tick;
    uint8_t aClassVariable;
    ....
}

void someClass::attachTicker() {
    _tick.attach(callback(this,&someClass::_readCB),0.1);
}

void someClass::_readCB() {
 //do something with aClassVariable
}

From this example someClass can be extended to have supporting functions used by the main function and others to check states, or register/call callbacks and act accordingly.

It’s a useful pattern. I don’t know if it’s the most efficient way to do things, but it’s definitely been working for me, and produces relatively neat looking code.

1 Like

I was hoping there was a way around classes. haha.
Whenever I write a class instead of functions and look at the code 6 months later I have trouble remembering how it works. This does look pretty straight forward though. Thank you.

I know the feeling. My training was in C, so I was exactly like that when trying to teach myself to work with them. They’re indispensable now though…

Another option that would be worth considering is Mbed’s Mail object. This is purpose built to aid in synchronising threads, and passing info between them. The only problem with the RTOS functions is that AFAIK when called from an ISR they don’t actually act, and instead queue the action to happen on return to normal context (via EventQueue and similar). It can cause some minor problems if you need exact timing, or are trying to pass to another interrupt which follows immediately, but as long as you give the RTOS the time it needs to do its thing, it does the job neatly.

You end up with a single (doesn’t have to be though) global Mail object that all your ISR’s/Threads post to, and your main looks at the available messages in the Mail(box) and hands them out to it’s functions as needed.

1 Like

I’m way behind the curve. The only formal education I had was in BASIC and it was very um… basic. I learned more earlier as a kid scrolling through the memory of Commodore 64’s memory in hex to try to hack video games for extra lives and more inventory items. I had some friends who wrote in assembly and I didn’t know there was anything in between.
About 7 years ago I discovered Arduino which was much more accessible than the Microchip PIC stuff I tried a couple years earlier. It got me started, but support for anything learning beyond the basics was not easy. Often the solution to common problems is in the middle of a 30 page forum discussion and it’s hard to filter the bad advice from the good. I’ve definitely learned more and developed better habits using Mbed.
Thanks for the heads up on the RTOS option. I’m not sure I’ve wrapped my head around RTOS completely. So far haven’t found found it was necessary. Most of the stuff I do is relatively simple, but timing us usually critical. When I’ve glanced at RTOS as a solution it seems like it needs a lot of pre-planning. I’m still dumb enough that I run into at least a couple unanticipated problems in every project that should have been pretty obvious before I wrote a line of code.
Anyway, thanks again for the help.