RustでAVRを動かす
こんにちは、naotacoです。これはIoTLT Advent Calendar 2017の12日目の記事です。
IoTといえばモダンな環境がたくさんあるので目移りしてしまいますが、ほかの皆様とネタがかぶってしまうので、初心に返って素のAVRマイコンをいじりたいと思います。Rustで。
AVRマイコンとは
AVRマイコンというのはArduinoのど真ん中に乗っているマイコンのことで、安くて高性能で使いやすい[ref]アセンブリが書きやすいとかTTLレベルでflash書き込みができるとかそういうことですので、Web上でJSを書いたらLEDが光るとかそういうのとはちょっと違います。[/ref]ので、マイコンおじさんに愛され続けているやつです。言い換えると、AVRにプログラムをあらかじめ書き込んでおいて、さらに便利な電源とか周辺回路をセットにして基板に乗せたものがArduinoですね。
現代においてこれを素のまま使うというのはただの苦行というか趣味でしかないのですが、ネタかぶりを避けるためには犠牲がつきものです。虎穴に入らずんばなんとやら。勉強もかねてこれでやっていきます。
ちなみに昔はAVRの開発をアセンブリでやる人が多かったようですが、最近はC言語で書かれるのが一般的だと思います。Atmel謹製のIDEもあるので、いまから普通に始める人はそちらを使うとよいでしょう。
Rust on AVR
そうはいってもマイコンを動かすためにC言語を書くというのは全然おもしろくないので、今回はRustを使ってAVRを動かす方向で考えます。なぜRustなのかというと、最近私がRustを勉強しはじめたところだからです。
何も考えず始めたんですが、やってみると低レベルプログラミングを安全に低コストでできるようにするというポリシーがとても組み込み向きだなと思っています。もうC/C++はいやだお・・・。コンパイル時に変数の生存期間を決定してチェックしてくれたりするので、安全性が高いですがコンパイルを通すのが大変です(初心者並みの感想)。あと大規模なコードになってくるとどうなるのかよくわかりません。
さてそんなRustですが、当然デフォルトでAVRターゲットのクロスコンパイルができるわけはありません。それ用にコンパイラを用意してやる必要があります。
avr-rust
avr-rust: https://github.com/avr-rust/rust
avr-rustというのがあり、RustでAVRを動かせると書いてあるので試してみることにします。AVR-LLVMというのがあるんですね。avr-rustもアレだけど、LLVMさんマジパネェ。
例によってこいつ自身をコンパイルする必要がありますが、さすがにこれをWindowsでこれをコンパイルしにいく根性はありません。とりあえずUbuntu on Windows環境で試します。つまり、Windows上のUbuntuでRustのコードをAVR向けにクロスコンパイルしようとしている状況です。
基本的にはREADMEにある通りでよいのですが、Windows上のUbuntuだからか足りないパッケージがけっこうあります。また/optへのインストールも失敗したので、インストール先は自分のhome下にしました。あと、rustupとcargo, xargoが必要なので入れておきます。
[code lang=”shell”] # Ubuntu on Windows10 ですが、普通のでも多分同じようにやればできる # packages sudo apt-get install build-essential python cmake libffi-dev cargo
# rustup. インストール後に表示される場所にパスを通す. curl https://sh.rustup.rs -sSf | sh # xargo cargo install xargo |
# avr-rust git clone https://github.com/avr-rust/rust.git mkdir build && cd build
../rust/configure \ –enable-debug \ –disable-docs \ –enable-llvm-assertions \ –enable-debug-assertions \ –enable-optimize \ –prefix=~/.avr-rust # インストール先はhome下
make make install rustup toolchain link avr-toolchain $(realpath $(find . -name ‘stage1’)) rustup default avr-toolchain [/code]
Windows 10 (build 17025)ならこれでavr-rustがコンパイルできると思います。依存submoduleがめちゃくちゃ多いので、makeのうちsubmoduleのアップデートに死ぬほど時間がかかります。いろんなパッケージがないとかで詰まりまくったので、おとなしく普通のUbuntuでやればよかったという後悔があります。
~/.avr-rust/binにrustcなどができていれば成功です。
Blink in Rust
コンパイラだけできてもしょうがないので、Lチカをやりましょう。
blink: https://github.com/avr-rust/blink
[code lang=”shell”] $ git clone git@github.com:avr-rust/blink.git $ XARGO_RUST_SRC=~/.avr-rust/ rustup run avr-toolchain xargo build –target avr-atmega328p –release
Compiling arduino v0.1.0 Compiling blink v0.1.0 (file:///home/naotaco/blink) Finished release [optimized] target(s) in 0.60 secs [/code]
かっこいいですね。target/avr-atmega328p/blink.elf ができています。
[code lang=”shell”] $ tree . ├── avr-atmega328p.json ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── src │ └── main.rs ├── target │ ├── avr-atmega328p │ │ └── release │ │ ├── blink.d │ │ ├── blink.elf ★これ │ │ ├── … │ │ └── native │ └── release │ ├── … │ └── native └── Xargo.toml
15 directories, 13 files [/code]
焼く
さて、いよいよ焼く段ですが、今回はUSBaspという太古のAVRライタを使用します。細かいことは過去エントリを読んで頂くとして、USB接続して焼きます。というところまでやって気づいたんですが、Ubuntu on WindowsではUSBデバイスの使用はサポートされていないようです。
仕方なくWindowsにドライバを入れようとしましたが何をやってもだめでした。結局普通のLinux Mintマシンを出してきて、そこにavrdudeなどを入れて焼くことにします(最初から全部Linuxでやれよ)。
[code lang=”shell”] # ここから本当のUbuntu, ただしWindowsでも動くはず、ドライバさえ当たれば、、 sudo apt-get install gcc-avr binutils-avr gdb-avr avr-libc avrdude [/code]
さっきできたelfファイルはそのままだと焼けないGNU executableなので、これをIntel HEXという形式に変換してやる必要があります。
[code lang=”shell”] avr-objcopy -j .text -j .data -O ihex blink.elf blink.hex [/code]
するとblink.hexというファイルができ、これをavrdudeで焼くことができます。
[code lang=”shell”] avrdude -c usbasp -p atmega328p -U flash:w:blink.hex avrdude: warning: cannot set sck period. please check for usbasp firmware update. avrdude: AVR device initialized and ready to accept instructions
Reading | ################################################## | 100% 0.02s |
(略)
Writing | ################################################## | 100% 1.21s |
(略)
Reading | ################################################## | 100% 1.12s |
avrdude: verifying … avrdude: 254 bytes of flash verified
avrdude: safemode: Fuses OK (H:07, E:D9, L:62)
avrdude done. Thank you.
[/code]
Warningが出まくっていますが、焼けます。
— naotaco (@naotaco) December 11, 2017
— naotaco (@naotaco) December 11, 2017
光ったあああああああ
ちなみに、Atmega328p用の設定ファイルしかリポジトリに含まれていないため、そのままだとほかのマイコン用にはコンパイルできません。とりあえず、同じ構造で容量違いのAtmega168で試そうと、jsonを2カ所、Xargo.tomlを1カ所、マイコン名を328から168に変更してコンパイルしたところAtmega168でも動作させることができました。
コード
https://gist.github.com/naotaco/676f8e2a9b2f664882175ae1ec60f8fd
さてこのサンプルリポジトリにあるLチカのコードですが、普段見慣れているシンプルなRustのコードとは似ても似つかない禍々しいオーラを放っていることが見て取れます。no_stdアトリビュートが指定されているので普通の関数は何一つ使えません。またループの実装にasm(インラインアセンブリ)が使われていて不気味ですね。随所にあるwrite_volatileを囲うunsafeもおぞましい。ちなみにデフォルトだとsmall_delayのループ回数が400000になっていますが、これだと長すぎるので上の動画を撮るときには0を一つ取っています。
コードもこんな感じですし使えるライブラリも皆無ですので、残念ながら実用にはほど遠いようです。このような状況を切り開いていける本物のエンジニア各位におかれましては、冬休みの自由研究の題材にいかがでしょうか。私はおとなしくLinux/Windowsで書いていくことにします。
まとめ
ここまで書いておいていま気づいたんですが、Internetにつながるところまで進めませんでした。ごめんなさい。
明日は@shiogenさんの順番です。お楽しみにどうぞ。