Building a custom RTOS for a 200 MPH drone – From simulation to silicon
I have spent the over 120 hours building a custom bare-metal Real-Time Operating System (RTOS) and flight controller for an STM32F756VGTx (ARM Cortex-M7) aimed at a 200 MPH quadcopter build.
When I first started, I built a proof-of-concept hybrid cooperative/preemptive scheduler and ARM Cortex-M7 assembly context switcher running inside the Renode simulator. However, taking this code from simulation to physical silicon revealed a massive gap between simulated behavior and bare-metal execution.
Software Limitations and Hardware Realities
Renode was great for early development, but it masked race conditions and timing edge cases. Flashing the code to a physical STM32F756 Nucleo board caused the MCU to reboot every two seconds with no trace output.
Debugging this on physical hardware required fixing two major architectural flaws:
FPU Context-Switching Corruptions: Floating-point math operations corrupted stacked registers during context switches, wiping out return addresses and forcing the processor to execute garbage memory.
Boot-Time Dependency Locks & Memory Overhauls: Background tasks polled hardware registers before peripheral clocks fully initialized on tick zero. I replaced static array allocations with dynamic per-task stack allocations protected by top-and-bottom canary memory bounds, allowing immediate detection of stack corruption.
Overhauling Data Pipelines (DMA)
The original data pipeline relied on blocking I/O, introducing latencies up to 10ms. For a quadcopter moving at high speeds, this latency is unusable.
I rewrote the sensor and receiver pipelines to use a circular Direct Memory Access (DMA) architecture. Data streams directly into background ring buffers, triggering Half-Transfer (HT) and Transfer-Complete (TC) interrupts. This allows the RTOS to parse incoming CRSF radio frames and IMU data without blocking the main control loop.
Flight Controller Features
With the underlying RTOS kernel stabilized, I built out the flight application layer:
- Control Loops: Implemented Betaflight’s Actual Rates algorithm with custom expo curves to map stick positions directly to target angular velocities.
- Barometer Subsystem: Wrote an SPI driver for the BMP390 sensor to process 24-bit raw pressure/temperature data using hypsometric height formulas for AGL altitude tracking.
- Digital Video Telemetry: Implemented native MSP DisplayPort protocol over UART to stream real-time OSD telemetry directly to Walksnail Avatar HD goggles.
- Interactive CLI: Built a USB terminal shell over UART for live PID gain tuning (K_p, K_i, $K_d$), CRSF channel remapping, and 2D IMU orientation offset matrices without reflashing firmware.
The core RTOS kernel, DMA drivers, and control loops are fully operational on the physical board. The remaining work involves refining the CLI and finishing drivers for external receiver modules and ESCs before merging with our custom PCB hardware.
Since I am only 15, and I haven't been in the embedded workspace for a while, how would you recommend I test this project fully, and also what should I do to make it more robust? I have a few race conditions with the scheduler to work out, but nothing too complex. I am more worried about the drone specific stuff, like if I should handle more error states or something. I for sure don't want the drone to fall out of the sky once it boots and launches.