Ramblings of an aging IT geek
← Ramblings of an aging IT geek
rust

Blinking An LED On An STM32 With Embedded Rust

First impressions of writing bare-metal Rust on an STM32, where the type system models the hardware and a misused peripheral becomes a compile error.

Code on a screen next to a microcontroller

I finally sat down with an STM32 board and the embedded Rust ecosystem this week, and the thing that surprised me was not that it worked, it was how the type system felt on bare metal. I am used to embedded meaning C, a vendor HAL, and a quiet hope that I have not enabled a peripheral's clock in the wrong order.

The Rust approach inverts that. The hardware abstraction layer models peripherals as types, and ownership rules apply to them like anything else. You take ownership of a pin, configure it as a push-pull output, and that configured pin is now a distinct type. Try to use the same pin in two places and the borrow checker stops you at compile time, because you cannot have two owners of one piece of hardware. A whole class of "two bits of code fighting over the same register" bugs simply will not compile.

The embedded-hal traits are the clever bit. They define what a digital output, an SPI bus, or a delay is, abstractly, so a driver written against the trait runs on any chip whose HAL implements it. I wrote the obligatory blink against the trait, not the specific board, and the same logic would move to a different STM32 with barely a change.

It is not all smooth. The error messages when a trait bound does not line up are a wall of generics, and you spend time fighting the build setup, the linker script, the memory.x, before you blink a single LED. But once it builds, it runs, and the confidence is real. A peripheral I have misconfigured is a compile error, not a board that silently does nothing at three in the morning. For embedded work, that trade is one I will happily take.