chakokuのブログ(rev4)

テック・コミック・ごくまれにチャリ

STM32用バイナリがビルドできるよう組み込み用Rust環境をセットアップ

絶対座標で計算するから大変なんであって、XYZ軸の傾きを検知して、相対的にモータの回転数を±で補正する程度のアルゴリズムで、まずはドローンを飛ばしてみようと思う(変な方に飛んでいくかもしれないが)。ただ、このままMicroPython で試作を続けるとあまりに楽でRustに移行しなさそうなので、、ドローンが離陸*1するコードは絶対にRustで書こうと決めた。というわけで、、STM32に対応したRustをUbuntuに導入する。先人のブログを参考に手順は以下

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source $HOME/.cargo/env
rustup target add thumbv7em-none-eabihf
cargo install cargo-binutils
rustup component add llvm-tools-preview
cargo install cargo-generate
$ rustc --print target-list

            *略*
thumbv6m-none-eabi
thumbv7a-pc-windows-msvc
thumbv7a-uwp-windows-msvc
thumbv7em-none-eabi
thumbv7em-none-eabihf
thumbv7m-none-eabi
thumbv7neon-linux-androideabi
thumbv7neon-unknown-linux-gnueabihf
thumbv7neon-unknown-linux-musleabihf

STM32用にビルドするには、マイコンのメモリマップに合ったリンカ設定やらスタートアップやらもろもろが必要になるのだが、そこを0から準備しているとどれほど時間がかかるか分からないので(問題に出くわして自力で解決できるかどうかも不明)、セットアップも先人に頼ることにした。
https://github.com/stm32-rs/stm32-rs

git clone git@github.com:stm32-rs/stm32f1xx-hal.git

example配下のblinky.rsに対してLEDのピンを修正してビルド
buildすると、thumbv7em-none-eabiが足りないと言われるので以下を再実行

rustup target add thumbv7em-none-eabi
cargo build --example blinky --release --features=stm32l4x6

/stm32l4xx-hal/target/thumbv7em-none-eabi/release/examples配下にblinkyが作られる。これを何も考えずに、Drag&DropでNUCLEOのボードに書き込み・・・動かない(動いているけどLEDが点滅しないのかも)

OpenOCDを使わないとだめなんだろうか。。
記事を参考にOpenOCD+gdbで試す。
OpenOCDでNucleoボードに接続

./bin-x64/openocd.exe -f ./scripts/interface/stlink-v2-1.cfg  -f ./scripts/target/stm32l4x.cfg

gdbを起動

$ /usr/local/GNUToolsARMEmbedded/4.8_2013q4/bin/arm-none-eabi-gdb.exe  target/thumbv7em-none-eabi/release/examples/blinky

Reading symbols from C:\cygwin64\home\sumi\lang\rust\stm32\stm32l4xx-hal\target\thumbv7em-none-eabi\release\examples\blinky...done.

(gdb) target remote localhost:3333

Remote debugging using localhost:3333
Reset ()    at /home/sumi/.cargo/registry/src/github.com-1ecc6299db9ec823/cortex-m-rt-0.6.15/src/lib.rs:497
497     /home/sumi/.cargo/registry/src/github.com-1ecc6299db9ec823/cortex-m-rt-0.6.15/src/lib.rs: No such file or directory.

(gdb) monitor reset halt
Unable to match requested speed 500 kHz, using 480 kHz
Unable to match requested speed 500 kHz, using 480 kHz
adapter speed: 480 kHz
target halted due to debug-request, current mode: Thread
xPSR: 0x01000000 pc: 0x08000400 msp: 0x20010000

(gdb) load
Loading section .vector_table, size 0x400 lma 0x8000000
Loading section .text, size 0x2640 lma 0x8000400
Loading section .rodata, size 0xea0 lma 0x8002a40
Start address 0x8000400, load size 14560
Transfer rate: 12 KB/sec, 4853 bytes/write.

(gdb) l
492     in /home/sumi/.cargo/registry/src/github.com-1ecc6299db9ec823/cortex-m-rt-0.6.15/src/lib.rs

