Embedded Systems
Microcontroller architecture, real-time firmware, and embedded software design patterns.
5 subtopics · 66 pages
Embedded systems are purpose-built computing systems that perform a dedicated function within a larger product. Unlike general-purpose computers, an embedded system typically runs one application continuously, often in real time, with constrained resources — limited memory, restricted processing power, and tight power budgets. The field spans everything from a battery-powered sensor node to a high-speed motor controller.
What Are Embedded Systems?
An embedded system consists of a microcontroller or microprocessor, firmware running directly on that processor, and peripheral hardware — sensors, actuators, communication interfaces, and power management. The firmware has direct access to hardware registers and must handle real-time events, manage limited resources, and often operate for years without intervention.
The discipline encompasses:
- Microcontroller selection — choosing the right processor for the application's performance, power, and interface requirements.
- Peripheral configuration — setting up GPIO, timers, ADCs, DACs, SPI, I2C, UART, USB, and other on-chip peripherals.
- Firmware architecture — deciding between bare-metal, RTOS-based, or event-driven approaches.
- Interrupt handling — responding to hardware events with deterministic timing.
- Debugging and validation — using JTAG/SWD debuggers, logic analysers, and oscilloscopes to verify firmware and hardware behaviour.
Why Embedded Systems Engineering Matters
Most electronic products are embedded systems. Getting the firmware and hardware interface right determines whether the product is reliable, power-efficient, responsive, and manufacturable at cost. A microcontroller choice that fits the prototype may be wrong for production (availability, cost, thermal); a firmware architecture that works on a workbench may fail under real-world interrupt loads.
Common challenges in embedded development:
- Real-time correctness — missing a deadline in a motor controller or safety system is not a software bug; it is a hardware failure.
- Power management — battery-powered IoT devices must carefully manage sleep states, wake sources, and peripheral power sequencing.
- Hardware–software co-design — the firmware developer must understand the hardware (schematic, PCB layout, component datasheets) to configure peripherals correctly and debug unexpected behaviour.
- Update and bootloader strategy — field-deployed products need a safe mechanism to update firmware without bricking the device.
Key Concepts
- Microcontroller (MCU) — a single-chip device integrating a processor core, memory (flash and RAM), and peripherals. Common families include STM32, ESP32, nRF52, and RP2040.
- RTOS (Real-Time Operating System) — a lightweight operating system that provides task scheduling, inter-task communication, and deterministic timing guarantees. FreeRTOS is the most widely used open-source RTOS.
- Bare-metal programming — writing firmware that runs directly on the hardware without an RTOS, relying on interrupts and a main loop for scheduling.
- GPIO (General Purpose Input/Output) — configurable digital pins used for inputs (buttons, encoders, digital sensors) and outputs (LEDs, motor drives, relay control).
- Interrupt — a hardware signal that suspends the current execution context to run an interrupt service routine (ISR), then returns to the original context.
- JTAG/SWD — debug interfaces that allow a host debugger to set breakpoints, inspect registers, and step through firmware execution on-target.
- HAL (Hardware Abstraction Layer) — firmware library code that abstracts peripheral registers behind a consistent API, making code more portable across MCU variants.
Common Tools and Software
- IDEs and build tools — STM32CubeIDE (STM32, free and official), VS Code with platform-specific extensions (multi-platform, widely used), IAR EWARM and Keil MDK (commercial, common in professional and safety-critical projects), Eclipse with GNU Arm Embedded Toolchain.
- Debug probes — J-Link (SEGGER, industry standard, supported by most platforms), ST-Link V3 (STM32-specific, inexpensive, built into Nucleo/Discovery boards), CMSIS-DAP compatible adapters (open-source, lower cost), OpenOCD (open-source debug server compatible with most probes and targets).
- RTOS — FreeRTOS (most widely deployed, included in STM32Cube and ESP-IDF), Zephyr (used by Nordic nRF Connect SDK, increasingly adopted across platforms), Azure RTOS/ThreadX (commercial, used in medical and safety applications).
- Logic analysers and oscilloscopes — Saleae Logic (USB, excellent protocol decode), Rigol DS/MSO series (entry-level benchtop); both are useful for verifying peripheral signals, timing, and catching hardware/firmware interaction bugs.
Common Mistakes
- Choosing the MCU first, requirements second — microcontroller selection should follow requirements (peripheral count, power budget, compute, connectivity, supply chain). A mismatched MCU forces workarounds throughout the project or a costly respin.
- Blocking in interrupt service routines — ISRs must be short and non-blocking. Long ISRs degrade real-time performance, cause priority inversion in RTOS systems, and are a common source of hard-to-reproduce bugs.
- Not enabling the watchdog from the start — the watchdog is a recovery mechanism, but adding it late in development can unmask previously hidden firmware hangs in ways that are difficult to debug. Enable it early and ensure the firmware's main control flow reliably services it.
- Skipping a hardware debug probe setup — relying solely on UART printf for debugging is significantly slower than on-target debugging with breakpoints and register inspection. A JTAG/SWD probe configured at the start of a project returns the investment within the first debug session.
- Deferring power management to the end — sleep modes, wake sources, and peripheral power sequencing are firmware architecture decisions. Retrofitting them into firmware designed for continuous operation is expensive. Budget sleep behaviour into the architecture before writing the first line of application code.
Common Questions
What is the difference between a microcontroller and a microprocessor?
A microcontroller integrates processor core, flash memory, RAM, and peripherals on a single chip, and is designed to run one application directly from its own flash. A microprocessor is a processor-only chip that requires external memory and peripherals, and typically runs an operating system loaded from external storage. For most embedded products, a microcontroller is the right choice; a microprocessor (such as the Raspberry Pi's Broadcom SoC running Linux) is appropriate when you need a full OS, rich connectivity, or significant compute power.
Should I use bare-metal or an RTOS?
For simple applications with one or two concurrent tasks, bare-metal with interrupts is simpler and more predictable. An RTOS adds value when you have three or more concurrent tasks with different priorities, need stack isolation between tasks, or require deterministic inter-task communication. The overhead of FreeRTOS on a modern Cortex-M4 is typically a few KB of flash and a small fraction of CPU cycles — it is rarely the bottleneck. See the full comparison for the complete decision framework.
How do I get started with a new MCU platform?
Start with an evaluation board and the manufacturer's SDK or HAL. Configure one peripheral at a time and verify its behaviour on hardware before adding complexity. Set up a debug probe (J-Link, ST-Link, or CMSIS-DAP compatible) from the beginning — debugging via printf alone is much slower than on-target debugging. Zeus Design's firmware development team can accelerate new platform bringup for commercial projects.
Knowledge Base
Microcontrollers and Firmware Fundamentals
- What Is a Microcontroller? — architecture, memory map, peripherals, and how an MCU differs from a microprocessor
- How Do You Choose a Microcontroller? — evaluating processor core, memory, peripherals, power, ecosystem, and availability
- What Are Interrupts in Embedded Systems? — how interrupts work, ISR constraints, and priority configuration
Peripheral Configuration
- How Do GPIO Pins Work? — input/output configuration, push-pull vs open-drain, pull-ups, alternate function routing
- PWM Explained: Frequency, Duty Cycle, Dead-Time, and Hardware Timers — duty cycle, frequency, resolution, dead-time for H-bridge motor control, hardware timer channels vs software PWM across STM32/ESP32/nRF52/RP2040
Firmware Architecture
- What Is an RTOS? — tasks, schedulers, semaphores, queues, and when an RTOS adds value
- Bare-Metal vs RTOS: Which Should You Use for Your Firmware? — when to use each approach and what to watch for at the boundary
Debugging and Validation
- How Do You Debug Embedded Firmware? — JTAG/SWD debugging, printf tracing, SWO, and systematic fault isolation
Platform-Specific Resources
This topic has dedicated subtopics for the most widely used MCU platforms:
- STM32 — STMicroelectronics' ARM Cortex-M MCUs, CubeMX configuration, and HAL firmware
- ESP32 — Espressif's Wi-Fi/BLE SoC platform for connected products
- nRF — Nordic Semiconductor's nRF52 Bluetooth Low Energy SoC family
- Raspberry Pi — hardware interfacing and GPIO with Linux-based embedded computing
Subtopics
STM32
STMicroelectronics STM32 microcontroller peripherals, toolchains, and design questions.
Raspberry Pi
Raspberry Pi hardware interfacing, GPIO, and embedded Linux questions.
ESP32
Espressif ESP32 Wi-Fi/Bluetooth SoC design, firmware, and power considerations.
nRF
Nordic Semiconductor nRF Bluetooth Low Energy SoC design and firmware questions.
Firmware
Low-level firmware design, bootloaders, and embedded software architecture.
Forum Discussions
STM32 GPIO interrupt configured but ISR never fires — what am I missing?
Trying to use a button on PA0 to trigger an interrupt on an STM32F411 Nucleo board. Using HAL, generated the init code with CubeMX. The GPIO
Can't decide between FreeRTOS and bare-metal for a simple sensor node — what's the tipping point?
Working on a temperature and humidity monitoring node — STM32F103 target, BME280 over I2C, reports data every 60 seconds over UART to a Rasp