Chapter 1, Fundamentals of Embedded Systems, defines what embedded systems are, how they are different from other systems, why specific programming techniques are needed, and why C++ is good and in many cases the best choice for embedded development. It outlines the constraints and challenges that embedded developers encounter in their everyday work: limited system resources and CPU performance, dealing with hardware errors, and remote debugging.
Chapter 2, Setting Up the Environment, explains the differences in a development environment for embedded systems compared to web or desktop application development and goes through concepts of the build and target system, cross-compilation and cross-toolkits, the serial console, and the remote shell. It provides practical steps for setting up virtualized build and target hosts for the most common desktop configurations running Windows, macOS, or Linux.
Chapter 3, Working with Different Architectures, explains how to take into account important differences in CPU architectures and memory configuration of target systems in your C++ code.
Chapter 4, Handling Interrupts, covers the low-level concepts of interrupts and interrupt service routines. In modern OSes, even developers or device drivers have to use a higher-level API provided by the OS. That is why we explore the interrupt techniques using the 8051 microcontroller.
Chapter 5, Debugging, Logging, and Profiling, covers debugging techniques specific to Linux-based embedded systems, such as running gdb directly on the target board, setting up gdbserver for remote debugging, and the importance of logging for debugging and failure root cause analysis.
Chapter 6, Memory Management, provides several recipes and best practices of memory allocation that will be helpful for developers of embedded systems. We discuss why dynamic memory allocation is avoided in embedded applications and what alternatives can be considered for fast, deterministic memory allocation.
Chapter 7, Multithreading and Synchronization, explains how to use the functions and classes provided by the standard library of C++ to implement efficient multithreading applications that can utilize all the power of the modern multicore CPUs.
Chapter 8, Communication and Serialization, covers the concepts, challenges, and best practices for inter-process and inter-system communications, such as sockets, pipes, shared memory, and memory-efficient serialization using the FlatBuffers library. Decoupling applications into independent components that talk to each other using well-defined asynchronous protocols is a de facto standard way of scaling a software system while keeping it fast and fault-tolerant.
Chapter 9, Peripherals, explains how to work with various peripheral devices in C++ programs. Though most device communication APIs do not depend on a particular programming language, we will learn how to use the power of C++ to write wrappers that are convenient for developers and help prevent common resource leaking errors.
Chapter 10, Reducing Power Consumption, explores the best practices for writing energy-efficient applications and utilizing the power management functions of the OS. It provides several practical recipes for Linux-based embedded systems, but the same concepts can be expanded to any OS and any platform.
Chapter 11, Time Points and Intervals, covers various topics related to time manipulations, from measuring intervals to adding delays. We will learn about the API provided by the standard C++ Chrono library and how it can be used efficiently to build portable embedded applications.
Chapter 12, Error Handling and Fault Tolerance, explores possible implementations and best practices of error handling for embedded applications written in C++. It explains how to use C++ exceptions efficiently and compares it to alternatives such as traditional error codes and complex return types. It touches on basic fault-tolerance mechanisms such as watchdog timers and heartbeats.
Chapter 13, Guidelines for Real-Time Systems, covers the specifics of real-time systems. It briefly describes how real-time systems are defined and what kinds of real-time systems exist. It contains practical recipes on how to make the behavior of applications more deterministic, a crucial requirement for real-time systems.
Chapter 14, Guidelines for Safety-Critical Systems, explains what safety-critical systems are and how they are different from other embedded systems. It covers development methodologies and tools that are required when working on safety-critical systems, from following formalized coding guidelines such as MISRA, AUTOSAR, or JSF to using static code analysis or formal software validation tools.
Chapter 15, Microcontroller Programming, outlines basic concepts of writing, compiling, and debugging C++ code for microcontrollers. We will learn how to set up the development environment using the widely used Arduino board as an example.