(gdb) l main
20      use crate::rt::ExceptionFrame;
21
22      use crate::sh::hio;
23      use core::fmt::Write;
24
25      #[entry]
26      fn main() -> ! {
27          let mut hstdout = hio::hstdout().unwrap();
28
29          writeln!(hstdout, "Hello, world!").unwrap();

(gdb) l
30
31          let cp = cortex_m::Peripherals::take().unwrap();
32          let dp = hal::stm32::Peripherals::take().unwrap();
33
34          let mut flash = dp.FLASH.constrain(); // .constrain();
35          let mut rcc = dp.RCC.constrain();
36          let mut pwr = dp.PWR.constrain(&mut rcc.apb1r1);
37
38          // Try a different clock configuration
39          let clocks = rcc.cfgr.hclk(8.mhz()).freeze(&mut flash.acr, &mut pwr)
;
(gdb) hbreak main

Hardware assisted breakpoint 1 at 0x80005d0: file examples/blinky.rs, line 25.

(gdb) i r
r0             0x0      0
r1             0x0      0
r2             0x0      0
r3             0x0      0
r4             0x0      0
r5             0x0      0
r6             0x0      0
r7             0x0      0
r8             0x0      0
r9             0x0      0
r10            0x0      0
r11            0x0      0
r12            0x0      0
sp             0x20010000       0x20010000
lr             0xffffffff       -1
pc             0x8000400        0x8000400 <Reset>
xPSR           0x1000000        16777216

(gdb) cont
Continuing.

Breakpoint 1, main () at examples/blinky.rs:25
25      #[entry]

(gdb) l
20      use crate::rt::ExceptionFrame;
21
22      use crate::sh::hio;
23      use core::fmt::Write;
24
25      #[entry]
26      fn main() -> ! {
27          let mut hstdout = hio::hstdout().unwrap();
28
29          writeln!(hstdout, "Hello, world!").unwrap();

(gdb) n
Note: automatically using hardware breakpoints for read-only addresses.

Program received signal SIGTRAP, Trace/breakpoint trap.
0x08002902 in __c_m_sh_syscall ()
    at /rustc/a178d0322ce20e33eac124758e837cbd80a6f633/library/core/src/option.rs:388
388     /rustc/a178d0322ce20e33eac124758e837cbd80a6f633/library/core/src/option.rs: No such file or directory.
(gdb)

mainの次でハングしているのだろうか。スタックがおかしいのか?
スタックがおかしいのではないようだ。。なぜこんなところで止まるのか分からないが。

(gdb) where
#0  0x08002902 in __c_m_sh_syscall ()
    at /rustc/a178d0322ce20e33eac124758e837cbd80a6f633/library/core/src/option.rs:388
#1  0x080027e8 in syscall1 (_nr=5, _arg=536936268)
    at /home/sumi/.cargo/registry/src/github.com-1ecc6299db9ec823/cortex-m-semihosting-0.3.7/src/lib.rs:215
#2  syscall<[usize; 3]> (nr=5, arg=0x2000ff4c)
    at /home/sumi/.cargo/registry/src/github.com-1ecc6299db9ec823/cortex-m-semihosting-0.3.7/src/lib.rs:207
#3  write_all (fd=1, buffer=...)
    at /home/sumi/.cargo/registry/src/github.com-1ecc6299db9ec823/cortex-m-semihosting-0.3.7/src/hio.rs:69
#4  write_all (self=<optimized out>, buffer=...)
    at /home/sumi/.cargo/registry/src/github.com-1ecc6299db9ec823/cortex-m-semihosting-0.3.7/src/hio.rs:34
#5  write_str (self=<optimized out>, s=...)
    at /home/sumi/.cargo/registry/src/github.com-1ecc6299db9ec823/cortex-m-semihosting-0.3.7/src/hio.rs:40
#6  _$LT$$RF$mut$u20$W$u20$as$u20$core..fmt..Write$GT$::write_str::hb6fe49e168115967 (self=<optimized out>, s=...)
    at /rustc/a178d0322ce20e33eac124758e837cbd80a6f633/library/core/src/fmt/mod.rs:191
#7  0x08001e9e in core::fmt::write::hf86e9be5054282ca ()
    at library/core/src/fmt/mod.rs:1131
#8  0x08000632 in blinky::__cortex_m_rt_main::h1a11350d26d59f31 ()
    at /rustc/a178d0322ce20e33eac124758e837cbd80a6f633/library/core/src/fmt/mod.rs:184
#9  0x080005d4 in main () at examples/blinky.rs:25

mainのソースとしては以下の行

    let mut hstdout = hio::hstdout().unwrap();

セミホスティングを使うための初期設定と思われるが、Lチカだけやりたいので、セミホスティング要らないので、ややこしい行はコメントにしてみる。

セミホスティングの行をコメント、ビルドしてデバッグ

     //    let mut hstdout = hio::hstdout().unwrap();
     //    writeln!(hstdout, "Hello, world!").unwrap();

gdbを起動、mainでハードウエアブレーク設定して、contで実行

umbv7em-none-eabi/release/examples/blinkyq4/bin/arm-none-eabi-gdb.exe  target/th

Reading symbols from #######\lang\rust\stm32\stm32l4xx-hal\target\thumbv7em-none-eabi\release\examples\blinky...done.

(gdb) target remote localhost:3333
Remote debugging using localhost:3333
0x08002902 in ?? ()

(gdb) monitor reset halt
Unable to match requested speed 500 kHz, using 480 kHz
Unable to match requested speed 500 kHz, using 480 kHz
adapter speed: 480 kHz
target halted due to debug-request, current mode: Thread
xPSR: 0x01000000 pc: 0x08000400 msp: 0x20010000

(gdb) load
Loading section .vector_table, size 0x400 lma 0x8000000
Loading section .text, size 0x2500 lma 0x8000400
Loading section .rodata, size 0xde8 lma 0x8002900
Start address 0x8000400, load size 14056
Transfer rate: 11 KB/sec, 4685 bytes/write.

(gdb) hbreak main
Hardware assisted breakpoint 1 at 0x800058e: file examples/blinky.rs, line 25.

(gdb) cont
Continuing.

Breakpoint 1, main () at examples/blinky.rs:25
25      #[entry]

(gdb) n
Note: automatically using hardware breakpoints for read-only addresses.

Program received signal SIGINT, Interrupt.
blinky::__cortex_m_rt_main::h1a11350d26d59f31 () at src/delay.rs:72
72                  while !self.syst.has_wrapped() {}
(gdb)

LED2が点滅した。。 ゆえに、、セミホスティングは何か手当が必要、タイマとかGPIOは使える。

バイナリは動くバージョンになったということで、、Drag&Dropでファームに焼けるのかやってみた。
正常ファームの状態に正しいファームを上書きインストールしたことになり、正しく書けたかどうか、判断が難しいが、単純にDrag&DropでもLEDは点滅している。多分、デバッガなくてもこの方法で書き込めるのだろう。とはいえ、組み込みプログラムの開発においてシリアル等の通信手段がない場合、デバッガがないと非常に困るので、結局はOpenOCD+GDDBは必須だと思いますが。。

■追記
gdbセミホスティングを有効にするコマンドがあって、これを打ち込んでいなかったのを思い出した。

monitor arm semihosting enable

上記を打ち込むとどういう挙動になるのか再度試す。

OpenOCD側のコンソールに以下が表示、LEDが点滅されるのを確認した。よって、gdb側からセミホスティングを有効にする操作をしていなかったのが原因。

semihosting is enabled
Hello, world!

以上により、基本的なテストは行えたので、、今後はRust+STM32用ドライバを使って、USBシリアルと、GPIO/SPIがプログラムで制御できるようにして、センサデータをシリアルダンプしたり、簡単なモニタプログラムを作ってPC側からセンサデータを取得、モータ制御用テーブルを書き換えたり、そんなことができるように、Rustでドローン制御プログラムを組む予定。

■Rustのコードサイズ

$ size blinky
   text    data     bss     dec     hex filename
  14560       0       4   14564    38e4 blinky

RustによるLチカのコードサイズは、バイナリ領域で14KB程度。STEVAL-DRONE01に乗っているSTM32F103CはFlash 256Kなので、十分に余裕がある。かなりややこしいコードを書いても大丈夫。。

■振り返り
RustでLチカサンプルをビルドして走らせた。先人の説明記事が明確だったのと、インストールツール類が枯れるレベルに仕上がっているので、Rust環境を作るのはほとんどトラブルなくできた。一方、サンプルソースを見てもC言語等とはかなり違っていて、ぱっと見た限り、コードの表記の意味が全然分からない。ライブラリとか自分で自由にプログラミングできるレベルまでは時間かけて勉強しないといけないと感じた。


■参考URL
Rustで組み込みプログラミングの第一歩、LチカとHello Worldを試してみた | DevelopersIO
https://nkon.github.io/Rust-embedded/

*1:1mmでも浮くような