A number of components make up the toolchain required to develop for MCUs. These are often hidden in a single Integrated Development Environment (IDE), where the user can write and debug software from within a single application. Several steps are often combined into a push of a button. This makes them easy to use, but it is also helpful to know a little about what is going on behind the scenes, which will be presented in the next chapter. Afterwards development on Linux will be presented before the various Windows IDEs. Setting up things on Linux is a bit more elaborate, but the payoff is a better understanding of the different components – and the software is free. The Windows solutions range from those available on Linux to robust commercial packages, that offers ease of use as well as high quality compilers, such as IAR and Keil.
Regardless if your toolchain consist of one large application or many small, the main steps performed are the same. A short introduction to the components and the terminology follows.
Figure 2.1. An overview of some of the development steps
The compilation process is fairly straight-forward. First you write your c-code in a texteditor (like Vim or WordPad) and save it with the “.c” extension. Then the compiler converts your code file into an object (“.o”). The linker then combines several objects into an executable (“.out” or “.axf”) This executable can then be converted into pure binary and flashed onto the microcontroller.
2.1 Text Editors
Text editors are what the source code is written in. Text editors range from rudimentary ones, like Notepad, to advanced editors like Vim and Emacs. Some are standalone while others are embedded in IDEs.
Compilers often consist of many steps, including optimization at several levels. Some of the most common parts of a compiler will be introduced here. Compilers are often divided into two parts: the front-end and the back-end. This is convenient in order to reuse parts of the compiler for different programming languages and different target platforms. A front-end for the C language can for instance be used for both PCs and different MCUs, but the back-end has to be changed to support the target hardware.
Figure 2.2. Compilation steps
The lexical analysis gives meaning to the input text by recognizing things like variables, numbers and keywords (for, if, while, etc.) The syntax analysis then checks if the input conforms to the grammar of the language, i.e. if the placement of the different words is correct. The semantic analysis checks if the statements are meaningful, e.g. that the types of an assignment are compatible.
2.2.2 Code generation and optimizations
By producing intermediate code, different front-ends and back-ends can be combined. High-level optimizations are performed on this intermediate representation – it is independent from the target architecture. The intermediate representation is then translated into the target assembly language and target specific optimizations are performed – here the compiler can tailor the program to best fit the hardware it will run on.
The assembler takes the assembly code and translate it into machine code, i.e. the binary instructions the target machine can read. This translation is 1-1, i.e. the two representations are equivalent. Therefore the inverse process, called disassembly, can be performed when debugging.
Each source file is usually compiled into its own file called an object file. The linker then collects these into a single binary executable, which is the finished file the target machine can run. When static linking is used, external library routines are included in the executable by the linker, the result is a standalone binary that can be run on the target machine.
Figure 2.3. Each source code file produce an object code file which are combined into one executable by the linker.
The step of downloading the executable to the MCU is called flashing the MCU. This is because the program is stored in the Flash memory of the MCU. Energy Micro’s energyAware Commander is one solution to accomplish this step.
When compiling code, you have the options of a release or debug target. The debug version allows easier debugging of the program at runtime by including debug symbols as well as performing less optimization. The debug symbols make it possible to view the original source code for the instructions that are running, while the reduced optimization makes it easier to understand the disassembly, since the most code obfuscating optimizations have not been performed. The release version on the other hand aims to produce the best performing code possible.
After the executable is downloaded to the MCU, it is ready to run. Program execution can be triggered by resetting the device. Debugging is more elaborate: it requires a debug connection to a PC and appropriate software. The payoff is the ability to see the state of the device as it is running the code.
A debug session might starts with pausing the execution right before the execution of main(). This is done by setting a breakpoint at the line of main(). Breakpoints can be set on multiple lines in the code. There are usually several ways to resume execution. The first is to continue execution until a new breakpoint is reached or the execution is paused by the user. Instructions can also be executed in groups corresponding to a single line in the source code. This is called stepping and has various forms when executing a function call. You can usually choose between executing the function as one step or to step into the function. Once there, it is possible to step line by line or to step out of the function, i.e. execute the remaining instructions in the function. When the execution is paused, you can check the state of the program; what line of the source code is the next to be run, previous and upcoming instructions that the CPU will execute as well as the contents of registers, RAM and Flash. Bugs can usually be identified by setting breakpoints at appropriate places and to assert that the contents of registers and RAM/Flash are what they should be at those points in the program.
2.6 Energy Profiling
If application requires low power consumption it is useful to use a energy profiler. An energy profiler displays not only the time used in each function as a regular profiler would, it also shows the power consumption. This is useful for the programmer to identify the power critical parts of the code to energy optimize. Energy Micro provides the energyAware Profiler for this purpose.
3 Working with EFMs
The steps introduced so far are largely independent of MCU vendor and even computer platform, some more details on working with Energy Micro’s Energy Friendly Microcontrollers are in order.
The Cortex Microcontroller Software Interface Standard (CMSIS) was developed by ARM to offer a vendor independent software interface to the MCUs using the Cortex architecture. The standard includes how to use #defines and structs to simplify register manipulation.
The emlib is written to ease the use of features specific to Energy Micro’s EFM32 MCUs. This includes configuring peripherals and initiating the different sleep modes.
SEGGER’s J-Link interface is used to communicate between the kits and PCs.
Serial Wire Debug (SWD) is used to debug the MCUs. Among other things it offers access to reading and writing register values at runtime.
The Embedded-Application Binary Interface (EABI) is a standard specifying the layout of object files and how to use runtime resources like registers and the stack. This is for instance useful because it allows code compiled by different compilers to be linked together as long as they both comply to the same EABI. ARM specifies the standard used by the Cortex architecture and hence the EFM32 MCUs.
Energy Micro University is a program developed by Energy Micro to encourage learning and to help institutions develop their own programs for teaching microcontroller development. To download learning materials in pdf, please click here: Energy Micro University Program
For technical questions about this project, please use our support forum.