Rustの組み込み系ハンズオンを途中まで

Rustの組み込み系のハンズオンのDiscoveryをやってみます

https://docs.rust-embedded.org/discovery/

ひとまず、環境構築からLEDが光るくらいまでを目標にします

では、ドキュメントを読んでいきます
以下は、後で自分自身が振り返るときに便利なことを書いたメモです

不足や誤りなどありましたら、ご容赦ください

# どの開発ボードを使うか

https://docs.rust-embedded.org/discovery/index.html

> Read the newer book, using a micro:bit

と、あり、micro:bitが入手しやすかったため、micro:bitを使うことにしました

今回は以下から、『micro:bit（マイクロビット） v2.2』を購入しました

https://www.switch-science.com/products/7952?_pos=1&_sid=7db0deee4&_ss=r

micro:bit用のドキュメントはこちらがスタートです

https://docs.rust-embedded.org/discovery/microbit/

# このハンズオンで勉強すること

このドキュメントを勉強することで、
まずは基本的な要素である、ボード向けのソースコードの書き方、ビルドの仕方、ボードへの書き込み方、デバッグの仕方がわかるようです

また、ボードの各機能であるデジタル入出力、PWM、ADC、Serial通信、I2C通信、SPI通信についてや、マルチタスクのコンセプトとかを学べるとかなんとか

# 準備するもの

開発ボードのmicro:bit v2.2と、開発ボードと開発環境のパソコンをつなぐUSBケーブルです

mircro:bitはUSBマイクロB端子です

最近は、USBマイクロB端子が淘汰されてきましたが、この手の開発ボードや安価なボードはいまだUSBマイクロB端子ですね

なんでだろう、安いのかな


# 各種ツール

`rustc -V`でツールチェインのバージョンを確認しておきます

`rustc 1.79.0 (129f3b996 2024-06-10)`とのことなので、ドキュメントの`1.57.0`に比べると多少新しいため、もしかしたら、そのままいかないところもあるかもしれませんが、まあ気にせず進めます

そのほか、ドキュメントにあるとおり、あれこれインストールしました

--- 
実行したコマンドは以下のような感じです

```
$ rustup component add llvm-tools
$ cargo install cargo-binutils --vers 0.3.3
$ cargo size --version
```

そして、以下にしたがって、probe-rsもインストールします

https://probe.rs/docs/getting-started/installation/

それから、下記のコマンドです
```
$ cargo install probe-rs-tools --vers 0.24.0
$ cargo embed --version
```
---
また、ハンズオン用のソースコードがあるとのことなので、GitHubからコピーしてきました

https://github.com/rust-embedded/discovery/

基本的に、`microbit`ディレクトリのソースコードを使うっぽいですね

---

開発環境がWindowsなので、以下も実行します

https://docs.rust-embedded.org/discovery/microbit/03-setup/windows.html

自分の環境にあったarm-none-eabi-gccをインストールします

これをインストールした後は、IDEを再起動しないと`arm-none-eabi-gcc`コマンドが認識されませんでした

---

PuTTYも必要らしいので、インストールします


# micro:bitに電源をつないでみる

https://docs.rust-embedded.org/discovery/microbit/03-setup/verify.html

パソコンとmicro:bitをつないでみました

なんか音が鳴って驚きました
後、めちゃくちゃ光ります
製造時に書かれていたプログラムがパリピだったようです

では、認識されているか確認するために`probe-rs list`を実行します

```
$ probe-rs list
The following debug probes were found:
[0]: BBC micro:bit CMSIS-DAP -- 0d28:0204:9906360200052820AFF95D856A2782CB000000006E052820 (CMSIS-DAP)
[1]: CMSIS-DAP v1 -- 0d28:0204:9906360200052820aff95d856a2782cb000000006e052820 (CMSIS-DAP)
```

なぜか、2つ認識されていますが、いったん見なかったことにします

続いて、ソースコードをビルドしてみます

```
$ cargo embed --target thumbv7em-none-eabihf
```
を実行したところ、
```
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.07s
      Config default
      Target E:\github\rust_embedded\microbit\target\thumbv7em-none-eabihf\debug\rtt-check
       Error The following devices were found:
              [0]: BBC micro:bit CMSIS-DAP -- 0d28:0204:9906360200052820AFF95D856A2782CB000000006E052820 (CMSIS-DAP)
             [1]: CMSIS-DAP v1 -- 0d28:0204:9906360200052820aff95d856a2782cb000000006e052820 (CMSIS-DAP)
              Use '--probe VID:PID'
              You can also set the [default.probe] config attribute (in your Embed.toml) to select which probe to use. For usage examples see https://github.com/probe-rs/probe-rs/blob/master/probe-rs-tools/src/bin/probe-rs/cmd/cargo_embed/config/default.toml .
```
と、怒られました

