Bottom Line: I blinked an LED in Rust. I’ve been increasingly enthusiastic about Mozilla’s new open source programming language Rust over the last year or so. There is lots to read about the reasons that different people like rust, so I’ll spare you the details, but I think it’s exciting that even an amateur like myself can enjoy such a powerful, fast, and safe language.

One of the many promising things about Rust is its potential for safer and more ergonomic low-level programming in the embedded world; I’ve been keeping a casual eye on progress here as a tinkerer that really don’t know much C++ (and so my Arduino and ATMEGA328p adventures tend to be pretty tame).

Recently, a quick glance through a thread on r/rust (which has been one of several examples of Rust’s unusually welcoming community) suggested that the STM32 boards were a good way for a beginner to get started in embedded rust, so I picked one up.

I found a really great “hello world” walkthrough that took me most of the way. Much of it has been copied verbatim below, though a few steps are slightly different, and linked post has much more information about the steps being taken, so I highly recomend you read through it. I’ve also put a basic framework up as a git repo at github.com/n8henrie/rust-stm32; it already has the memory file, .cargo/config, and Cargo.toml as well as the basic main.rs; once dependencies are installed, if you clone it you should be able to cargo build get a binary you can use. The step below essentially walk you through making your own identical repo, (like the author of the linked blog post has also done here).

Requirements:

  • STM32 bluepill
  • FTDI for programming it (which seems to work fine instead of an ST-Link)
  • Working rust setup (including rustup and cargo, I’m on rust 1.34.1)

Step by step:

  1. cargo install cargo-binutils and rustup component add llvm-tools-preview to get the cargo objcopy commands cargo-binutils repo
  2. rustup target add thumbv7m-none-eabi (note this is 7m and not 7em, which may be a typo in the how-to post linked above)
  3. make a new crate: cargo new rust-stm32 && cd rust-stm32
  4. edit Cargo.toml to match this example
  5. make memory.x as instructed here:
     $ cat > memory.x <<EOF
     /* Linker script for the STM32F103C8T6 */
     MEMORY
     {
     FLASH : ORIGIN = 0x08000000, LENGTH = 64K
     RAM : ORIGIN = 0x20000000, LENGTH = 20K
     }
     EOF
    
  6. Add to ./.cargo/config:
     [build]
     # Instruction set of Cortex-M3 (used in BluePill)
     target = "thumbv7m-none-eabi"
    
     rustflags = [
     # use the Tlink.x script from the cortex-m-rt crate
     "-C", "link-arg=-Tlink.x",
     ]
    
  7. make src/main.rs:
  8. cargo build --release
  9. cargo objcopy -- -O binary target/thumbv7m-none-eabi/release/rust-stm32 rust-stm32.bin
  10. Build stm32loader to flash the code to the STM32 from MacOS:
    1. Clone repo: git clone https://github.com/florisla/stm32loader.git
    2. cd stm32loader
    3. Make venv: python3 -m venv .venv
    4. Source venv: source ./.venv/bin/activate
    5. Update pip to >= 19.1.1 pip install --upgrade pip
    6. Install in editable mode: pip install -e .
  11. Connect the (unplugged) FTDI to the STM32 (as a mnemonic, remember “Tx to Ten”, and also make sure your FTDI is set to 3.3v)

    FTDI STM32
    TX A10
    RX A9
    3V 3V
    GND GND
  12. Move the jumper for BOOT0 (the one closer to edge of board) from 0 to 1 (3.3v)
  13. Flash the program: stm32loader -p /dev/tty.usb1 -e -w -v /path/to/rust-stm32.bin, where flags are for port, erase, write, and verify
  14. If it looks like it flashed correctly, disconnect it, move BOOT0 back to 0 / off, reconnect power, and see if you have a blinking light!

Links: