Embedded · 4 months · CMU

Real-Time Embedded Vehicle

An embedded car powered by a custom-built real-time operating system.

STM32 Cortex-M4RMS scheduler<10% average speed error
Role
Embedded systems engineer
Team
CMU embedded systems course project
My contribution
RTOS kernel, boot code, device drivers, carrier PCB, PID control, and system integration
Assembled embedded vehicle with STM32 carrier PCB and motor system

Overview

I created a real-time embedded vehicle from the bare metal up, combining custom hardware design, low-level firmware, a custom operating system, and closed-loop motor control. A preemptive RTOS schedules the PID loop, UART telemetry, and LCD updates against their selected execution periods.

The system is built around an STM32 Cortex-M4 microcontroller on a custom two-layer PCB that routes power and signals to L298 H-bridge motor drivers, LM1084 voltage regulators, quadrature encoders, servos, and an I2C LCD.

PCB Design

I designed the board in Autodesk Fusion to interface the STM32 Nucleo with the vehicle's peripherals. The PCB handles 5V and 3.3V power distribution, motor-control routing, encoder inputs, and user I/O.

Vehicle carrier board PCB layout with STM32 Nucleo, L298 motor driver, encoder and LCD headers
Carrier board layout, STM32 Nucleo

Firmware

I wrote the system bootloader in ARMv7-M assembly to initialize the exception vector table and stack pointers, then built bare-metal C drivers using MMIO for PWM, ADC, I2C, and interrupt-driven UART.

Preemptive multitasking runs on a dual-stack context-switching architecture built on the ARM Cortex-M exception model: each thread executes in user mode on its own stack (PSP), while the kernel runs in handler mode on a shared stack (MSP). SysTick tracks thread budgets and pends PendSV; the handler below saves the running thread's context, consults the thread control block (TCB) list to select the next runnable thread by priority, and restores it.

Context switch flow from SysTick through the PendSV handler Thread A runs on the user stack until SysTick pends PendSV. The PendSV handler, running in kernel mode on the main stack, saves Thread A's context, consults the thread control block list, selects the next runnable thread by priority, and restores its context so Thread B resumes on the user stack. USER MODE HANDLER MODE Thread A running on PSP Thread B resumes on PSP PendSV HANDLER 1. Save PSP context 2. Push state, update TCB 3. Select highest-priority thread 4. Restore new context SysTick tick TCB list sorted by priority Context switch flow from SysTick through the PendSV handler Thread A runs on the user stack until SysTick pends PendSV. The PendSV handler, running in kernel mode on the main stack, saves Thread A's context, consults the thread control block list, selects the next runnable thread by priority, and restores its context so Thread B resumes on the user stack. USER MODE Thread A running on PSP HANDLER MODE SysTick tick PendSV HANDLER 1. Save PSP context 2. Push state, update TCB 3. Select highest-priority thread 4. Restore new context TCB list sorted by priority USER MODE Thread B resumes on PSP
Context switch

Task priorities are assigned by a rate-monotonic scheduler validated against the utilization bound, with an SVC path bridging user and kernel space, newlib support for printf and malloc, and priority-inheritance mutexes (HLP/IPCP) to bound priority inversion.

Motor Control

The vehicle decodes wheel position through EXTI quadrature encoders and drives the motors through hardware-timer PWM. I tuned a closed-loop PID controller to keep average wheel speed within 10% of its target, measured from encoder telemetry visualized over UART. UART and I2C LCD updates ran as concurrent real-time tasks under the same scheduler.

Results

The selected PID, UART, and LCD parameters produce 71.25% modeled utilization, below the 77.98% rate-monotonic utilization bound for three tasks. The simulator below reconstructs that task set so you can explore the analytical schedulability margin.

Wheel-speed error
<10% average error from the target, measured through encoder telemetry over UART
Task utilization
71.25% of CPU time across PID, UART, and LCD threads
UB schedulability bound
77.98% for 3 tasks, comfortably met
Scheduling result
Analytically schedulable under the selected RMS task parameters
Vehicle carrier board PCB layout
Adjust the schedulerPointerClick the −/+ controls to change task timing.TouchTap the −/+ controls to change task timing.KeyboardTab to each control and press Enter or Space.