2つ認識されているため、どちらを使えばいいかわからないらしいですね

`Use '--probe VID:PID'`と教えてくれているため、そうします

```
$ cargo embed --target thumbv7em-none-eabihf --probe 
0d28:0204:9906360200052820AFF95D856A2782CB000000006E052820
```

どこか`VID:PID`なのかがわからないため、ひとまずそれっぽいものをまるごとコピペして、実行です

```
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.06s
      Config default
 Terminal
Hello World
```

おなじみのHello Worldが出たので、よさそうです


# 開発ボードのMCUについて

https://docs.rust-embedded.org/discovery/microbit/04-meet-your-hardware/microbit-v2.html

micro:bit v2では`Nordic nRF52833`を使うよと解説してくれています

ここで、今回のチップの基礎的な知識だけでなく、ARMとはなんぞや？みたいな話もしてくれているのは、さすが親切なドキュメントです

# 用語

https://docs.rust-embedded.org/discovery/microbit/04-meet-your-hardware/terminology.html

以下のような用語が説明されています

- Peripheral Access Crate (PAC)
- The Hardware Abstraction Layer (HAL)
- The Board Support Crate (historically called Board Support Package, or BSP)

HALとBSPは知っていましたが、PACは始めて聞きました

# LED Roulette

続いて、ビルド、フラッシュ、デバッグのやり方を実践です

https://docs.rust-embedded.org/discovery/microbit/05-led-roulette/index.html

---
前回と同じく、`Embed.toml`のチップのアンコメントします

```toml:Embed.toml
[default.general]
chip = "nrf52833_xxAA" # uncomment this line for micro:bit V2
# chip = "nrf51822_xxAA" # uncomment this line for micro:bit V1

[default.reset]
halt_afterwards = true

[default.rtt]
enabled = false

[default.gdb]
enabled = true
```
---
クロスコンパイルのターゲット用のライブラリをインストールしておきます
これは一度実行すればOK

```
# For micro:bit v2
$ rustup target add thumbv7em-none-eabihf
```

二度目以降は、アップデートの確認が走りました
（`up to date`は最新という意味だと知った）

---
ビルドします
```
# make sure you are in the `src/05-led-roulette` directory
# For micro:bit v2
$ cargo build --features v2 --target thumbv7em-none-eabihf
```

---
書きこみます
```
# For micro:bit v2
$ cargo embed --features v2 --target thumbv7em-none-eabihf
```

エラーになりました

```
> cargo embed --features v2 --target thumbv7em-none-eabihf
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.10s
      Config default
      Target E:\github\discovery-master\microbit\target\thumbv7em-none-eabihf\debug\led-roulette
       Error The following devices were found:
              [0]: BBC micro:bit CMSIS-DAP -- 0d28:0204:9906360200052820AFF95D856A2782CB000000006E052820 (CMSIS-DAP)
             [1]: CMSIS-DAP v1 -- 0d28:0204:9906360200052820aff95d856a2782cb000000006e052820 (CMSIS-DAP)     
              Use '--probe VID:PID'
              You can also set the [default.probe] config attribute (in your Embed.toml) to select which probe to use. For usage examples see https://github.com/probe-rs/probe-rs/blob/master/probe-rs-tools/src/bin/probe-rs/cmd/cargo_embed/config/default.toml .
```

デバイスがふたつ認識されているから、困っていますね
`--probe VID:PID`オプションをコマンドにつけるか、`Embed.toml`に書いてくださいとありますが、今後も`--probe`をつけるのがめんどうなので、`Embed.toml`に書くことにします

`Embed.toml`に書くのは以下の通りです
VID、PIDはフラッシュ時のエラー文章を参照しました
```
[default.probe]
usb_vid = "0d28"
usb_pid = "0204"
```

---

そして、実行すると、今度はこうなりました

