Environment Setup for STM Developement in Rust
STM32 is the recommended choice to start with Embedded System Programming in Rust consider there are many supported libraries for Cortex-m series and STM targets and hals among the ecosystem. If you are already familiar with programing with them, all you have to do is add a few config files in your cargo projects and it's done. If not, fear not then. This post will list all the tools and environment you have to setup. I'm going to use STM32F3DISCOVERY as an example but other STM series should follow similar steps.
Tooling
First of all, we need to install some tools if you didn't have any embedded setup on your computer before. The common choice for our debuggers are usually GDB with OpenOCD. minicom
may be helpful if you want to also try out some serail module. The GDB you have to install may be different depends on your distribution. On Ubuntu/Debian, it will be gdb-multiarch
:
$ sudo apt-get install \
gdb-multiarch \
minicom \
openocd
While others like arch linux will use arm-none-eabi-gdb
:
$ sudo pacman -S \
arm-none-eabi-gdb \
minicom \
openocd
If you want to communicate with bluetooth, there are some more packages recommended to install: bluez
, bluez-utils
, rfkill
.
udev rules
Next, we want to set some rules to let us use those devices directly without root privilege sudo
. Here's the example for STM32F3:
$ cat /etc/udev/rules.d/99-ftdi.rules:
# FT232 - USB <-> Serial Converter
ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6001", MODE:="0666"
$ cat /etc/udev/rules.d/99-openocd.rules
# STM32F3DISCOVERY rev A/B - ST-LINK/V2
ATTRS{idVendor}=="0483", ATTRS{idProduct}=="3748", MODE:="0666"
# STM32F3DISCOVERY rev C+ - ST-LINK/V2-1
ATTRS{idVendor}=="0483", ATTRS{idProduct}=="374b", MODE:="0666"
Then reload the rules:
$ sudo udevadm control --reload-rules
Finally, let's plug the device and verify the permissions:
$ lsusb | grep -i stm
Bus 001 Device 006: ID 0483:374b STMicroelectronics ST-LINK/V2.1
This means my device is connected to Bus 001 and enumerated as Device 006. Let's check the permissions according to the number we have:
ls -l /dev/bus/usb/001/006
crw-rw-rw-+ 1 root root 189, 5 Jun 30 17:31 /dev/bus/usb/001/006
We should see that current user does have read/write permissions as crw-rw-rw-
shows.
OpenOCD connection
Once we install the packages, we could start to connect to our device using OpenOCD. The cfg file might be different depends on your device. But if everything works as planned, you should see something like following:
$ openocd -f interface/stlink-v2-1.cfg -f target/stm32f3x.cfg
Open On-Chip Debugger 0.10.0
Licensed under GNU GPL v2
For bug reports, read
http://openocd.org/doc/doxygen/bugs.html
Info : auto-selecting first available session transport "hla_swd". To override use 'transport select <transport>'.
adapter speed: 1000 kHz
adapter_nsrst_delay: 100
Info : The selected transport took over low-level target control. The results might differ compared to plain JTAG/SWD
none separate
Info : Unable to match requested speed 1000 kHz, using 950 kHz
Info : Unable to match requested speed 1000 kHz, using 950 kHz
Info : clock speed 950 kHz
Info : STLINK v2 JTAG v27 API v2 SWIM v15 VID 0x0483 PID 0x374B
Info : using stlink api v2
Info : Target voltage: 2.910427
Info : stm32f3x.cpu: hardware has 6 breakpoints, 4 watchpoints
You should see it says how many breakpoints and watchpoints are there.
ITM
For ITM protocol, there's itmdump
we could install from cargo directly:
$ cargo install itm
And then make sure you start the tool in the same directory of where you start OpenOCD:
$ touch itm.txt && itmdump -F -f itm.txt
Other LLVM tools
If you also want to use some LLVM tools like objdump
, size
and more. There's also a cargo subcommand already:
$ cargo install cargo-binutils
$ rustup component add llvm-tools-preview
Then you could use thos tools like this:
```bash
$ cargo objdump --target thumbv7em-none-eabihf --bin app --release -- -disassemble -no-show-raw-insn
$ cargo size --target thumbv7em-none-eabihf --bin app --release -- -A -x
...
Rust Toolchains & Configurations
After installing packages that we needs for general embedded development, we can now start tweaking our rust related stuffs. Like other projects we do in Rust, we usually create a cargo project since it can integrate several important build tools especially toolchains when we want to do cross compilation. For STM boards, or Arm Cortex-m architectures more precisely, here are the targets we could choose:
- Cortex-M0, M0+, and M1 (ARMv6-M architecture):
thumbv6m-none-eabi
- Cortex-M3 (ARMv7-M architecture):
thumbv7m-none-eabi
- Cortex-M4 and M7 without hardware floating point (ARMv7E-M architecture):
thumbv7em-none-eabi
- Cortex-M4F and M7F with hardware floating point (ARMv7E-M architecture):
thumbv7em-none-eabihf
To install the toolchain, simply run:
$ rustup target add thumbv7em-none-eabihf
Config files
Once toolchain is installed, we can cross compile the project to specific target using cargo build --target
. But this
will become tedious and there are more steps need to do when you entering gdb. So instead of typing these command
manually each time, we could setup config files in the cargo project like this.
.cargo/config
:
[target.thumbv7em-none-eabihf]
runner = "arm-none-eabi-gdb -q -x openocd.gdb"
rustflags = [
"-C", "link-arg=-Tlink.x",
]
[build]
target = "thumbv7em-none-eabihf"
openocd.gdb
:
target remote :3333
set print asm-demangle on
set print pretty on
monitor tpiu config internal itm.txt uart off 8000000
monitor itm port 0 on
load
break main
continue
This means we will build the project to specific target thumbv7em-none-eabihf
and then run with arm-none-eabi-gdb
with gdb config file openocd.gdb
. Then we will just need to use cargo run
to enter the gdb and see how it goes!
More References
One of great reasons to develop embedded system in Rust is always its community. There are plenty of resources you can find in the ecosystem. Here are some I think are useful and might help you too.