Project detail
Real-Time Embedded Vehicle
An embedded car powered by a custom-built real-time operating system.
Embedded
4 months
CMU


Overview
I created a real-time embedded vehicle from the bare metal up, integrating custom hardware design, low-level firmware, a custom operating system, and closed-loop motor control. The system is powered by an STM32 Cortex-M4 microcontroller mounted on a custom 2-layer printed circuit board that routes power and signals to L298 H-bridge motor drivers, LM1084 voltage regulators, quadrature encoders, servos, and an I2C LCD. To control the vehicle, I developed a preemptive, multithreaded Real-Time Operating System that guarantees strict execution deadlines for concurrent tasks, including a highly tuned PID speed-control loop, live UART telemetry streaming, and real-time LCD state updates.
PCB Design
I designed a custom 2-layer PCB in Autodesk Fusion to interface the STM32 Nucleo board with external peripherals. This included routing a power distribution system (5V and 3.3V), motor control signals to a L298 H-bridge, quadrature encoders, and user I/O.
Firmware
First, I authored the system bootloader in ARMv7-M Assembly to initialize the exception vector table, configure the stack pointers, and initialize the .data and .bss memory sections. I developed bare-metal C drivers using MMIO for hardware PWM, ADC, I2C, and interrupt-driven UART.
To achieve preemptive multitasking, I designed a dual-stack context-switching architecture utilizing the ARM Cortex-M exception model. A hardware SysTick timer tracks thread budgets and pends a PendSV interrupt each tick. Upon interruption, hardware automatically saves caller registers to the user stack (PSP). My custom assembly handler then pushes the PSP, link register, and remaining callee-saved registers to the kernel stack (MSP). The scheduler saves this MSP pointer into the Thread Control Block (TCB), selects the highest-priority runnable thread, and passes its MSP back to the handler. Finally, the handler restores the new thread's state, updates the hardware PSP, and triggers an exception return to seamlessly resume execution.
To ensure tasks met their deadlines, I implemented a Rate-Monotonic Scheduler (RMS) backed by a UB schedulability test. I also designed an SVC handler to transition between user and kernel space, integrating newlib to support standard C library functions like printf and malloc. Finally, I prevented unbounded priority inversion using mutexes with HLP/IPCP for synchronization.
Motor Control
To integrate the vehicle hardware, I first decoded the wheel position using EXTI for quadrature encoders and drove the motors using hardware-timer PWM. I then implemented and tuned a closed-loop PID control algorithm to maintain precise wheel speed with under 10% steady-state error. By the end of the project, I successfully scheduled the PID control loop as a real-time task alongside concurrent UART telemetry logging and I2C LCD updates without missing critical execution deadlines.