```
> cargo embed --features v2 --target thumbv7em-none-eabihf
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.09s
      Config default
      Target E:\github\discovery-master\microbit\target\thumbv7em-none-eabihf\debug\led-roulette
      Erasing ✔ [00:00:00] [####################################] 16.00 KiB/16.00 KiB @ 33.13 KiB/s (eta 0s )
  Programming ✔ [00:00:00] [####################################] 16.00 KiB/16.00 KiB @ 17.30 KiB/s (eta 0s )
    Finished in 1.444s
 WARN probe_rs::util::rtt: No RTT header info was present in the ELF file. Does your firmware run RTT?       
    GDB stub listening at 127.0.0.1:1337
During the execution of GDB an error was encountered:
通常、各ソケット アドレスに対してプロトコル、ネットワーク アドレス、またはポートのどれか 1 つのみを使用できます。 (os error 10048)

Caused by:
    通常、各ソケット アドレスに対してプロトコル、ネットワーク アドレス、またはポートのどれか 1 つのみを使用できます。 (os error 10048)
        Done processing config default
```

`127.0.0.1:1337`が既に使用済みか何かのようです

コマンドプロンプトで``netstat -a`を実行しみてみると、確かに使われていました

```
  TCP         127.0.0.1:1337         myPC:0               LISTENING
```

こちらを参考にして、何が使っているか確かめてみました

https://qiita.com/C_HERO/items/c2e9ab1a7bb3537d4cb4

```
$netstat -ano | find "1337"

  TCP         127.0.0.1:1337         0.0.0.0:0              LISTENING       6808
