RustでSTM32マイコンをLチカしてみる(Embedded HAL)

[こちらの記事](https://qiita.com/tatsuya6502/items/7d8aaf3792bdb5b66f93)をみて興味を持っていろいろ試していたところ、[本家ブログ](http://blog.japaric.io/brave-new-io/)にてHAL traitsの記事が出ていたのでこれを使ってLチカしてみました。[^1]

[^1]: というか、ちょうど手をつけ始めたタイミングでレジスタアクセスの方法が切り替わってしまっていろいろ混乱してみたりして。

# 使用するボード Nucleo-F303RE
1/21現在で実装されているHALはSTM32F30xだけのようです。ちょうど適合するマイコンが載っているボードが手元にあったのでこれを使いました。

# 環境構築
Linux(Ubuntu 16.04 LTS x86_64)で環境を作成します。WindowsやmacOSでは試していませんが、[こちら](https://japaric.github.io/discovery/03-setup/README.html#OS%20specific%20instructions)が参考になると思います。

## インストール

```
$ rustup default nightly
$ rustc -V
rustc 1.25.0-nightly (15a1e2844 2018-01-20)

$ sudo apt install binutils-arm-none-eabi
$ arm-none-eabi-ld -V | head -n1
GNU ld (2.26-4ubuntu1+8) 2.26

$ sudo apt-get install gdb-arm-none-eabi
$ arm-none-eabi-gdb --version | head -n1
GNU gdb (7.10-1ubuntu3+9) 7.10

$ sudo apt install openocd
$ openocd -v 2>&1 | head -n1
Open On-Chip Debugger 0.9.0 (2015-09-02-10:42)

$ sudo apt install libssl-dev libssh-dev pkg-config cmake
$ cargo install cargo-clone
$ cargo install cargo
$ cargo -V
xargo 0.3.10
cargo 0.26.0-nightly (6a8eb71f6 2018-01-13)

$ rustup component add rust-src
$ cargo install cargo-edit
```

## gdb-dashboardレイアウト
gdb-dashboardのGitHubリポジトリ (https://github.com/cyrus-and/gdb-dashboard) を取得して、`.gdbinitスクリプト`をホームディレクトリ直下に保存します。保存後、safe-path設定を追加します。

```
$ git clone https://github.com/cyrus-and/gdb-dashboard.git
$ cp gdb-dashboard/.gdbinit ~/
$ echo 'set auto-load safe-path /' >> ~/.gdbinit
```

# Cargoプロジェクト設定

依存モジュールとして

- stm32f30x-hal

を設定します。

```Cargo.toml
[package]
name = "stm32f30x_hal_blinky"
version = "0.1.0"
authors = ["..."]
description = "LED blinky for STM32F303RE using stm32f30x_hal"
keywords = ["arm", "cortex-m", "blinky"]
categories = ["embedded", "no-std"]
license = "MIT OR Apache-2.0"
repository = "https://"

[profile.dev]
codegen-units = 1
incremental = false

[profile.release]
lto = true
debug = true

[dependencies.stm32f30x-hal]
version = "0.1.1"
features = ["rt"]

[dependencies.cortex-m]
version = "0.4.1"

[dependencies.cortex-m-rt]
version = "0.3.12"
features = ["abort-on-panic"]

[dependencies.cortex-m-semihosting]
version = "0.2.0"
```

Cortex-M4(FPUあり)をターゲットに設定

```..cargo/config
[target.thumbv7em-none-eabihf]
runner = 'arm-none-eabi-gdb'
rustflags = [
  "-C", "link-arg=-Tlink.x",
  "-C", "linker=arm-none-eabi-ld",
  "-Z", "linker-flavor=ld",
]

[build]
target = "thumbv7em-none-eabihf"
```

STM32F303REのメモリ容量を設定

```memory.x
MEMORY
{
  /* NOTE K = KiBi = 1024 bytes */
  /* TODO Adjust these memory regions to match your device memory layout */
  FLASH : ORIGIN = 0x08000000, LENGTH = 512K
  RAM   : ORIGIN = 0x20000000, LENGTH = 64K
}
```

# ソースコード

```src/main.rs
#![no_std]

extern crate cortex_m;

// "as hal"をつける習わしらしい？
extern crate stm32f30x_hal as hal;

// "extern crate stm32f30x"を *書かない*
// 代わりに"use hal::stm32f30x"を書く
use hal::stm32f30x;

// おまじない
use hal::prelude::*;

// GPIOでOutputするための指定
// NucleoボードではPA5がLEDに接続されている
use hal::gpio::{Output, PushPull};
use hal::gpio::gpioa::{PA5};

// Systickを用いたディレイ
use hal::delay::Delay;

fn main() {
    let cp = cortex_m::Peripherals::take().unwrap();
    let p = stm32f30x::Peripherals::take().unwrap();

    let mut flash = p.FLASH.constrain();
    let mut rcc = p.RCC.constrain();

    // 現在はHSIをクロック源として設定のみ
    // 何も指定しなければデフォルトの8MHz
    let clocks = rcc.cfgr.freeze(&mut flash.acr);

    // GPIOAのPA5をOutput-PushPullで使用するオブジェクト
    let mut gpioa = p.GPIOA.split(&mut rcc.ahb);
    let mut pa5: PA5<Output<PushPull>> = 
            gpioa.pa5.into_push_pull_output(&mut gpioa.moder, &mut gpioa.otyper);

    // Systickベースでディレイするオブジェクト
    let mut delay = Delay::new(cp.SYST, clocks);

    loop {
        pa5.set_high();
        delay.delay_ms(1_000_u16);
        pa5.set_low();
        delay.delay_ms(1_000_u16);
    }
}
```

- - -