[Rust,AVRマイコン] RustコードをATmega328pへ書き込む

# 目的
ATmega328p向けのLチカプログラムをRustで作成しそれをコンパイルします。

# 環境
利用するライブラリ：[avr-hal](https://github.com/Rahix/avr-hal/tree/885e8ec6d6d2fe34f26a1e2697a99f41092f0985)（Versionはハッシュ値が`885e8ec`のもの）
OS：Ubuntu20.04.1
使用したAVRライター: [Arduino Nano Everyをライター化したもの](https://qiita.com/mutuya/items/0c793863374ea25c05cb)

注意：現在（2021/5/21）、avr-halを利用している回帰処理を含んだプログラムをコンパイルするとき、`nightly-2021-01-07`より後のコンパイラではうまく行えないようです。そのためavr-halは`nightly-2021-01-07`を使うことを勧めています。

# バイナリクレートの作成
## ツールチェインの固定
まずは、普通にバイナリクレート（ここではblinkと命名）を作成します。作成後、クレートの直下に`rust-toolchain`ファイルを作成し、利用するツールチェインを指定します。

```:blink/rust-toolchain
nightly-2021-01-07
```

## Atmega328pを`taget`に指定する
次にrustcではAtmega328pに対応していないため、[Custom Target Specification](https://doc.rust-lang.org/rustc/targets/custom.html)を用意し、コンパイルするときにそれを参照するよう指示します。

嬉しいことに`avr-hal`が`Custom Target Specification`を[用意してくださってる](https://github.com/Rahix/avr-hal/blob/885e8ec6d6d2fe34f26a1e2697a99f41092f0985/avr-specs/avr-atmega328p.json)ので、それを使わさせていただきます。この`atmega328p.json`をblinkクレート直下に設置します。

`Custom Target Specification`が用意できたら`.cargo/config`を用いて、コンパイル時に`atmega328p.json`を用いるよう指定します。ちなみに`.cargo/config`でも`.cargo/config.toml`でもいいです[^1]。

[^1]: [ConfigurationのHierarchical structure](https://doc.rust-lang.org/cargo/reference/config.html#hierarchical-structure)のNoteに書いてあります。

また、サポートしていない`target`でRustを使うためには、その`target`用の[core](https://doc.rust-lang.org/core/index.html)クレートが必要なため、`atmega328p`に向けに`core`をコンパイルしてもらうように設定します。このような用意されているRust標準ライブラリを使わずに、指定した`target`に合わせてローカルでコンパイルすることを`std Aware Cargo`と言うようです[^2]。

[^2]: `build-std`については[Unstable Features](https://doc.rust-lang.org/cargo/reference/unstable.html#build-std)を参照してください。`std Aware Cargo`を利用するケースについては[ここ](https://github.com/rust-lang/wg-cargo-std-aware#use-cases)を参照してください。

```toml:blink/.cargo/config
[check]
target = "atmega328p.json"

[build]
target = "atmega328p.json"

[unstable]
build-std = ["core"]
```

## 依存関係
最後に、[avr-hal](https://github.com/Rahix/avr-hal/tree/885e8ec6d6d2fe34f26a1e2697a99f41092f0985#starting-your-own-project)に従って`Catgo.toml`に依存関係を書いていきます。

```toml:blink/Cargo.toml
[package]
name = "blink"
version = "0.1.0"
authors = ["mutuya"]
edition = "2018"

[dependencies]
panic-halt = "0.2.0"

[dependencies.atmega328p-hal]
git = "https://github.com/Rahix/avr-hal"
rev = "885e8ec"
features = ["rt", "atmega328p"]

# Configure the build for minimal size
[profile.dev]
panic = "abort"
lto = true
opt-level = "s"

[profile.release]
panic = "abort"
codegen-units = 1
debug = true
lto = true
opt-level = "s"

```

`atmega328p-hal`向けにコンパイルする場合において

```toml
features = ["rt", "atmega328p"]
```

がとっても大切です。`atmega328p`を忘れてもコンパイル時にわかりやすいエラーが出るためあまり問題にならないと思いますが、`rt`を忘れるとちょっと厄介です。理由は後述します。

# コードの作成
# 完成コード
```rust:blink/src/main.rs
#![no_std]
#![no_main]

extern crate panic_halt;

use atmega328p_hal::clock;
use atmega328p_hal::prelude::*;
use atmega328p_hal::pac::Peripherals;
use atmega328p_hal::delay::Delay;

#[atmega328p_hal::entry]
fn main() -> ! {
    let mut delay = Delay::<clock::MHz8>::new();

    let peripherals = Peripherals::take().unwrap();
    let mut portd = peripherals.PORTD.split();
    let mut led = portd.pd4.into_output(&mut portd.ddr);

    led.set_low().unwrap();
    
    loop {
        delay.delay_ms(500_u16);
        led.toggle().unwrap();
    }
}
```

## コードの解説

エントリーポイントは次のように書かれます。

```rust
#[architecture::entry]
fn main() -> ! {
    // do something.
}
```

この`fn main() -> !`は`Never Type`と呼ばれるものであり、`main()`が何も返さない（≒ 終了しない）ことを表しています[^3]。

`#[architecture::entry]`は今回の場合、`#[atmega328p_hal::entry]`となります。ここで前述した`features = ["rt"]`を忘れているとエラーになるため気をつけてください。
このエラー内容は「はぅぅ。entryが見つからないよぉぉ。」と言うだけで「`rt`を指定してください」とは言ってくれず、`avr-hal`のクレート内容をくまなく探すことになります。。

[^3]: [rust-lang.orgのNever type](https://doc.rust-lang.org/reference/types/never.html)

# Pinの操作
操作したいPinの取得は次のように行います。

```rust
use atmega328p_hal::port::portc;
use atmega328p_hal::port::mode::Output;

// 様々なレジスタへのアドレスが組み込まれている構造体Peripheralsを取得する
let peripherals = Peripherals::take().unwrap();

// Peripheralsの中から、GPIO Port Cを制御しているレジスタのアドレスを取り出す。
let mut portc: portc::Parts = peripherals.PORTC.split();

// portcから制御したいPin（ここではPC5）を取り出す。
// でも、portc.pc5は初めはinputとなっているから、outputに変換する。
let mut led: portc::PC5<Output> = portc.pc5.into_output(&mut portc.ddr);
```

ちなみに、`Peripherals`や`portc::Parts`、`portc::PC5<Output>`はレジスタへのアドレスの情報を持っていますが、値として持っていません。マクロやトレイトを駆使しプログラムとして書き込まれています。本当にすごいと思った（小並感）。

AVRマイコン組み込み初心者には「`portc.pc5.into_output(&mut portc.ddr);`の`&mut portc.ddr`って何だ？」と思われるかもしれません。AVRマイコンのPinの制御には3つのレジスタが必要で、それぞれDDRレジスタ、PORTレジスタ、PINレジスタと呼ばれています。

DDRレジスタを書き換えることで、Pinを出力で扱うか入力で扱うかを選択できます。PORTレジスタはPinの出力を設定（High or Low）し、PINレジスタではPinがHighの入力を受けているかLowの入力を受けているかが格納されています。

（この&mut portc.ddrなんですが、実は中身は空っぽです。）

操作については、[portc::PC5のドキュメント](https://rahix.github.io/avr-hal/atmega328p_hal/port/portc/struct.PC5.html)を見れば問題ないと思います。詳細については、[こちら](https://rahix.github.io/avr-hal/avr_hal_generic/port/index.html)をどうぞ。

# 書き込み
[自作ライター](https://qiita.com/mutuya/items/0c793863374ea25c05cb)を利用します。

書き込みには`avrdude`を用います。インストールは次の通り。

```shell
sudo apt install avrdude
```

書き込み時は次のように実行します。

```shell
sudo avrdude -v -c avrisp -p m328p -b 19200 -P $(USB Device) -U flash:w:$(ELF File)
```

`$(USB Device)`は接続しているライター（`/dev/ttyACM*`と表されてると思う．．．）に置き換え、`$(ELF File)`はコンパイルの成果物である`blink.elf`を入れる。

カレントディレクトリが`blink`で、自作ライターが`/dev/ttyACM0`のときは、次のようになる。

```shell
sudo avrdude -v -c avrisp -p m328p -b 19200 -P /dev/ttyACM0 -U flash:w:./target/atmega328p/release/blink.elf
```

もちろん、`blink.elf`はDebugでコンパイルしたら`debug`フォルダ内にあるので、適宜、変えること。

## メモ、自作ライターの判別

接続しているライターの`/dev/ttyACM*`がどうなっているかを判断するには、次のコマンドの結果をライター非接続時と接続時の前後で見比べるのがてっとり速いと思う。

```shell
ls /dev/ | grep tty
```