Embedded Systems Design

An embedded system is a computer built into a larger device to do a specific job — the controller in a thermostat, the engine management unit in a car, the processor in a camera. The design decisions are different from writing software for a PC: resources are constrained, real-time behaviour matters, and the code usually can't be patched in the field. Getting it right the first time is more important.

Microcontroller Selection

The right MCU is the one that has just enough of everything you need — processing power, memory, peripherals, and I/O. Over-speccing costs money and power; under-speccing means workarounds.

  • 8-bit MCUs (ATmega, PIC16) — cheap, low power, simple toolchains. Plenty for simple control tasks: reading sensors, driving outputs, basic state machines. Arduino's ATmega328P runs most hobby projects without complaint.
  • 32-bit ARM Cortex-M — the current mainstream for serious embedded work. STM32, nRF52, SAM, SAME. Better performance, more peripherals, more memory, often lower active power than 8-bit at the same clock frequency. RP2040 (Raspberry Pi Pico) stands out for its PIO state machines — programmable I/O that can implement custom serial protocols in hardware.
  • Application processors (Cortex-A, RISC-V) — Linux-capable, more like a small PC than a microcontroller. Raspberry Pi, BeagleBone, Allwinner-based boards. Use when you need an OS, networking stack, or the complexity requires it. Not a good choice for tight real-time control.

Communication Interfaces

UART

Asynchronous serial, one wire each direction. Simple and universal — almost every MCU has at least one. 9600, 115200 baud are common. No clock line, so both sides must agree on baud rate. Good for: debug output, GPS modules, Bluetooth HC-05, PC communication via USB-UART bridge.

SPI

Synchronous, 4-wire (MOSI, MISO, SCK, CS). Clock is sent by the master. Can run at tens of MHz. Full duplex. One CS line per slave. Good for: displays, SD cards, ADCs, DACs, external Flash. Fastest of the common interfaces for data throughput.

I2C

Two-wire (SDA, SCL) with 7-bit addressing — up to 127 devices on one bus. Needs pull-up resistors (4.7kΩ typical). Slower than SPI (100kHz–1MHz), half-duplex. Good for: sensors (BME280, MPU-6050), EEPROMs, RTCs. The addressing system makes it easy to connect many different devices with only two wires.

CAN

Differential bus designed for automotive environments — high noise immunity, multi-master, 1Mbit/s. The protocol most cars use internally. MCP2515 adds CAN to any SPI-capable MCU. The whole protocol is designed around the assumption that any node can fail at any time without disrupting the bus.

Real-Time Operating Systems

For simple embedded systems, bare-metal programming works fine — a main loop with interrupts for time-critical events. When you have multiple tasks that need to run concurrently with timing guarantees, an RTOS makes sense.

  • FreeRTOS — the dominant open-source RTOS. Runs on almost everything, well-documented, AWS-maintained. If you need a RTOS, start here.
  • Zephyr — modern, actively developed, strong ARM support, good networking stack. Higher overhead than FreeRTOS but more features.
  • ChibiOS/RT — fast, compact, good for STM32 projects where you want the RTOS but can't spare much flash/RAM.

An RTOS adds complexity. Don't use one unless you need it. Many "concurrent" requirements can be met with a cooperative scheduler, interrupt service routines, and a state machine.

Power Management

In battery-powered designs, power consumption determines product lifetime. Key strategies:

  • Sleep modes — nearly every modern MCU has multiple sleep modes. The deepest (deep sleep, hibernate) turn off almost everything — the MCU draws microamps instead of milliamps. Wake from a GPIO interrupt or RTC alarm. Between measurements, sleep. A sensor that samples every 10 seconds and sleeps in between will run for months on a small battery.
  • Clock gating — disable peripheral clocks when not in use. An unused UART left running wastes power even when no data moves.
  • Run at minimum clock speed — dynamic power scales with frequency. If the task is throughput-limited, run fast; if it's latency-limited (interrupt response), run at the minimum that meets timing.
  • Power off peripherals — sensors, radios, and displays often have a power pin or enable pin. Pull it low between readings. A BME280 draws <1µA in sleep mode but 3.6mA during measurement — duty cycle matters.

Debugging

Hardware debugging tools:

  • JTAG / SWD — the standard way to program and debug ARM MCUs in-circuit. Set breakpoints, inspect registers and memory, single-step. An ST-Link, J-Link, or CMSIS-DAP adapter connects MCU to debugger (OpenOCD, GDB). Every serious embedded project should have SWD pads on the PCB.
  • Logic analyser — capture digital signals for protocol decoding. Essential for debugging SPI, I2C, UART communication. The Saleae Logic and cheap Cypress FX2-based clones are popular. PulseView (open source) works well with the cheap clones.
  • Serial debug output — the embedded equivalent of printf debugging. Simple, always available, often enough to diagnose problems without a full debugger.