4
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

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

Last updated at Posted at 2021-05-24

目的

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

環境

利用するライブラリ:avr-hal(Versionはハッシュ値が885e8ecのもの)
OS:Ubuntu20.04.1
使用したAVRライター: Arduino Nano Everyをライター化したもの

注意:現在(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を用意し、コンパイルするときにそれを参照するよう指示します。

嬉しいことにavr-halCustom Target Specification用意してくださってるので、それを使わさせていただきます。このatmega328p.jsonをblinkクレート直下に設置します。

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

また、サポートしていないtargetでRustを使うためには、そのtarget用のcoreクレートが必要なため、atmega328pに向けにcoreをコンパイルしてもらうように設定します。このような用意されているRust標準ライブラリを使わずに、指定したtargetに合わせてローカルでコンパイルすることをstd Aware Cargoと言うようです2

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

[build]
target = "atmega328p.json"

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

依存関係

最後に、avr-halに従ってCatgo.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向けにコンパイルする場合において

features = ["rt", "atmega328p"]

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

コードの作成

完成コード

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();
    }
}

コードの解説

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

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

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

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

Pinの操作

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

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);

ちなみに、Peripheralsportc::Partsportc::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のドキュメントを見れば問題ないと思います。詳細については、こちらをどうぞ。

書き込み

自作ライターを利用します。

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

sudo apt install avrdude

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

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のときは、次のようになる。

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*がどうなっているかを判断するには、次のコマンドの結果をライター非接続時と接続時の前後で見比べるのがてっとり速いと思う。

ls /dev/ | grep tty
  1. ConfigurationのHierarchical structureのNoteに書いてあります。

  2. build-stdについてはUnstable Featuresを参照してください。std Aware Cargoを利用するケースについてはここを参照してください。

  3. rust-lang.orgのNever type

4
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?