```

PID6808が使っているらしいです

このままコマンドプロンプトを使っても調べられそうですが、めんどうくさいのでタスクマネージャーを開き、「詳細」タブから`6808`を探します

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3821654/7791bb28-707d-108c-0bb4-e12a56dac328.png)

Razer Chromaが使っているらしいです
Razerには心当たりがあるため、このプロセスをkillするのはやめて、ポート番号を変える方向にします

---
`Embed.toml`の`default.toml`にhost:portの設定がありました

https://github.com/probe-rs/probe-rs/blob/master/probe-rs-tools/src/bin/probe-rs/cmd/cargo_embed/config/default.toml

```
[default.gdb]
# Whether or not a GDB server should be opened after flashing.
enabled = false
# The connection string in host:port format wher the GDB server will open a socket.
gdb_connection_string = "127.0.0.1:1337"
```

と、ありましたので、以下のようにしたいと思います
ポート番号は適当です

```
[default.gdb]
enabled = true
gdb_connection_string = "127.0.0.1:1338"
```

これでもう一度フラッシュコマンドを実行します

```
$ cargo embed --features v2 --target thumbv7em-none-eabihf

    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.08s
      Config default
      Target E:\github\discovery-master\microbit\target\thumbv7em-none-eabihf\debug\led-roulette
      Erasing ✔ [00:00:00] [####################################] 16.00 KiB/16.00 KiB @ 34.23 KiB/s (eta 0s )
  Programming ✔ [00:00:00] [####################################] 16.00 KiB/16.00 KiB @ 19.40 KiB/s (eta 0s )
    Finished in 1.332s
 WARN probe_rs::util::rtt: No RTT header info was present in the ELF file. Does your firmware run RTT?       
    GDB stub listening at 127.0.0.1:1338
```

よさそうです

# Debug

https://docs.rust-embedded.org/discovery/microbit/05-led-roulette/debug-it.html

上記のフラッシュコマンドを実行した状態で、別ターミナルを開き、`arm-none-eabi-gdb`をインストールしたので、以下のコマンドを実行します

```
# For micro:bit v2
$ arm-none-eabi-gdb target/thumbv7em-none-eabihf/debug/led-roulette
```

以下が出力されました

```console
GNU gdb (Arm GNU Toolchain 13.3.Rel1 (Build arm-13.24)) 14.2.90.20240526-git
Copyright (C) 2023 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "--host=i686-w64-mingw32 --target=arm-none-eabi".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
--Type <RET> for more, q to quit, c to continue without paging--c
<https://bugs.linaro.org/>.
Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word"...
target/thumbv7em-none-eabihf/debug/led-roulette: No such file or directory.
(gdb)
```
no such file or directoryと言われているため、以下のコマンドを実行し直します
（GDBを抜けるときは、`quit`を入力します）

```
arm-none-eabi-gdb ../../target/thumbv7em-none-eabihf/debug/led-roulette
```

warningが出ていますが、それを無視すると、よさそうです
作業を進めます

```
> arm-none-eabi-gdb ../../target/thumbv7em-none-eabihf/debug/led-roulette
GNU gdb (Arm GNU Toolchain 13.3.Rel1 (Build arm-13.24)) 14.2.90.20240526-git
Copyright (C) 2023 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>      
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "--host=i686-w64-mingw32 --target=arm-none-eabi".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
--Type <RET> for more, q to quit, c to continue without paging--
<https://bugs.linaro.org/>.
Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from ../../target/thumbv7em-none-eabihf/debug/led-roulette...

warning: could not convert 'main' from the host encoding (CP1252) to UTF-32.
This normally should not happen, please file a bug report.
(gdb)
```

---
ポート番号を変えたので、接続先のポート番号を変えて、コマンドを入力します

```
(gdb) target remote :1338
Remote debugging using :1338
0x00000100 in cortex_m::delay::Delay::delay_us (self=0x9e8280c9, us=4291676149)
    at src/delay.rs:57
57                  self.syst.clear_current();
(gdb) 
```

続いて、`break main`です
```
(gdb) break main
Breakpoint 1 at 0x15c: file src\05-led-roulette\src/main.rs, line 9.
Note: automatically using hardware breakpoints for read-only addresses.
```

そして、`continue`をすると、
```
(gdb) c
Continuing.

Program received signal SIGINT, Interrupt.
led_roulette::__cortex_m_rt_main () at src\05-led-roulette\src/main.rs:11
11      fn main() -> ! {
```

よさそうですね

---
breakpointの一覧は`info break`で表示され、`delete <breakpoint-num>`で削除できます
```
(gdb) info break
Num     Type           Disp Enb Address    What
1       breakpoint     keep y   0x0000015c in led_roulette::__cortex_m_rt_main_trampoline at src\05-led-roulette\src/main.rs:9
        breakpoint already hit 1 time
(gdb) delete 1
(gdb) info break
No breakpoints or watchpoints.
```

---
次は`layout src`が出てきますが、Windows環境にはないため、飛ばします
`break <ブレイクポイントを置きたい行数>`でブレイクポイントを貼ります

指示にあるとおり、main.rsの13行目にブレイクポイントを貼ろうとしましたが、どうもずれてしまいます
未使用変数だから、最適化されているのか？

`disassemble /m`で確認してみると、下記のようになっていました
`_y = x;`に対応するアセンブリが存在していないようです
最適化か……？

```
(gdb) disassemble /m
Dump of assembler code for function _ZN12led_roulette18__cortex_m_rt_main17h515d3c049099b5e4E:
10      fn main() -> ! {
=> 0x00000160 <+0>:     sub     sp, #8
   0x00000162 <+2>:     movs    r0, #42 @ 0x2a

11          let _y;
12          let x = 42;
   0x00000164 <+4>:     str     r0, [sp, #0]
   0x00000166 <+6>:     str     r0, [sp, #4]
--Type <RET> for more, q to quit, c to continue without paging-- 

13          _y = x;
14
15          // infinite loop; just so we don't leave this stack frame
16          loop {}
   0x00000168 <+8>:     b.n     0x16a <_ZN12led_roulette18__cortex_m_rt_main17h515d3c049099b5e4E+10>
   0x0000016a <+10>:    b.n     0x16a <_ZN12led_roulette18__cortex_m_rt_main17h515d3c049099b5e4E+10>

--Type <RET> for more, q to quit, c to continue without paging--
```

でもビルドする際に、`unoptimized`と出ています……？

で、よく見てみると、
```
11          let _y;
12          let x = 42;
   0x00000164 <+4>:     str     r0, [sp, #0]
   0x00000166 <+6>:     str     r0, [sp, #4]
```
とあって、どうも`x`と`_y`両方に値がストアされていそうですね

だから、13行目の`_y = x;`に対応する直接的な処理がないみたい？

ということみたいなので、まあよしとしましょう

後は、`stepi`や`monitor reset`などがありました

# Light it up

では、実際にLEDを光らせてみます

https://docs.rust-embedded.org/discovery/microbit/05-led-roulette/light-it-up.html

`main.rs`を書き換えて、実行したところ、LEDがひとつ光りました


# まとめ

ということで、Rust DiscoveryのLEDがひとつ光るまでをやりました

環境構築はめんどうくさいし、つまずくと厄介な作業栄えある第一位だと思いますが、なんとかなってよかったです

もし似たようなところで、つまずいた方がいたら、ご参考になれば幸いです

この後の部分もちょいちょいとやっていきたいと思います
記事にしたほうがよさそうだったら、そうします

以上、ご清聴ありがとうございました