The architecture of an embedded system is centered around its microcontroller, also sometimes referred to as the microcontroller unit (MCU), typically a single integrated circuit containing the processor, RAM, flash memory, serial receivers and transmitters, and other core components. The market offers many different choices among architectures, vendors, price range, features, and integrated resources. These are typically designed to be inexpensive, low-resource, low-energy consuming, self-contained systems on a single integrated circuit, which is the reason why they are often referred to as System-on-Chip (SoC).
Due to the variety of processors, memories, and interfaces that can be integrated, there is no actual reference architecture for microcontrollers. Nevertheless, some architectural elements are common across a wide range of models and brands, and even across different processor architectures.
Some microcontrollers are dedicated to specific applications and exposing a particular set of interfaces to communicate to peripherals and to the outside world. Others are focused on providing solutions with reduced hardware costs, or with very limited energy consumption. Nevertheless, the following set of components is hardcoded into almost every microcontroller:
- Microprocessor
- RAM
- Flash memory
- Serial transceivers
Additionally, more and more devices are capable of accessing a network, to communicate with other devices and gateways. Some microcontrollers may provide either well-established standards, such as Ethernet or Wi-Fi interfaces, or specific protocols specifically designed to meet the constraints of embedded systems, such as Sub-GHz radio interfaces or Controller Area Network (CAN) bus, being partially or fully implemented within the IC.
All the components must share a bus line with the processor, which is responsible for coordinating the logic. The RAM, flash memory, and control registers of the transceivers are all mapped in the same physical address space:
A simplified block diagram of the components inside a generic microcontroller
The addresses where the RAM and flash are mapped to depend on the specific model, and are usually provided in the datasheet. A microcontroller is able to run code in its own native machine language, a sequence of instructions conveyed into a binary file that is specific for the architecture it is running on. By default, compilers provide a generic executable file as the output of the compilation and assembly operations, which needs to be converted into the format that is executable for the target.
The processor is designed to execute the instructions stored, in its own specific binary format directly from RAM as well as from its internal flash memory, usually mapped starting from position zero in memory, or from another well-known address specified in the microcontroller manual. The CPU can fetch and execute code from RAM faster, but the final firmware is stored in the flash memory, which is usually bigger than the RAM on almost all microcontrollers, and permits it to retain the data across power cycles and reboots.
Compiling a software operating environment for an embedded microcontroller and loading it onto the flash memory requires a host machine, which is a specific set of hardware and software tools. Some knowledge about the target device's characteristics is also needed to instruct the compiler to organize the symbols within the executable image. For many valid reasons, C is the most popular language in embedded software, although not the only available option. Higher-level languages, such as Rust and C++, can produce embedded code when combined with a specific embedded runtime, or even in some cases by entirely removing the runtime support from the language. This book will focus entirely on C code, because it abstracts less than any other high-level language, thus making easier to describe the behavior of the underlying hardware while looking at the code.
All modern embedded systems platforms also have at least one mechanism (such as JTAG) for debugging purposes, and to upload the software to the flash. When the debug interface is accessed from the host machine, a debugger can interact with the breakpoint unit in the processor, interrupting and resuming the execution, and can also read and write from any address in memory.
A significant part of embedded programming is the communication with the peripherals, using the interfaces that the MCU exposes. Embedded software development requires basic knowledge of electronics, the ability to understand schematics and datasheets, and confidence with the measurement tools, such as logic analyzers or oscilloscopes.