STM32F0xx Development – Part 2: Software
Overview
As mentioned in STM32F0xx DigitalIO / AnalogIn PCB, one of the main motivations behind this project was to learn more Rust.
If Rust turns out to be genuinely useful in an embedded setting, I definitely won’t be subjecting myself to C again anytime soon. Just kidding — I like you, C… …nah, not really. 😄
Setup
Hardware
Target MCU – STM32F030
This is a solid Cortex-M0 device. The variant I’m using offers around 35 usable GPIO pins, 64KB of flash, and 8KB of RAM. The real selling point, though, is the price — roughly $1 USD in low quantities.
Programmer – ST-Link V2
The inexpensive AliExpress clone, which can be picked up for around $3 USD.
Software
IDE – VS Code
I’ve been using VS Code for a while now and it’s my daily driver for Python, C#, and Rust development.
OS – Ubuntu KDE 20.04 (Kubuntu)
Recently upgraded from Kubuntu 18.04. It’s been mostly stable, although I’ve had a few interesting issues when docking and undocking.
Getting Started Resources
There are a few additional tools that need to be installed (Rust, GDB, OpenOCD, etc.). The following resources are absolutely worth checking out:
Debugging and Testing
After installing and setting everything up, we can finally start writing code.
…Hang on. How do we actually test that code?
Hardware-Level Debugging
The resources above walk through setting up a debug environment in VS Code. This allows you to step through code while it executes on the MCU.
Below is one of the debugger configurations from my launch.json file, with a few custom tweaks:
Unit Testing.
Can we run unit tests on the host instead of the target MCU?
Yes — but it requires some conditional compilation using cfg_attr.
When not running unit tests, the code is compiled with no_std and no_main, and certain modules are excluded:
| |
This setup allows logic to be tested on the host while still compiling cleanly for the embedded target.
Code Size While Debugging
At around 1–1.5k lines of code, I was already running out of space on the 64KB flash.
That raised a big question:
If this is the size of a small Rust codebase, is Rust actually usable in embedded systems?
The Solution
Enable optimization.
By default, debug builds include a huge amount of metadata. Adding the following to Cargo.toml drastically reduced the binary size:
| |
Normally, opt-level defaults to 0. Switching to “z” made a massive difference.
RTOS and Embedded Libraries
RTOS
For this project, I used RTIC, a real-time concurrency framework written entirely in Rust.
While it’s not as mature as something like FreeRTOS, it fits my use case perfectly. One of its biggest advantages is safe data sharing between priority-based interrupts — something that’s notoriously easy to get wrong in traditional embedded systems.
Check it out here: RTIC
Embedded Libraries: Embedded Libs
Actual Code Example
Below is an example of the serial module used in this project.
You’ll notice heavy use of traits and trait bounds, which are conceptually similar to interfaces in C#.
The mod tests section contains all unit tests for this module.
| |