ARM design on the mbed Integrated Development Environment - Part 3: More complex functionsEditor’s Note: In Part 3, excerpted from their book Fast and effective embedded systems design: Applying the ARM mbed, authors Tim Wilmshurstand Rob Toulson take the techniques for function-based program structuring used on the mbed IDE and apply them to a variety of complex functions typical of some high end ARM designs.
Large complex embedded projects in C/C++ benefit from being split into a number of different files, usually so that a number of engineers can take responsibility for different parts of the code.
This approach also improves readability and maintenance. For example, the code for
a processor in a vending machine might have one C/C++ file for the control of the actuators delivering the items and a different file for controlling the user input and liquid crystal display.
It does not make sense to combine these two code features in the same source file as they each relate to different peripheral hardware. Furthermore, if a new batch of vending machines were to be built with an updated keypad and display, only that piece of the code would need to be modified. All the other source files could be carried over without change.
Modular coding uses header files to join multiple files together. In general, a main C/C++ file (main.c or main.cpp) is used to contain the high-level code, but all functions and global variables (variables that are available to all functions) are defined in feature-specific C files. It is good practice therefore for each C/C++ feature file to have an associated header file (with a.h extension). Header files typically include declarations only, for example compiler directives, variable declarations and function prototypes.
A number of header files also exist from within C/C++, which can be used for more advanced data manipulation or arithmetic. Many of the built-in C/C++ header files are already linked to through the mbed.h header, so we do not need to worry too much about those here.
Overview of the C/C++ Program Compilation Process
To understand further the design approach to modular coding, it helps to understand the way programs are preprocessed, compiled and linked to create a binary execution file for the microprocessor. A simplified version of this process is shown in Figure 6.7 and described in detail below.
Figure 6.7: C program compile and link process
In summary, first a preprocessor looks at a particular source file and implements any preprocessor directives and associated header files. The compiler then generates an object file for the particular source code. In doing so, the compiler ensures that the source files do not contain any syntax errors - note that a program can have no syntax errors, but still be quite useless. The compiler then generates correct object code for each file and ensures that the object and library files are formatted correctly for the linker.
Object files and built-in library files are then linked together to generate an executable binary (.bin) file, which can be downloaded to the microprocessor. The linker also manages the allocation of memory for the microprocessor application and it ensures that all object files are located into memory and linked to each other correctly. In undertaking the task, the linker may uncover programming faults associated with memory allocation and capacity.
The C/C++ Preprocessor and Preprocessor Directives
The C/C++ preprocessor modifies code before the program is compiled. Preprocessor directives are denoted with a ‘#’ symbol and, may also be called ‘compiler directives’ as the preprocessor is essentially a subprocess of the compiler.
The #include (usually referred to as ‘hash-include’) directive is commonly used to tell the preprocessor to include any code or statements contained within an external header file. Indeed, we have seen this ubiquitous #include statement in every complete program example so far in this book, as this is used to connect our programs with the core mbed libraries.
Figure 6.8 below shows three header files to explain how the #include statement works. It is important to note that #include essentially just acts as a copy-and-paste feature. If we compile afile.h and bfile.h where both files also #include cfile.h, we will have two copies of the contents of cfile.h (hence variable pears will be defined twice). The compiler will therefore highlight an error, as multiple declarations of the same variables or function prototypes are not allowed.
Figure 6.8: C preprocessor example with multiple declaration error for variable ‘pears’
We can also use #define statements (usually referred to as ‘hash-define’), which allow us to use meaningful names for specific numerical values that never change. Here are some examples:
#define SAMPLEFREQUENCY 44100
#define PI 3.141592
#define MAX_SIZE 255
The preprocessor replaces #define with the actual value associated with that name, so using
#define statements does not actually increase the memory load on the processor.
The #ifndef Directive
We have just seen that multiple declarations of a variable or function prototype are not allowed. However, it is important for header files to include all the variables and features that are required for the linker to build the project successfully. It is therefore necessary to ensure that all variables and function prototypes are preprocessed on just one occasion.
When using header files it is therefore good practice to use a conditional statement to define variables and prototypes only if they have not previously been defined. For this the #ifndef directive, which means ‘if not defined’, can be used. In order to use the #ifndef directive effectively, a conditional statement based on the existence of a #define value is required. If the #define value has not previously been defined then that value and all of the header file’s variables and prototypes are defined.
If the #define value has previously been declared then the header file’s contents are not implemented by the preprocessor (as they must certainly have already been implemented). This ensures that all header file declarations are added only once to the project. The example code shown in Program Example 6.4 represents a template header file structure using the #ifndef condition, avoiding the error highlighted in Figure 6.8 earlier in this article.
Program Example 6.4: Example header file template
Page 1 of 2Next >
Currently no items