STM32 NUCLEOボードによる組み込みRust開発環境構築

# 記事の概要
組み込みRustについて、STM32 NUCLEO-F446RE を用いて、開発環境の構築からサンプルプロジェクトによる動作確認までを説明します。

現在、STM32 NUCLEO-F446RE は入手困難ですが、本記事の内容に沿って設定を修正すれば[他のSTM32F4シリーズを使用したボード](https://www.stmcu.jp/design/hwdevelop/nucleo/?key%5B%5D=65)にも適用できます。

# 開発環境構築

本記事の内容は以下のWindows環境で動かしています。
Linux (Ubuntu 20.04 LTS)でも同様のことができるはずです

- PC
    - Windows 11
- マイコンボード
    - STM32 NUCLEO-F446RE
        - CPU：STM32F446RET6
        - CPUコア：ARM32bit Cortex-M4(+FPU)
        - フラッシュ：512KB
        - SRAM：128KB+4KB(Backup)

開発環境の構築は以下のサイトを参照しました。

https://zeptoelecdesign.com/rust-embedded2/


上記のサイトは Nucleo-F303K8 を対象としており、本記事では主に設定の差分について解説したいと思います。

まずは上記サイトを参照して以下の準備をしてください。
コマンドの入力にはPowerShellもしくはコマンドプロンプトを使用してください。

- arm-none-eabi-gdb のインストール
    - [Arm GNU Toolchain Downloads](https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads)のサイトから現時点（2023/01/10）での最新の arm-gnu-toolchain-12.2.rel1-mingw-w64-i686-arm-none-eabi.exe をダウンロードして実行
- OpenOCD のインストール
    - [GNU MCU Eclipse OpenOCD v0.10.0-12 20190422](https://github.com/ilg-archived/openocd/releases/v0.10.0-12-20190422)から gnu-mcu-eclipse-openocd-0.10.0-12-20190422-2015-win64.zip をダウンロードして、解凍フォルダ内のopenocd.exeファイルを実行
    - インストール後は、環境変数設定画面において、openocd.exeを格納しているフォルダへのPathを通す
    - USBでボードをPCと接続し `openocd -f interface/stlink.cfg -f target/stm32f4x.cfg` を実行して、上記サイトと同様のログが出ることを確認
        - 他のデバッガーやボードを使用する場合は、 \OpenOCD フォルダ内の \scripts フォルダの中から自分の環境に一致するものを選ぶ 
- Rustup のインストール
    - RustupはRustのインストールと管理をするソフト
    - Windows にRustup をインストールするには、 [Visual Studio C++ Build tools](https://visualstudio.microsoft.com/ja/visual-cpp-build-tools/)のインストールも必要
    - 具体的なインストール方法は以下のサイトを参照。（書籍では「基礎から学ぶ組み込みRust」の2章が詳しい）

https://www.rust-lang.org/ja/tools/install

https://zenn.dev/chikoski/articles/2115efed69aed014616f

- クロスビルドツールチェインのインストール
    - マイコンボードに対応したクロスビルドツールチェインをインストール
    - NUCLEO-F446REのCPUはCortex-M4 (with FPU)なので `rustup target add thumbv7em-none-eabihf` を実行
    - thumbv6m-none-eabi
        - Cortex-M0
        - Cortex-M0+
    - thumbv7em-none-eabi
        - Cortex-M4 (no FPU)
        - Cortex-M7 (no FPU)
    - thumbv7em-none-eabihf
        - Cortex-M4F (with FPU)
        - Cortex-M7F (with FPU)
    - thumbv7m-none-eabi
        - Cortex-M3
- Cargo-generate のインストール
    - `cargo install cargo-generate` を実行
- VS Code のインストール
- VS Code拡張ツール Cortex-Debug のインストール

Linux での開発環境構築する場合もWindowsと大差はありません。
以下のサイトなどを参照ください。

https://qiita.com/mitoneko/items/48303c33cf5c674cda3a


# サンプルプロジェクトで動作確認

以下のコマンドを実行するとサンプルプロジェクトが生成されます
```
cargo generate -–git https://github.com/rust-embedded/cortex-m-quickstart.git
```

サンプルプロジェクトはSTM32 Discovery Board用の設定なので、STM32 NUCLEO-F446RE Board用の設定に修正します

## .cargo/config.toml

サンプルプロジェクトのトップ階層から .cargo/config.toml を開いて修正します。
ターゲットを Cortex-M3用の `thumbv7m-none-eabi` から Cortex-M4F and Cortex-M7F (with FPU)用の `thumbv7em-none-eabihf` に変更します。
また、デバッグツールのopenocdを有効にします

修正前
```
[target.thumbv7m-none-eabi]
```

```
# runner = "arm-none-eabi-gdb -q -x openocd.gdb"
```

```
target = "thumbv7m-none-eabi"        # Cortex-M3
```

```
# target = "thumbv7em-none-eabihf"     # Cortex-M4F and Cortex-M7F (with FPU)
```

修正後
```
[target.thumbv7em-none-eabihf]
```

```
runner = "arm-none-eabi-gdb -q -x openocd.gdb"
```

```
# target = "thumbv7m-none-eabi"        # Cortex-M3
```

```
target = "thumbv7em-none-eabihf"     # Cortex-M4F and Cortex-M7F (with FPU)
```

## .vscode/launch.json

VS Codeのデバッグ用設定を修正します。サンプルプロジェクトのトップ階層から .vscode/launch.json を開いて修正します。

### Device

CPU の型番を修正します。STM32 NUCLEO-F446RのCPUは `STM32F446RET6` です

修正前
```
"device": "STM32F303VCT6",
```

修正後
```
"device": "STM32F446RET6",
```

### OpenOCD Config Files

STM32 NUCLEO-F446RのOpenOCD Config Files の名称 `stm32f4x.cfg` に修正します
他のマイコンボードのOpenOCD Config Fileの名称を知りたい場合は、以下のサイトから探してください。

https://sourceforge.net/p/openocd/code/ci/master/tree/tcl/target/

修正前
```
"configFiles": [
    "interface/stlink-v2-1.cfg",
    "target/stm32f3x.cfg"
],
```

修正後
```
"configFiles": [
    "interface/stlink-v2-1.cfg",
    "target/stm32f4x.cfg"
],
```

### SVDの設定とダウンロード

マイコンのレジスタ情報が記載されたsvd ファイルを設定します

修正前
```
"svdFile": "${workspaceRoot}/.vscode/STM32F303.svd", 
```

修正後
```
"svdFile": "${workspaceRoot}/.vscode/STM32F446.svd",
```

また、以下のサイトから自分のマイコンボードに対応したSVDファイルをダウンロードし、.vscodeフォルダに格納してください。

https://github.com/posborne/cmsis-svd/tree/master/data/STMicro

### CPU Frequency

CPUのマニュアルからCPU Frequencyを調べて修正します。
例えば STM32F446のCPU Frequencyは16 MHz です
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/150620/e3180f88-18d1-8541-bb6a-1a82329832d1.png)

修正前
```
"cpuFrequency": 8000000,
```

修正後
```
"cpuFrequency": 16000000,
```

## Cargo.toml

ターゲットマイコンを修正します。
クレート stm32f4xx-hal を使用する予定なので、以下のサイトの設定にします

https://crates.io/crates/stm32f4xx-hal

他のクレートを使用する場合は、それぞれのクレートのページに記載された設定にしてください。

修正前
```
[dependencies]
cortex-m = "0.6.0"
cortex-m-rt = "0.6.10"
cortex-m-semihosting = "0.3.3"
panic-halt = "0.2.0"

# [dependencies.stm32f3]
# features = ["stm32f303", "rt"]
# version = "0.7.1"
```

修正後
```
[dependencies]
embedded-hal = "0.2"
nb = "1"
cortex-m = "0.7"
cortex-m-rt = "0.7"
# Panic behaviour, see https://crates.io/keywords/panic-impl for alternatives
panic-halt = "0.2"

[dependencies.stm32f4xx-hal]
version = "0.13.2"
features = ["stm32f446"] # replace the model of your microcontroller here
```

## memory.x

memory.x の FLASH と RAM のアドレスとサイズを修正します。
これらの値はCPUのマニュアルのmemory mapに記載されています。

例えば、STM32F446のFLASHの開始アドレスは 0x08000000 であり、サイズは 512KBであり、SRAMの開始アドレスは 0x20000000 であり、サイズは 128KBであることが分かります

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/150620/1e161d3c-0846-728b-d36a-0abad4ab40cf.png)

修正前
```
  FLASH : ORIGIN = 0x00000000, LENGTH = 256K
  RAM : ORIGIN = 0x20000000, LENGTH = 64K
```

修正後
```
  FLASH : ORIGIN = 0x8000000, LENGTH = 512K
  RAM : ORIGIN = 0x20000000, LENGTH = 128K
```

# 動作確認

実際にプログラムをボードに書き込んで動作確認してみます。
動作確認のコードは以下のサンプルを参照しています。

https://github.com/krenzlin/rust-stm32f446-blinky

デバッグ方法の詳細は [組み込みRust開発環境構築【STM32 NucleoとVSCodeで構築】](https://zeptoelecdesign.com/rust-embedded2/#toc11)の「サンプルコードの実行」をご参照ください。

ボードをPCにUSBで接続しVS Codeでサンプルプロジェクトを開き、main.rsを以下のコードに置き換えてから、"Run and Debug"をクリック→プルダウンのDebug（OpenOCD)を選択→矢印マークをクリックの順番で、プログラムのビルド、書き込み、デバッグが実行されます

```Rust:main.rs
#![deny(unsafe_code)]
#![no_std]
#![no_main]

use cortex_m_rt as rt;
use stm32f4xx_hal as hal;
use panic_halt as _;
use hal::prelude::*;

#[rt::entry]
fn main() -> ! {
  if let Some(peripherals) = hal::pac::Peripherals::take() {

      let gpioa = peripherals.GPIOA.split();
      let mut led = gpioa.pa5.into_push_pull_output();

      let gpioc = peripherals.GPIOC.split();
      let button = gpioc.pc13; 

      loop {
          if button.is_high() {
              led.set_low();
          } else {
              led.set_high();
          }
      }
  }
}
```

USERスイッチを押下してLEDが点灯すれば正常に動作しています

ここで使用しているクレート embedded-hal については以下の解説をご参照ください
（ただし以下のサイトで解説しているのはstm32f4xx_halではなく、stm32f3xx_halになります）

https://zeptoelecdesign.com/rust-embedded3/