TL;DR: When doing timing critical stuff, use the
--release flag to get a faster binary!
cargo embed --release.
I’m learning Embedded Rust on a STM32 Bluepill board (with a STM32F103 microcontroller). At the time of writing there seems to be two toolchain options:
- The “official” Embedded Rust way, using OpenOCD and ARM-compatible GDB.
- Up-and-coming probe-rs that is working on having everything in Rust and installable via
cargo. Their tool
cargo-embedbasically replaces OpenOCD and GDB.
OpenOCD + GDB is true and tested, but a lot more work to set up. Probe-rs is litteraly just
cargo install cargo-embed, but it is a work in progress and far from feature-complete. I tried both, but this particular thing caught me while using
cargo-embed, so that’s the command I will be showing.
The Timer Problem
I wanted to talk to a ws2812 adressable RGB LED (also known as NeoPixel). I found the crate smart-leds that seemed perfect. It comes with several “companion crates” with device drivers that support different LEDs and several options for different ways of driving the ws2812, like the ws2812-spi and ws2812-timer-delay.
The SPI crate unfortunately did not work in my attempts so far. It manages to write to my LED once, then panics with the error “Overrun”. Probably I’m using a newer version of the
stm32f1xx-hal than it was written for. Maybe a topic for another day.
The Timer Delay crate also did not work at first. I broke out my Analog Discovery 2 to look at the data signal:
The time between bits was around 200 us. To get a comparison, I fired up a Platformio project for the same STM32 Bluepill board and imported Adafruit’s Neopixel library. Now, the LED of course worked perfectly and the problem was obvious:
The time between the bits was now only around 1,4 us. I will spare you the details of all the things I tried while wrongly thinking either the entire MCU or the timer was running at the wrong frequency.
The solution turns out to be almost silly: Rust binaries can be really slow if you do not compile them in release mode. Just add the
--release flag and all is well! 💩
cargo embed --release
There is apparently a way to override this per-dependency in
Cargo.toml, that might be worth a try if you need it.
I tried adding the following to
Cargo.toml to make all dependencies build with the highest optimization level, but this still was not enough to make the LED work in my case.
# Cargo.toml [profile.dev.package."*"] opt-level = 3
I also tried increasing the optimization level for the whole
dev profile. This worked already from level 2:
# Cargo.toml [profile.dev] opt-level = 2
Stepping through code compiled like this with a debugger might not work as well though so you might as well use the
release profile all the time and only drop down to
dev for debugging.