はじめに
先日Raspberry Pi Picoを購入した際に、送料無料にするためにST Nucleo Board STM32F411RET6も合わせて購入しました。これをRustでLチカさせたので、手順をまとめます。実装は以下GitHubにまとめています。
github.com
概要
- RustでSTM32F4シリーズ向けのクロスコンパイル環境構築手順を書きました。
- stm32f4xx-halクレートのREADMEに沿ってLチカ手順を書きました。
- STM32F4シリーズのマイコンに向けてRustでLチカしたい人向けの記事です。
これは自分でプロジェクトを用意してRustでLチカしたSTM32f411ボード pic.twitter.com/QCxH4sDmzS
— YU2TA7KA (@UGKGbrothers) 2021年5月25日
ST Nucleo Board STM32F411RET6とは
- STM32F4シリーズに属するマイコンボードです。
- Nucleoボードで安価でプロトタイプ向けです。
- 高性能マイコンと謳われています。
STM32とは?F4シリーズとは?Nucleoとは?といろいろな特徴があります。詳しくは下記のような記事が参考になります。今回のポイントは安価(2,000円)でプロトタイプ向けという点です。
ARMコアベースのSTM32F4シリーズ製品|32bitのARMコア搭載マイコン
www.kumikomi.jp
開発環境の構築
Rustのインストール
OSはUbuntu 20.04を使用しています。
sudo apt update sudo apt upgrade sudo apt install gcc sudo apt install curl curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh ls ~/.cargo/bin source $HOME/.cargo/env cargo new hello cd hello cargo run
Rustのクレートの追加
クロスコンパイル環境の構築に向けて、cargo-editを追加します。
sudo apt-get install libssl-dev cargo install cargo-edit
クロスコンパイル環境の構築
ターゲットボード(ST Nucleo Board STM32F411RET6)のコア(ARM Cortex-M4)に合わせたクロスコンパイル環境を準備します。ARM Cortex-M4 向け(FPU搭載)のターゲットトリプル*1はthumbv7em-none-eabihfになります。
rustup target add thumbv7em-none-eabihf cargo install cargo-binutils rustup component add llvm-tools-preview
以上が開発環境の構築になります。ホスト側でHello World!は確認できているはずですが、クロスコンパイル環境の動作確認はできず、以降のLチカで確認することになります。また、Rustを初めてインストールする場合は、エディタ環境の構築もあっても良いかもしれませんが、今回はほぼコードのコピペなので省略します。
Lチカプロジェクトの構築
いよいよLチカに向けてプロジェクトを構築していきます。STM32F4向けにはすでにHALクレートが用意されており、これを利用する方法を採用します。HALとは、Hardware Abstraction Layerの略称でハードウェア制御を抽象化し、ハードウェアごとの違いを吸収してくれているレイヤーです。ここらへんのファームウェア構造については基礎から学ぶ 組込みRustのPage145 embedded-halの説明がわかりやすいです。
まずは、新しいプロジェクトを作成します。
cargo new LED-blink cd LED-blink/ cargo run
Lチカコードのコピペ
ここから実装になっていくのですが、stm32f4xx-halクレートのREADMEに沿ってほぼコピペです。今回のターゲットはST Nucleo Board STM32F411RET6ですが、STM32F4シリーズであれば、他のボードでもターゲットに合わせてCargo.tomlのターゲット指定とmemory.xのメモリマップ指定を変更すれば、あとは以下同様の手順でLチカができるはずです。
以下をstm32f4xx-halのリポジトリからコピペします。
- examples/delay-blinky.rs
- Cargo.toml
- .cargo/config
- memory.x
注:Cargo.tomlでターゲット指定の部分のみ修正します。
examples/delay-blinky.rs
main.rsに下記をコピペします。これがLチカの核となるソースコードです。
#![deny(unsafe_code)] #![no_main] #![no_std] // Halt on panic use panic_halt as _; // panic handler use cortex_m; use cortex_m_rt::entry; use stm32f4xx_hal as hal; use crate::hal::{prelude::*, stm32}; #[entry] fn main() -> ! { if let (Some(dp), Some(cp)) = ( stm32::Peripherals::take(), cortex_m::peripheral::Peripherals::take(), ) { // Set up the LED. On the Nucleo-446RE it's connected to pin PA5. let gpioa = dp.GPIOA.split(); let mut led = gpioa.pa5.into_push_pull_output(); // Set up the system clock. We want to run at 48MHz for this one. let rcc = dp.RCC.constrain(); let clocks = rcc.cfgr.sysclk(48.mhz()).freeze(); // Create a delay abstraction based on SysTick let mut delay = hal::delay::Delay::new(cp.SYST, clocks); loop { // On for 1s, off for 1s. led.set_high().unwrap(); delay.delay_ms(1000_u32); led.set_low().unwrap(); delay.delay_ms(1000_u32); } } loop {} }
Cargo.toml
以下をCargo.tomlに追記でコピペします。ただし、featuresの部分はターゲットに合わせてf411に修正が必要です。これらが追加するクレートになります。すでに[pakage]の情報がありますが、それは残したままで良いです。
[dependencies] embedded-hal = "0.2" nb = "0.1.2" cortex-m = "0.6" cortex-m-rt = "0.6" # Panic behaviour, see https://crates.io/keywords/panic-impl for alternatives panic-halt = "0.2" [dependencies.stm32f4xx-hal] version = "0.8" features = ["rt", "stm32f411"] # replace the model of your microcontroller here
.cargo/config
新規にフォルダを作成してファイルを格納します。ここでは、ビルドのターゲットを指定します。
[target.thumbv7em-none-eabihf] runner = 'probe-run --chip stm32f411' rustflags = [ "-C", "link-arg=-Tlink.x", ] [build] target = "thumbv7em-none-eabihf"
memory.x
プロジェクトの直下に新規ファイルとして作成しコピペします。これは対象のCPUがどのようなメモリアドレス空間を持っているかを示すファイルです。cortex-m-rtクレートで動作するため必要です。アドレスは変更不要ですが、サイズを拡張します。
MEMORY { /* NOTE K = KiBi = 1024 bytes */ FLASH : ORIGIN = 0x08000000, LENGTH = 500K RAM : ORIGIN = 0x20000000, LENGTH = 32K } /* This is where the call stack will be allocated. */ /* The stack is of the full descending type. */ /* NOTE Do NOT modify `_stack_start` unless you know what you are doing */ _stack_start = ORIGIN(RAM) + LENGTH(RAM);
Lチカコードのビルド
以上で、必要な実装(コピペ)は完了です。これをビルドします。ビルドに必要なターゲット指定は.cargo/configで行っているため、オプション不要で下記のコマンドでOKです。
cargo build
ターゲットへ実行ファイルを送る
ビルドした実行ファイルをターゲットへ書き込みます。書き込みにはOpenOCDを利用します。
OpenOCDのインストール
sudo apt install openocd
OpenOCDの.cfgファイル
プロジェクトの直下に下記ファイルを作成します。これはnucleo-f411reのものを利用させてもらっています。これを実行時に指定します。
source [find interface/stlink-v2-1.cfg] transport select hla_swd source [find target/stm32f4x.cfg] reset_config srst_only
OpenOCDの実行
以下コマンドでターゲットへ実行ファイルを送ります。プロジェクト名はLED-blinkの想定です。
openocd -f nucleo.cfg -c"program target/thumbv7em-none-eabihf/debug/LED-blink verify reset exit"
Verified OK
Resetting Target
と出ていれば書き込みは成功しており、1秒間隔でLチカしているはずです。お疲れ様でした。
おわりに
RustでLチカやりたい!と思っていろいろ調べていたのですが、適切な情報になかなかたどり着けず、四苦八苦していました。そんな中で、個人的にはstm32f4xx-halクレートのREADME.mdが一番わかりやすかったです。今回のターゲットにはボタンが着いていたり、他にもたくさんのインタフェースがあるので、今後はそれらも使っていってみたいと思います。また、コピペの紹介だけったのでその中身の解説もできたらと思います。
参考
マニュアルのメモリマップ
https://www.st.com/resource/en/datasheet/stm32f411ce.pdf


*1:ターゲットプラットフォームを表す形式で、プロセッサアーキテクチャ-OS-ABIの順番で表記されます。ターゲットプラットフォーム一覧はPlatform Support - The rustc bookに記載されています。