To start working with an embedded system, we need to set up an environment. Unlike the environment we use for desktop development, the environment for embedded programming requires two systems:
- A build system: The system you use to write the code
- A target system: The system your code is going to be run on
In this chapter, we will learn how to set up these two systems and connect them together. Configurations of build systems may vary significantly— there may be different operating systems, compilers, and IDEs. The variance in target system configurations is even greater since each embedded system is unique. Moreover, while you can use your laptop or desktop as a build system, you do need some sort of embedded board as a target system.
It would be impossible to cover all the possible combinations of build and target systems. Instead...
In this recipe, we will set up a Docker container to run Ubuntu 18.04 on your desktop or laptop. It does not matter what operating system runs on your machine, as Docker supports Windows, macOS, and Linux. As a result of this recipe, you will have a unified, virtualized Ubuntu Linux build system running within your host operating system.
If your operating system already runs Ubuntu Linux, feel free to skip to the next recipe.
We are going to install the Docker application on our laptop or desktop and then use a ready-made image of Ubuntu to run this operating system in a virtual environment:
- In your web browser, open the following link and follow...
Using a real embedded board is not always possible or practical—hardware is not yet ready, or the number of boards is limited. Emulators help developers use an environment that's as close to the target system as possible, yet do not depend on hardware availability. It is also the best way to start learning embedded development.
In this recipe, we will learn how to set up QEMU (a hardware emulator) and configure it to emulate an ARM-based embedded system running Debian Linux.
We need a virtual environment that, unlike Docker, can emulate processors with architectures that differ from the architecture of our computer:
- Navigate to https://www.qemu.org/download/ and click...
We have already learned that the environment for embedded development consists of two systems: the build system, where you write and build code, and the host system, which runs the code.
We now have two virtualized environments set up:
- Ubuntu Linux in a Docker container, which will be our build system
- QEMU running Raspbian Linux, which will be our host system
In this recipe, we will set up the cross-compilation tools required to build Linux applications for the ARM platform and build a simple Hello, world! application to test the setup.
To set up the cross-compilation toolkit, we will need to use Ubuntu Linux, which we set up in the Setting up the build system in a Docker container...
After an embedded application is built on a build system using a cross-compiler, it should be transferred to the target system. The best way to do this on Linux-based embedded systems is by using networking connectivity and a remote shell. Secure Shell (SSH) is widely used due to its security and versatility. It allows you to not only run shell commands on a remote host but also copy files from one machine to another using cryptographic encryption and key-based authentication.
In this recipe, we will learn how to copy the application binary to the emulated ARM system using secure copy, connect to it using SSH, and run the executable in SSH.
We will use the Raspberry Pi emulator...
Debugging embedded applications depends significantly on the type of the target embedded systems. Microcontroller manufacturers often provide specialized debuggers for their microcontroller units (MCUs) as well as hardware support for remote debugging using a Joint Test Action Group (JTAG) protocol. It allows developers to debug the microcontroller code immediately after the MCU starts executing instructions.
If the target board runs Linux, the most practical method of debugging is to use an extensive debug output and to use GDB as an interactive debugger.
In this recipe, we will learn how to run our application in a command-line debugger: GDB.
As we have discussed, the environment for embedded development usually involves two systems—a build system and a target system (or emulator). Sometimes, interactive debugging on the target system is impractical because of the high latency of remote communication.
In such situations, developers can use remote debugging support provided by GDB. In this setup, an embedded application is launched on the target system using gdbserver. Developers run GDB on a build system and connect to gdbserver over the network.
In this recipe, we will learn how to start debugging an application using GDB and gdbserver.
In the Connecting to the embedded system recipe, we...
In the previous recipes, we learned how to compile a program that consists of one C++ file. Real applications, however, usually have a more complex structure. They can contain multiple source files, depend on other libraries, and be split into independent projects.
We need a way to conveniently define build rules for any type of application. CMake is one of the most well-known and widely used tools that allow developers to define high-level rules and translate them into a lower-level build system, such as a Unix make.
In this recipe, we will learn how to set up CMake and create a simple project definition for our Hello, world! application.
As discussed earlier, a common embedded...