I have heard many times that global variables can be "dangerous" if not used cautiously. And that one way of protecting globals (or indicate reader to which section of code those relate to) is to use struct and put all globals inside as members.
With term "globals protection" I refer to not being able to accidentally modify value of global from some xxx-source file. With struct, you clarify to reader (or yourself) where members of this global struct might be used and therefore there is less uncertainty about variable functionality.
I tried to implement this on my project where I thought this was a good idea since I plan to use more ISRs in future. In order to prevent unclear intentions or buggy code, used upper method of global variables protection. But came to conclusion that new organization of variables looks even worse if one has to access struct inside struct, as code readability is decreased for great portion (my opinion).
Here is snippet of code I use for button debouncing. XORing two members looks like a nasty long "snake" but could be written as change = new ^ old
if certain variable organization (inside structures) wouldn't be needed.
/*** HEADER FILE ***/
/** Button state structure **/
typedef struct {
uint16_t old;
uint16_t new;
uint16_t change;
} state;
/** Protect ISR globals within structure **/
typedef struct {
state buttonState;
uint16_t isPortStable;
uint16_t countStableStates[BTN_COUNT];
uint32_t tick;
} debounceISR;
debounceISR DB; // !! Keep struct name short to affect readability as little as possible !!
/*** SOURCE FILE ***/
/* Read keyboard and check for transitions */
DB.buttonState.new = BTN_PORT;
DB.buttonState.change = DB.buttonState.new ^ DB.buttonState.old;
DB.buttonState.old = DB.buttonState.new;
On the long run I want to prevent global variables to interfere without user (coder) being aware and cause complicated problems as number of ISRs (and therefore globals) increase in one project.
Are there any alternative options to such needs? Should I tend to use pointer and static variables in order to avoid globals when using ISRs?
CodePudding user response:
Read https://www.embedded.com/a-pox-on-globals.
Your starting premise is a fallacy, you still have a global, it just happens to be a structure. It is insufficient to suspect globals are "bad", you have to understand what the problems are and how to solve them. It is not one single problem, and they cannot be solved by one single technique, and in this case, the technique you propose is not a solution at all.
The ISR shared and state data objects should be declared static in the same source file as the ISR itself (in a structure if you wish and that makes sense - i.e. the variables are related). If the data objects are shared, then the same source should include accessor functions, and it is the accessors that you declare in the header, not the data objects.
The accessors can then include code to deal with data consistency and atomic access, as well as imposing read/write restrictions and range checking etc.
Moreover when you need to debug access to any shared data object, the accessor gives you a single place to put a breakpoint to catch any and all access.
As well as the A Pox in Globals article above, you might also read https://www.embedded.com/introduction-to-the-volatile-keyword/. Your ISR shared data must be declared volatile
at the very least.