Table of Contents

Embedded engineering

Embedded engineering is the discipline of writing software for microcontrollers (MCUs) and other resource-constrained hardware that is dedicated to a single task or a fixed set of tasks. Unlike general-purpose computing, an embedded system usually has no operating system, no display, and no user interacting with it directly — it just runs, often for years without being rebooted, inside some larger device.

The constraints define the work. An MCU might have 64 KB of flash for program storage, 8 KB of RAM, and a clock running at 16 MHz. There is no virtual memory, no dynamic allocator you can trust, and no second chance if you corrupt the stack. This forces habits that general-purpose programmers rarely need: careful memory layout, deterministic timing, interrupt-driven design, and an intimate understanding of the hardware peripherals you are driving.

Microcontrollers

A microcontroller integrates a CPU core, flash memory, RAM, and peripherals (GPIO, UART, SPI, I2C, ADC, timers) onto a single chip. Common families include PIC (Microchip), STM32 (ST Microelectronics, ARM Cortex-M), ESP32 (Espressif, Xtensa/RISC-V with built-in WiFi and Bluetooth), and the RP2040 (Raspberry Pi Pico, dual-core ARM Cortex-M0+). Arduino boards wrap an AVR or ARM MCU behind a beginner-friendly toolchain but compile down to the same bare metal.

Most MCUs follow the Harvard architecture: program code lives in flash and data lives in RAM. Flash is non-volatile — the program survives a power cycle — but it cannot be written at runtime without special procedures. RAM is fast and writable but disappears on reset. This split is why embedded code is compiled to a fixed binary and flashed onto the device, rather than loaded from a filesystem at boot.

Communicating with peripherals

Peripherals on an MCU are controlled through memory-mapped registers. Writing a value to a specific address sets a pin high, configures a baud rate, or triggers a DMA transfer. The three dominant serial buses are UART (asynchronous, point-to-point, two wires), SPI (synchronous, full-duplex, one master and one or more slaves), and I2C (synchronous, half-duplex, multi-device on two wires). GPIO pins are the simplest interface — a single bit, readable or writable, often used for LEDs, buttons, and bit-banged protocols.

Interrupts are the primary mechanism for reacting to hardware events without polling. When a peripheral finishes a transfer or a pin changes state, it fires an interrupt that suspends the main loop, runs an interrupt service routine (ISR), and returns. ISRs must be short and must not block — any variable shared between an ISR and the main loop needs to be declared volatile so the compiler doesn't optimize away the reads.

RTOS

A real-time operating system (RTOS) sits between bare-metal and a full OS. It provides a task scheduler, mutexes, semaphores, queues, and timers, without the overhead of a general-purpose kernel. FreeRTOS and ZephyrOS are the two most common choices. The key guarantee an RTOS provides is deterministic timing: a task with a given priority will run within a bounded deadline. This matters in control loops, motor drivers, and anything that interacts with the physical world on a fixed schedule.

List of concepts