0

I am building a BLE-based application for a STM32WB and the problem I have is that I have two programs, main.c and custom_stm.c and I want to use the same variable in both (the second program continuously updates it).

The main.c program is located in Core\Src\main.c and the custom_stm.c program is at STM32_WPAN\App\custom_stm.c within the same folder.

I tried declaring them in these ways:

main.c

extern uint8_t a=0;

and for the program which updates the value:

custom_stm.c

uint8_t a;

but I get a compiler error.

How could I overcome this issue?

Velvet
  • 4,299
  • 4
  • 14
  • 32
Theodor
  • 65
  • 6

3 Answers3

2
  1. Global variables can be declared many times but defined only once.
  2. The global variable should be defined in the *.c file that it is associated with.
  3. Variables defined within a function cannot be global.
  4. Variables defined within grouping braces {} cannot be global
  5. Use a header file to distribute the global variable to other files.

For example:

In custom_stm.c a global variable is defined outside any function. The variable then has file scope (global within the file):

uint8_t a=0;

In the associated header file custom_stm.h the line

extern uint8_t a;

is a declaration that will allow other files that include the header file to use the variable as a global variable like this in main.c:

#include "custom_stm.h"

int main(void) { --- some code a=5; --- some code }

RussellH
  • 15,016
  • 2
  • 10
  • 38
0

To share a variable between two source files (main.c and custom_stm.c), you need to ensure proper declaration and definition. Here's how you can do it:

In main.c (located in Core\Src\main.c):

// Declare the variable as external
extern uint8_t a;

In custom_stm.c (located in STM32_WPAN\App\custom_stm.c):

// Define the variable
#include "main.h" // Include the header file where the variable is declared
uint8_t a=0;

Make sure to include the proper header file (main.h or any other header where uint8_t a; is declared) in custom_stm.c. This ensures that custom_stm.c knows about the declaration of a in main.c.

Then, whenever you modify a in custom_stm.c, those changes will be reflected in main.c as well, given that a is declared as extern in main.c.

Theodor
  • 65
  • 6
0

"Global variables" (variables with external linkage) should be avoided in C programs.

There are a few rare exceptions to this rule, such as when implementing a hardware register map or perhaps when dealing with memory mapping into data flash or some other form of non-standard memory. But in the 99.99% of all use-cases you can think of, global variables is not the answer. In beginner-level programs, global variables should never be used for sure.

Because they come with the following severe and serious problems:

  • Tight coupling between unrelated parts of the code. You are creating dependencies between different parts of the code which do not make sense. In proper program design, every module should do it's designated task and not worry about anything else. Dependencies between different modules should be handled through functions, not with direct access to variables.

    For example, your LCD driver may have a dependency as it uses your SPI driver. But if your SPI driver in turn requires the LCD driver, something has gone terribly wrong in the program design. How some LCD happens to work is no business of a SPI driver.

  • "Spaghetti programming". It is very hard to read code where various state variables are updated literally everywhere, in multiple files. It will be difficult to get a sense over when a variable is updated from where, or whom is responsible for the variable.

  • Namespace clutter. The exposed variable will be visible to all manner of unrelated parts of the code, meaning that there could be naming collisions or even accidental access to the variable. Or some module accesses the variable on purpose, while the internal workings of your code module is really none of their business. Good programming practice is therefore to reduce the scope of all variables as much as possible.

const qualified "global" variables are generally a less serious design mistake than read/write ones, since they don't have the issues of spaghetti programming. So make everything that should be read-only const, which you probably want to do anyway to get it allocated in flash and not RAM.


Work-arounds:

To avoid all of the above issues, a proper program design technique called private encapsulation is used. This involves restricting the access of variables to the module where they are declared.

Normal embedded system C design uses static variables to solve this. They can still be declared at file scope and remain accessible to the module where they reside, but not to the outside world. If the outside world need to know the data they contain, that is done through so-called "setter/getter" functions. Example:

spi.c

#include "spi.h"

static uint32_t baudrate;

void spi_init (uint32_t baud) { baudrate = baud;

/* register setup code */ }

void spi_set_baudrate (uint32_t baud) { baudrate = baud;

/* calculate register values and update them */ }

uint32_t spi_get_baudrate (void) { return baudrate; }

spi.h

#ifndef SPI_H
#define SPI_H

/* document this function here */
void spi_init (uint32_t baud);

/* document this function here */ void spi_set_baudrate (uint32_t baud);

/* document this function here */ uint32_t spi_get_baudrate (void);

#endif

This is how the vast majority of code in normal "bare metal" microcontroller programs is designed.

Similarly, when using interrupts, variables communicating with the ISR should be kept local and static as described here: Avoiding global variables when using interrupts in embedded systems


There are a few problems even with static file scope variables too. They are not re-entrant, so even when only accessing them through setters/getters, that might not be ideal for code using interrupts or for multi-process RTOS applications.

They also block the code from having multiple instances. For example, the SPI driver above, as written, will only work for one SPI hardware peripheral. But what if you have several identical ones in the same MCU? Using arrays of the same kind of variables internally might be one solution.

But in general, for more intricate designs when you work with lots of data, or when you need to declare multiple instances of something and still maintain private encapsulation, you can use the design pattern known as "opaque type" - How to do private encapsulation in C?

This comes with a little bit of overhead, so one has to be sensible when to use it. For example I often use opaque type when implementing CAN bus drivers/HALs, since these tend to be rather complex and with various message buffer structures that may need to be allocated on the caller-side. On the other hand, the caller shouldn't need to know how the message buffers are laid out in memory in order to use the CAN driver.

Lundin
  • 20,162
  • 1
  • 26
  • 76