Take the 2-minute tour ×
Electrical Engineering Stack Exchange is a question and answer site for electronics and electrical engineering professionals, students, and enthusiasts. It's 100% free, no registration required.

I started writing firmware for my product and I'm a rookie here. I went through many articles about not using global variables or functions. Is there any limit for using global variables in an 8 bit system or is it a complete 'No-No'. How should I use global variables in my system or should I completely avoid them?

I would like to take valuable advice from you guys on this topic to make my firmware more compact.

share|improve this question
    
This question isn't unique to embedded systems. A duplicate can be found here. –  Lundin Jan 29 at 14:26

6 Answers 6

up vote 24 down vote accepted

You can use global variables successfully, as long as you keep in mind @Phil's guidelines. However, here are some nice ways to avoid their issues without making the compiled code less compact.

  1. Use local static variables for persistent state that you only want to access inside one function.

    #include <stdint.h>
    void skipper()
    {
        static uint8_t skip_initial_cycles = 5;
        if (skip_initial_cycles > 0) {
            skip_initial_cycles -= 1;
            return;
        }
        /* ... */
    }
    
  2. Use a struct to keep related variables together, to make it clearer where they should be used and where not.

    struct machine_state {
         uint8_t level;
         uint8_t error_code;
    } machine_state;
    
    struct led_state {
        uint8_t red;
        uint8_t green;
        uint8_t blue;
    } led_state;
    
    void machine_change_state()
    {
        machine_state.level += 1;
        /* ... */
        /* We can easily remember not to use led_state in this function. */
    }
    
    void machine_set_io()
    {
        switch (machine_state.level) {
        case 1:
            PIN_MACHINE_IO_A = 1;
            /* ... */
        }
    }
    
  3. Use global static variables to make the variables visible only within the current C file. This prevents accidental access by code in other files due to naming conflicts.

    /* time_machine.c */
    static uint8_t current_time;
    /* ... */
    
    /* delay.c */
    static uint8_t current_time; /* A completely separate variable for this C file only. */
    /* ... */
    

As a final note, if you are modifying a global variable within an interrupt routine and reading it elsewhere:

  • Mark the variable volatile.
  • Make sure it is atomic for the CPU (i.e. 8-bit for an 8-bit CPU).

OR

  • Use a locking mechanism to protect access to the variable.
share|improve this answer
    
volatile and/or atomic vars are not going to help you avoid errors, you need some sort of lock/semaphore, or to briefly mask off interrupts when writing to the variable. –  John U Jan 22 at 10:26
    
Volatile atomic variables work perfectly in 8-bit CPUS, if they are only written in one context and only read in other contexts. Only having multiple writers requires you to use a locking mechanism or disable interrupts. Locking is usually required, however, because the relevant data is more than can fit in an atomic variable access. –  richarddonkin Jan 22 at 10:36
    
That's quite a narrow definition of "work fine". My point was that declaring something volatile does not prevent conflicts. Also, your 3rd example is not a great idea - having two separate globals with the same name is at the very least making the code harder to understand / maintain. –  John U Jan 22 at 10:47
2  
@JohnU: The normal use of volatile variables is to allow code running in one execution context to let code in another execution context know something has happened. On an 8-bit system, a buffer which is going to hold a power-of-two number of bytes no greater than 128 can be managed with one volatile byte indicating the total lifetime number of bytes put into the buffer (mod 256) and another indicating the lifetime number of bytes taken out, provided that only one execution context puts data into the buffer and only one takes data out of it. –  supercat Jan 29 at 19:02
2  
@JohnU: While it might be possible to use some form of locking or temporarily disable interrupts to manage the buffer, it really isn't necessary or helpful. If the buffer had to hold 128-255 bytes, coding would have to change slightly, and if it had to hold more than that, disabling interrupts would likely be necessary, but on an 8-bit system buffers are apt to be small; systems with bigger buffers can generally do atomic writes larger than 8 bits. –  supercat Jan 29 at 19:06

The reasons you would not want to use global variables in an 8-bit system are the same you would not want to use them in any other system: they make reasoning about the program's behavior difficult.

Only bad programmers get hung up on rules like "don't use global variables". Good programmers understand the reason behind the rules, then treat the rules more like guidelines.

Is your program easy to understand? Is its behavior predictable? Is it easy to modify parts of it without breaking other parts? If the answer to each of these questions is yes, then you are on the way to a good program.

share|improve this answer
2  
+1 for the 3rd paragraph. Well asked. Well said. –  Michael Karas Jan 22 at 4:51
1  
What @MichaelKaras said - understanding what these things mean and how they affect things (and how they can bite you) is the important thing. –  John U Jan 22 at 10:50

You shouldn't completely avoid using global variables ("globals" for short). But, you should use them judiciously. The practical problems with excessive use of globals:

  • Globals are visible throughout the compilation unit. Any code in the compilation unit can modify a global. The consequences of a modification can surface anywhere where this global is evaluated.
  • As a result globals make the code harder to read and understand. The programmer always has to keep in mind all of the places where the global is evaluated and assigned.
  • Excessive use of globals makes the code more defect-prone.

It's a good practice to add a prefix g_ to the name of global variables. For example, g_iFlags. When you see the variable with the prefix in the code, you immediately recognize that it's a global.

share|improve this answer
2  
The flag doesn't have to be a global. The ISR could call a function that has a static variable, for example. –  Phil Frost Jan 22 at 3:46
    
