プロジェクトをゼロから開始する
nRF52840 を例として、組み込みプロジェクトをゼロから開始する方法を示します。ただし、このガイドはこの開発ボードに限定されません。
マイクロコントローラーを特定する
最初のステップは、使用するマイクロコントローラーを特定することです。必要となるマイクロコントローラーに関する情報は次のとおりです。
1. プロセッサーアーキテクチャとサブアーキテクチャ。
この情報は、デバイスのデータシートまたはマニュアルに記載されているはずです。nRF52840 の場合、プロセッサーは ARM Cortex-M4 コアです。この情報をもとに、互換性のあるコンパイルターゲットを選択する必要があります。rustup target list を実行すると、サポートされているすべてのコンパイルターゲットが表示されます。
$ rustup target list
(..)
thumbv6m-none-eabi
thumbv7em-none-eabi
thumbv7em-none-eabihf
thumbv7m-none-eabi
thumbv8m.base-none-eabi
thumbv8m.main-none-eabi
thumbv8m.main-none-eabihf
コンパイルターゲットは通常、$ARCHITECTURE-$VENDOR-$OS-$ABI という形式で命名されます。ただし、$VENDOR フィールドは省略されることがあります。マイクロコントローラーのようなベアメタルおよび no_std ターゲットでは、多くの場合 $OS フィールドに none が使用されます。$ABI フィールドが hf で終わる場合、出力される ELF が hardfloat Application Binary Interface (ABI) を使用することを示します。
上記の thumb ターゲットは、現在サポートされているすべての ARM Cortex-M ターゲットです。次の表は、コンパイルターゲットと ARM Cortex-M プロセッサーの対応関係を示しています。
| コンパイルターゲット | プロセッサー |
|---|---|
thumbv6m-none-eabi | ARM Cortex-M0, ARM Cortex-M0+ |
thumbv7m-none-eabi | ARM Cortex-M3 |
thumbv7em-none-eabi | ARM Cortex-M4, ARM Cortex-M7 |
thumbv7em-none-eabihf | ARM Cortex-M4F, ARM Cortex-M7F |
thumbv8m.base-none-eabi | ARM Cortex-M23 |
thumbv8m.main-none-eabi | ARM Cortex-M33, ARM Cortex-M35P |
thumbv8m.main-none-eabihf | ARM Cortex-M33F, ARM Cortex-M35PF |
ARM Cortex-M ISA には後方互換性があるため、たとえば thumbv6m-none-eabi ターゲットを使用してプログラムをコンパイルし、それを ARM Cortex-M4 マイクロコントローラー上で実行できます。これは動作しますが、thumbv7em-none-eabi を使用した方がパフォーマンスは向上するため(コンパイラーが ARMv7-M 命令を生成するため)、そちらを優先すべきです。
❗️ 選択したコンパイルターゲットを Rust ツールチェーンに追加する必要があります。
$ rustup +stable target add thumbv7em-none-eabihf
2. メモリレイアウト。
特に、デバイスがどれだけの Flash と RAM メモリを持っているか、またそのメモリがどのアドレスで公開されているかを特定する必要があります。この情報は、デバイスのデータシートまたはリファレンスマニュアルに記載されています。
nRF52840 の場合、この情報は Product Specification のセクション 4.2(図 2)にあります。 内容は次のとおりです。
- アドレス範囲
0x0000_0000-0x0010_0000にわたる 1 MB の Flash。 - アドレス範囲
0x2000_0000-0x2004_0000にわたる 256 KB の RAM。
knurling の app-template
これらすべての情報があれば、ターゲットデバイス向けのプログラムをビルドできるようになります。
私たちは、cortex-m-quickstart テンプレートをベースにした app-template という Cargo プロジェクトテンプレートを作成しました。これは、すべての knurling ツールをそのまま使用する ARM Cortex-M アーキテクチャ向けの新しいプロジェクトを開始できるものです。
🔎 その他のアーキテクチャについては、rust-embedded organization による他のプロジェクトテンプレートを確認してください。
❗️ インストール手順で案内されているとおり、probe-run と cargo-generate がインストールされていることを確認してください。
独自のプロジェクトをセットアップするために app-template を使用する推奨方法は、cargo-generate ツールを使うことです。
$ cargo generate \
--git https://github.com/knurling-rs/app-template \
--branch main \
--name co2sensor
❗️ cargo-generate ツールは libgit2(C ライブラリ)に依存しているため、Windows ではインストールが難しい場合があります。別の選択肢として、GitHub から app-template のスナップショットをダウンロードし、そのスナップショットの Cargo.toml にあるプレースホルダーを埋める方法があります。
テンプレートを使用してプロジェクトを作成したら、前の 2 つのステップで収集したデバイス固有の情報を入力する必要があります。
変更が必要なものはすべて、ファイル内で
TODOとしてもマークされています。
.cargo/config.tomlにチップを入力します。
# .cargo/config.toml
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
-runner = "probe-run --chip {{chip}} --defmt"
+runner = "probe-run --chip nRF52840_xxAA --defmt"
.cargo/config.tomlのコンパイルターゲットを調整します。
# .cargo/config.toml
[build]
-target = "thumbv6m-none-eabi" # Cortex-M0 および Cortex-M0+
-# target = "thumbv7m-none-eabi" # Cortex-M3
-# target = "thumbv7em-none-eabi" # Cortex-M4 および Cortex-M7(FPU なし)
-# target = "thumbv7em-none-eabihf" # Cortex-M4F および Cortex-M7F(FPU あり)
+target = "thumbv7em-none-eabihf" # Cortex-M4F(FPU あり)
Cargo.tomlで、適切な HAL を依存関係として追加します。
# Cargo.toml
[dependencies]
-# some-hal = "1.2.3"
+nrf52840-hal = "0.11.0"
- HAL を選択したので、
src/lib.rsの HAL インポートを修正します。
// my-app/src/lib.rs
-// use some_hal as _; // メモリレイアウト
+use nrf52840_hal as _; // メモリレイアウト
cargo buildが動作することを確認します。
$ cd co2sensor
$ cargo build
Compiling co2sensor v0.1.0 (/Users/ferrous/co2sensor)
Finished dev [optimized + debuginfo] target(s) in 0.65s
おめでとうございます!co2sensor/ のサンプルコードをターゲットデバイス向けに正常にクロスコンパイルできました。
rust-embedded organization の下に特定のアーキテクチャ向けのテンプレートやサポートの兆候がない場合は、embedonomicon に従って新しいアーキテクチャのサポートを自分でブートストラップできます。
プログラムのフラッシュ書き込み
ターゲットデバイスにプログラムを書き込むには、開発ボードにオンボードデバッガーが搭載されている場合はそれを特定する必要があります。または、開発ボードが何らかのコネクター経由で JTAG または SWD インターフェースを公開している場合は、外部デバッガーを選択します。
ハードウェアデバッガーが probe-rs プロジェクトでサポートされている場合(たとえば J-Link、ST-Link、CMSIS-DAP など)、cargo-flash や cargo-embed のような probe-rs ベースのツールを使用できます。これは nRF52840 DK の場合に当てはまります。このボードにはオンボード J-Link プローブがあります。
デバッガーが probe-rs でサポートされていない場合は、OpenOCD またはベンダー提供のソフトウェアを使用してボードにプログラムを書き込む必要があります。
ボードが JTAG、SWD、または同様のインターフェイスを公開していない場合、そのマイクロコントローラーには標準ファームウェアの一部としてブートローダーが搭載されている可能性があります。その場合、チップにプログラムを書き込むには dfu-util、または nrfutil のようなベンダー固有のツールを使用する必要があります。これは nRF52840 Dongle の場合に当てはまります。
出力を取得する
probe-rs がサポートするプローブのいずれかを使用している場合は、rtt-target ライブラリを使用して cargo-embed 上でテキスト出力を取得できます。例で使用したロギング機能は、rtt-target クレートを使用して実装されています。
そうでない場合、またはボード上にデバッガーがない場合は、ボードからテキスト出力を取得する前に HAL を追加する必要があります。
ハードウェア抽象化レイヤー (HAL) を追加する
これで、うまくいけばプログラムを実行し、その出力を取得できるようになっています。デバイスのハードウェア機能を使用するには、依存関係のリストに HAL を追加する必要があります。crates.io、lib.rs、awesome embedded Rust は HAL を探すのに適した場所です。
HAL を見つけたら、その API docs と examples を通じて API に慣れておくとよいでしょう。特にペリフェラルの初期化や設定に関して、HAL が常にまったく同じ API を公開しているとは限りません。ただし、ほとんどの HAL は embedded-hal トレイトを実装しています。これらのトレイトにより、HAL と ドライバー クレートの間で相互運用が可能になります。これらのドライバークレートは、I2C や SPI のようなインターフェイスを介して、センサー、アクチュエーター、無線機器などの外部デバイスとやり取りする機能を提供します。
使用しているデバイス向けの HAL が存在しない場合は、自分で構築する必要があります。これは通常、まず svd2rust ツールを使用して System View Description (SVD) ファイルから Peripheral Access Crate (PAC) を生成することで行います。PAC は、デバイス上のレジスタを変更するための低レベルだが型安全な API を公開します。PAC が用意できたら、crates.io にある多数の HAL のいずれかを参考にできます。そのほとんどは、svd2rust によって生成された PAC の上に実装されています。
こんにちは、💡
自分のプロジェクトをゼロからセットアップできたので、HAL だけを使用して DK のオンボード LED の 1 つを点灯させ、いろいろ試し始めることができます。その際に役立つかもしれないヒントをいくつか挙げます。
- Nordic Infocenter では、どの LED がどのピンに接続されているかを確認できます。
LED の点滅 今後の展望