# 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|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|UART]] (asynchronous, point-to-point, two wires), [[spi|SPI]] (synchronous, full-duplex, one master and one or more slaves), and [[i2c|I2C]] (synchronous, half-duplex, multi-device on two wires). [[gpio|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|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 - [[list-of-embedded-engineering-concepts]]