「基礎から学ぶ組込みRust」をRust 2021で書いてみました with Wio Terminal
はじめに
組込みRustの書籍として有名な「基礎から学ぶ組込みRust」に掲載されているプログラムを一通り書いた結果、僅かながらハードウェア制御が分かったような気がしました。
とても為になりましたが下記の点で苦労しました。
- GitHub上の情報が少ない(2023年7月16日時点で18リポジトリ!?)
- そのうち1つは私のリポジトリです
-
Wio Terminal用のクレートの情報が少ない
- 書籍では wio_terminal = "0.3" ですが2023年7月16日時点では wio_terminal = "0.7.0"
- ドキュメントも少し物足りない
- マイナーアップデートでモジュール構造やマクロが変更されている
-
embedded-graphics-simulatorクレートの導入が上手くいかなかった。
- SDL2のインストールが難しい。何かを間違っているのですが、分かりませんでした。
Rustは比較的新しい言語のため日々進歩している分過去の情報を適用できない状況があるようです。
この記事では表題の書籍に掲載されているプログラムをRust 2021で書いてみて情報更新を図りたいと思います。
皆様のお役に立てれば幸いです。
追記: v0.7.0 でのプログラム作成(2023年8月1日)
記事を書く少し前にwio_terminalクレートがv0.7.0へアップデートされましたが破壊的変更が良くわからなくてこの記事ではv0.6.1で書きました。
最近やっとv0.7.0が示している内容が分かってきましたので作例を基にv0.7.0でプログラムを書きました。
今度こそ最新版です。
修正: 2023/8/14
より簡単に環境構築ができるように修正しました。
- VS Code の拡張機能の追加
- GitHub上のリポジトリをローカルフォルダに保存する方法を
Git: Clone
コマンドに変更 - 実行コマンドを
cargo hf2 --release
に変更。Release ビルドにしないと遅い - コンパイルではなくプログラム実行に語彙を修正
- Wio Terminal の開発環境インストール一覧から
cargo install cargo-generate
を削除 - 最小構成リポジトリのみをテンプレート化
2023/8/28
- hf2のコマンド修正
結論: GitHubリポジトリ
作成したプログラムを下記のリポジトリに保存しています。
プログラム毎にcargo new
しているので普通にcargo hf2 --release
でコンパイルできます。
筆者の環境
- Windows 10 Home
- 64 ビット オペレーティング システム、x64 ベース プロセッサ
- Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz 2.59 GHz
- 実装RAM 16.0 GB
組込みRustの準備
書籍の第2章「開発環境の準備」に沿って説明します。
必要なハードウェアと購入先
この記事ではWio Terminal本体の購入が必要になります。
機材名 | 数量 | URL |
---|---|---|
Wio Terminal | 1 | 秋月電子通商 |
USB Type-Cケーブル | 1本 | (最近のスマホはType-Cに置き換わりつつあると思うので、お持ちのスマホ用ケーブルで良いと思います) |
Rustの開発環境の準備
- Rustのコンパイラに関わるC++ツールをインストールします
- 個人の方はVisual Studio Communityから下記のアイテムをインストールします
- NET デスクトップビルドツール
- C++ によるデスクトップ開発
- ユニバーサル Windows プラットフォームビルドツール
- Rustのビルドツールをインストールします
- Rustのインストールページからパソコンに合わせて64ビット版あるいは32ビット版をインストールします。
-
- Proceed with installation (default) で進めてください
- Proceed with installation (default) で進めてください
-
- エディタ(VS Code)をインストールします
-
ここからダウンロードします
- 左側の拡張機能欄から次の拡張機能をダウンロードします
rust-analyzer
CodeLLDB
GitHub Pull Requests and Issues
-
Choose a License
- 左側の拡張機能欄から次の拡張機能をダウンロードします
- Rustの開発環境の準備が完了したかを確認します
- VS Codeの
Terminal
タブからNew Terminal
を選択して生成したTerminalから下記3つのコマンドを入力してください。cargo new hello_check
cd hello_check
cargo run
- Terminalに"Hello, world!"が表示されていたらここまでの準備は問題ありません
もう少し丁寧な説明が知りたい方は素晴らしいインストラクションをご参照ください。
- Wio Terminalの開発環境をインストールします
- VS Codeの
Terminal
タブからNew Terminal
を選択して生成したTerminalから下記3つのコマンドを入力してください。rustup target add thumbv7em-none-eabihf
cargo install hf2-cli
cargo install cargo-hf2
コンパイルを通すことができる最小限の構成
初心者の最初の壁は環境構築です。
本記事の場合Rustの環境構築に加えてWio Terminal向けのも必要になるので難易度が跳ね上がります。
そこで最初はコンパイルを通すことができる最小限の構成を紹介します。
この章ではコンパイルまでの流れを紹介してからプログラム構成の説明に移ります。
プログラム実行までの流れ
プログラムをダウンロードする
-
画面上側の
View
タブからCommand Pallete
をクリックします。
-
Git: Clone コマンドをクリックします。
-
リポジトリのURLを入力します。
-
VS codeのデフォルトフォルダにプログラムセットを解凍します。
- 私は
C:\Users\あなたのユーザー名\source\RustApps
に保存しています
- VS Codeを起動してコンパイルを通すことができる最小限の構成のプログラムのフォルダに移動します~~
- Terminalで
ls
コマンドを入力すると現在のディレクトリに含まれるフォルダ一覧が出てきます -
cd rust-wio-terminal-exercise
コマンドでプログラムセットのフォルダ内に入ります - 再び
ls
コマンドを入力するとプログラム一覧がでてきます -
cd ch00_minimum_component
コマンドでコンパイルを通すことができる最小限の構成プログラムのフォルダに移動します
Wio Terminalをブートローダーモードに切り替える
ブートローダーモードとはコンパイルできる状態のことらしいです。
Wio TerminalのWikiを見ながらパソコンとWio TerminalをUSBケーブルでつないで左側のスライドスイッチを下側に素早く2度動かして下さい。
プログラム実行
- Terminalで
cargo hf2 --release
コマンドを入力するとプログラム実行コンパイルが完了します
プログラム構成
下記の3点が必要です。
- Cargo.tomlファイルに必須クレートをインポートします
wio_terminal = "0.6.1" # 必須クレート
panic-halt = "0.2" # 必須クレート
- クレートとはまあ大体ライブラリのことを指しています
- .cargoフォルダをCargo.tomlファイルと同じフォルダにコピペします
- rootディレクトリと呼ぶそうです
- main.rsファイルの中身に必須項目が書きます(以降の節で説明)。
Cargo.tomlファイル
Wio Terminalを制御したいのでwio_terminal = "0.6.1"
クレートをインポートします。
さらに組込みRustの世界で例外処理を扱いたいのでpanic-halt = "0.2"
クレートをインポートします。
[package]
name = "ch00_minimam_component"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
wio_terminal = "0.6.1" # 必須クレート
panic-halt = "0.2" # 必須クレート
.cargoフォルダ
Wio TerminalにはArm社のCortex-M4FというCPUが搭載されています。
このCPU向けのプログラムを作成するためにtarget = "thumbv7em-none-eabihf"
という宣言が必要です。
rustflags
は何らかの調整だと思います。
# 必須ファイル
# .cargoフォルダごとCargo.tomlファイルがあるディレクトリにコピペする
[build]
target = "thumbv7em-none-eabihf"
[target.thumbv7em-none-eabihf]
rustflags = [
"-C", "link-arg=-Tlink.x",
]
main.rsファイル
- WindowsやmacOS等の通常のOS以外でのプログラムを作成するという宣言のために
#![no_std]
や#![no_main]
アトリビュートが必要になります。 -
use panic_halt as _;
とuse wio_terminal;
はrust:Cargo.toml
でインポートしたクレートを使用する宣言です。 - 画面幅と画面高さの定数はよく使うので実質必須の定数です。
-
#[wio_terminal::entry]
は組込みRustにおけるmain関数につける宣言です。- 組込みRustのmain関数は終了させない宣言
-> !
をつけます
- 組込みRustのmain関数は終了させない宣言
- 組込みの場合終了させないために
loop
が必要です- 冷蔵庫が勝手に止まったら嫌ですよね?
//! 組込みRustのおまじない
#![no_std] // 必須アトリビュート
#![no_main] // 必須アトリビュート
use panic_halt as _; // 必須クレート
use wio_terminal; // 必須クレート
const DISPLAY_WIDTH: u32 = 320; // 画面幅の定数. ほぼ必須で良いだろう
const DISPLAY_HEIGHT: u32 = 240; // 画面高さの定数. ほぼ必須で良いだろう
#[wio_terminal::entry] // 必須アトリビュート
fn main() -> ! {
// 初期化
// ここまで 初期化
// 組込みはloop必須
loop
{}
// ここまでloop処理
}
// ここまでmain関数
Lチカ
組込みのHello worldことLチカの説明をします。
今回は200ミリ秒毎にLEDの点灯・消灯を切り替えます。
プログラム実行に関してはrust-wio-terminal-exercise\ch04_p132_01_lchika
フォルダに移動して頂いてcargo hf2 --release
コマンドを入力するとプログラム実行できます。
プログラム構成
Cargo.tomlファイル
- 最小構成と同じクレートをインポートします。
wio_terminal = "0.6.1" # 必須クレート
panic-halt = "0.2" # 必須クレート
.cargoフォルダ
最小構成と同じです。
main.rsファイル
最小構成と異なる内容を説明します。
-
use wio_terminal::prelude::*;
- LEDを点滅させるための遅延処理のトレイトが入っています。ほぼ必須
-
let mut peripherals = wio_terminal::pac::Peripherals::take().unwrap();
- CPUコアとメモリ以外の周辺機器を制御するための宣言のようです
-
let sets = wio_terminal::Pins::new(peripherals.PORT).split();
- 入出力関係を扱う宣言
-
let mut clocks = 略
- 内部の時計を使用する宣言
- 遅延処理などに使用します
-
let mut core = wio_terminal::pac::CorePeripherals::take().unwrap();
-
Peripherals
との違いが分かりません
-
-
let mut delay = wio_terminal::hal::delay::Delay::new(core.SYST, &mut clocks);
- 遅延処理の宣言
-
let mut user_led = sets.user_led.into_push_pull_output();
- LED出力の初期化
-
wio_terminal = "0.7.0"
ではsets
のポートにアクセスできないので書籍と異なる呼び出し方をする -
user_led.set_low().unwrap();
はLED消灯(?)の初期化
- loop内部のコードは200ミリ秒ごとにLEDの点灯・消灯を切り替えるコード
//! 組込みRustのおまじない
#![no_std] // 必須アトリビュート
#![no_main] // 必須アトリビュート
use panic_halt as _; // 必須クレート
use wio_terminal; // 必須クレート
use wio_terminal::prelude::*; // よく使うtraitやstructureが収められているようだ。ほぼ必須。
//const DISPLAY_WIDTH: u32 = 320; // 画面幅の定数. ほぼ必須で良いだろう
//const DISPLAY_HEIGHT: u32 = 240; // 画面高さの定数. ほぼ必須で良いだろう
#[wio_terminal::entry] // 必須アトリビュート
fn main() -> ! {
// 初期化
// 制御関係(wio_terminal)の初期化
let mut peripherals = wio_terminal::pac::Peripherals::take().unwrap(); // 周辺機器(CPUコアとメモリ以外)
let sets = wio_terminal::Pins::new(peripherals.PORT).split(); // 入出力
// 画面表示の描画間隔を設定するために時計インスタンス
let mut clocks = wio_terminal::hal::clock::GenericClockController::with_external_32kosc(
peripherals.GCLK,
&mut peripherals.MCLK,
&mut peripherals.OSC32KCTRL,
&mut peripherals.OSCCTRL,
&mut peripherals.NVMCTRL,
);
// 制御関係(wio_terminal)の初期化
let mut core = wio_terminal::pac::CorePeripherals::take().unwrap(); // 周辺機器(CPUコアとメモリ以外)
// 画面表示の描画間隔
let mut delay = wio_terminal::hal::delay::Delay::new(core.SYST, &mut clocks);
// LEDの初期化
// 書籍の内容だとエラーになる
// Githubの内容を参考にすること
// https://github.com/atsamd-rs/atsamd/blob/0820f0df58eb8705ddfa6533ed76953d18e6b992/boards/wio_terminal/examples/blinky.rs
let mut user_led = sets.user_led.into_push_pull_output();
user_led.set_low().unwrap();
// 組込みはloop必須
loop {
user_led.toggle().ok();
delay.delay_ms(200u8);
}
// ここまでloop処理
}
// ここまでmain関数
終わりに
Wio TerminalのプログラムをRustで開発できる最小限の構成とLチカを紹介しました。
続きはGitHubにコードがあるのでそちらをご覧ください。
お役に立てれば幸いです。
Discussion