+1 I haven't heard about such trick before. I've removed that paragraph from the answer. How would the the static flag become visible to the main() ? Are you implying that the same function that has the static can return it to the main() later? –  Nick Alexeev Jan 22 at 3:55
    
That's one way to do it. Perhaps the function takes the new state to set, and returns the old state. There are plenty of other ways. Maybe you have one source file with a function to set the flag, and another to get it, with a static global variable holding the flag state. Though technically this is a "global" by C terminology, its scope is limited to just that file, which contains only the functions that need to know. That is, it's scope is not "global", which is really the problem. C++ provides additional encapsulation mechanisms. –  Phil Frost Jan 22 at 4:01
3  
Some methods of avoiding globals (such as accessing hardware only through device drivers) may be too inefficient for a severely resource-starved 8-bit environment. –  Spehro Pefhany Jan 22 at 4:07
1  
@SpehroPefhany Good programmers understand the reason behind the rules, then treat the rules more like guidelines. When the guidelines are in conflict, the good programer weighs the balance carefully. –  Phil Frost Jan 22 at 4:10

The advantage of global data structures in embedded work is that they are static. If every variable you need is global, then you will never accidentally run out of memory when functions are entered and space is made for them on the stack. But then, at that point why have functions? Why not one big function that handles all the logic and processes - like a BASIC program with no GOSUB allowed. If you take this idea far enough you will have a typical assembly language program from the 1970's. Efficient, and impossible to maintain and trouble shoot.

So use globals judiciously, like state variables (for instance, if every function needs to know if the system is in interpret or run state) and other data structures that need to be seen by many functions and as @PhilFrost says, is the behavior of your functions predictable? Is there a possibility to fill the stack with an input string that never ends? These are matters for algorithm design.

Note that static has different meaning inside and outside a function. http://stackoverflow.com/questions/5868947/difference-between-static-variable-inside-and-outside-of-a-function

http://stackoverflow.com/questions/5033627/static-variable-inside-of-a-function-in-c

share|improve this answer
    
Many embedded-systems compilers allocate automatic variables statically, but overlay variables used by functions which cannot be in scope simultaneously; this generally yields memory usage which is equal to the worst-case-possible usage for a stack-based system in which all statically-possible call sequences may in fact occur. –  supercat Jan 22 at 18:01

Global variables should only be used for truly global state. Using a global variable to represent something like e.g. the latitude of the northern boundary of the map will only work if there can only ever be one "northern boundary of the map". If in future the code might have to work with multiple maps that have different northern boundaries, code which uses a global variable for the northern boundary will likely need to be reworked.

In typical computer applications, there's often no particular reason to assume there will never be more than one of something. In embedded systems, however, such assumptions are often far more reasonable. While it's possible that a typical computer program might be called upon to support multiple simultaneous users, the user interface of a typical embedded system will be designed for operation by a single user interacting with its buttons and display. As such, it will at any moment in time have a single user-interface state. Designing the system so that multiple users could interact with multiple keyboards and displays would require a lot more complexity, and take much longer to implement, than designing it for a single user. If the system is never called upon to support multiple users, any extra effort invested to facilitate such usage will be wasted. Unless it's likely that multi-user support will be required, it would likely be wiser to risk having to discard the code used for a single-user interface in the event that multi-user support is required, than to spend extra time adding multi-user support which will likely never be needed.

A related factor with embedded systems is that in many cases (especially involving user interfaces), the only practical way to support having more than one of something would be to use multiple threads. In the absence of some other need for multi-threading, it's likely better to use a simple single-threaded design than increase the system complexity with multi-threading that is likely never to really be necessary. If adding more than one of something would require a huge system redesign anyway, it won't matter if it also requires reworking the use of some global variables.

share|improve this answer
    
So keeping global variables that won't clash with each other won't be a problem right. eg: Day_cntr , week_cntr etc for counting days and week respectively.And I trust one should not deliberately use much of global variables that resemble same purpose and must clearly define those.Thanks a lot for the overwhelming response.:) –  Rookie91 Jan 23 at 4:40

Lots of people are confused about this subject. The definition of a global variable is:

Something which is accessible from everywhere in your program.

This is not the same thing as file scope variables, which are declared by the keyword static. Those are not global variables, they are local private variables.

 int x; // global variable
 static int y; // file scope variable

 void some_func (void) {...} // added to demonstrate that the variables above are at file scope.

Should you use global variables? There are a few cases where it is fine:

In every other case, you shall not use global variables, ever. There is never a reason to do so. Instead use file scope variables, which is perfectly fine.

You should strive to write independent, autonomous code modules designed to do a specific task. Inside those modules, internal file scope variables should reside as private data members. This design method is known as object-orientation and widely acknowledged as good design.

share|improve this answer
    
Why the downvote? –  m.Alin Jan 29 at 13:28
    
I think "global variable" can also be used to describe allocations to a global segment (not text, stack, or heap). In that sense file static and function static variables are/can be "global". In the context of this question, it is somewhat clear that global is referring to scope not allocation segment (though it is possible the OP did not know this). –  Paul A. Clayton Jan 29 at 14:14
1  
@PaulA.Clayton I've never heard of a formal term called "global memory segment". Your variables will end up one of the following places: registers or stack (runtime allocation), heap (runtime allocation), .data segment (explicitly initialized static storage variables), .bss segment (zeroed static storage variables), .rodata (read-only constants) or .text (part of the code). If they end up anywhere else, that's some project-specific setting. –  Lundin Jan 29 at 14:22
1  
@PaulA.Clayton I suspect that what you refer to as "global segment" is the .data segment. –  Lundin Jan 29 at 14:24

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.