Rust で組み込み開発(※趣味レベル)をやっていたこともあったのですが、内容をほとんど忘れたので覚書として残そうかなと思います。
開発の効率なども考えて、今回は SWD (Serial Wired Debug) の機能を使い、下記の要素を取り入れて開発環境を準備していきます。
なお、私の手元で確認したことを記載するつもりですが、お試しいただく時期や環境によって動作しないこともあるかと思いますので、そのときはイイ感じに読み替えたり試行錯誤してもらえればと思います。
名称 | 備考 |
---|---|
開発ホスト | M1 MacBook Pro 2020 / macOS 12.6 Monterey |
Rust | 1.64.0 |
ブレッドボード | 適当なやつ x1 |
Raspberry Pi Pico | 開発ターゲット |
Seeeduino XIAO | デバッグアダプタ用に使用 |
LED | 何色でもいいです x1 |
抵抗 | 330Ω x1 |
ジャンパワイヤー | 5 本ぐらい。 |
ちなみに、LED や抵抗に関しては、マイコンボード上の据え付けのものも利用できるので、準備がない方はそちらでもいいと思います。
使用する Rust 言語のバージョンはこんな感じ。
$ rustup --version
rustup 1.24.3 (2021-05-31)
info: This is the version for the rustup toolchain manager, not the rustc compiler.
info: The currently active `rustc` version is `rustc 1.64.0 (a55dd71d5 2022-09-19)`
$ rustc --version
rustc 1.64.0 (a55dd71d5 2022-09-19)
$ cargo --version
cargo 1.64.0 (387270bc7 2022-09-16)
次に使用するツールチェーンと各種ツール類をインストールします。
$ rustup target install thumbv6m-none-eabi
$ cargo install flip-link probe-run
今回は Raspberry Pi Pico を開発ターゲットにするので、使用されている RP2040 コア (Arm Cortex-M0+デュアルコア@133MHz) に合うように thumbv6m-none-eabi を選択しています。
追加でインストールしているツール類については、ざっくりと下記のとおりです。
今回、ターゲットにするのが Raspberry Pi Pico の ARM プロセッサということなので、SWD (Serial Wired Debug) を活用してデバックをかんたんにしていきたいので、どのご家庭にもある Seeeduino XIAO に DAPLink ファームウェアを焼いて使っていきます。
詳細な手順は Seeed Studio XIAO SAMD21 を使用して DAPLink デバイスを構築する - Seeed Wiki にある通り、ファームウェアをダウンロード、XIAO をブートローダーモードに切り替え、uf2 ファイルをドラッグ&ドロップして終わりです。
結線の方法についてはこんな感じです。
XIAO 物理ピン | Pico 物理ピン | |
---|---|---|
09 (D9) | <-> | SWDIO |
10 (D10) | <-> | SWCLK |
12 (GND) | <-> | GND |
13 (5v) | <-> | 1 (VSYS) |
まずは、Cargo コマンドでプロジェクト用のディレクトリを作成して行きます。
$ cargo init ras-pico-tutorial
$ cd ras-pico-tutorial
今回は、Raspberry Pi Pico 用の BSP (Board Support Package) があるのでそれを使ってプログラミングしていこうかなと思います。
$ cargo add rp-pico cortex-m cortex-m-rt embedded-hal defmt defmt-rtt
次にビルド時のターゲットやランナーの指定を設定ファイルに書いておきます。 必須ではないのですがファイルを作っておくとコマンドが短くなって楽になります。
.cargo/config.toml
[build]
target = "thumbv6m-none-eabi"
[target.thumbv6m-none-eabi]
runner = "probe-run --chip RP2040"
rustflags = [
"-C", "linker=flip-link",
"-C", "link-arg=--nmagic",
"-C", "link-arg=-Tlink.x",
"-C", "link-arg=-Tdefmt.x",
"-C", "inline-threshold=5",
"-C", "no-vectorize-loops",
]
[env]
DEFMT_LOG = "debug"
続いて、VS Code の設定ファイルも作成しておくと、コードのフォーマッティングや文法警告などの機能が使えるので設定しておくといいと思います。 ちなみに、拡張機能としては rust-analyzer をインストールしてあります。
.vscode/settings.json
{
"rust-analyzer.checkOnSave.allTargets": false,
"rust-analyzer.checkOnSave.extraArgs": ["--target", "thumbv6m-none-eabi"],
"editor.formatOnSave": true
}
つづいて、RP2040 用のメモリマップファイル?を用意します。
memory.x
MEMORY {
BOOT2 : ORIGIN = 0x10000000, LENGTH = 0x100
FLASH : ORIGIN = 0x10000100, LENGTH = 2048K - 0x100
RAM : ORIGIN = 0x20000000, LENGTH = 256K
}
EXTERN(BOOT2_FIRMWARE)
SECTIONS {
/* ### Boot loader */
.boot2 ORIGIN(BOOT2) :
{
KEEP(*(.boot2));
} > BOOT2
} INSERT BEFORE .text;
それではメインのソースコードです。LED は GPIO28 につなぐ想定です。(内蔵 LED 使う人は)
#![no_std]
#![no_main]
// エントリポイント用アトリビュート
use cortex_m_rt::entry;
// デバッグ出力用
use defmt::*;
use defmt_rtt as _;
// パニックハンドラ
use panic_probe as _;
// Board Support Package
use rp_pico as bsp;
use bsp::hal::{
clocks::{init_clocks_and_plls, Clock},
pac,
sio::Sio,
watchdog::Watchdog,
};
// GPIO 制御用
use embedded_hal::digital::v2::OutputPin;
// エントリポイント
#[entry]
fn main() -> ! {
// スタート
info!("Program start.");
// シングルトンオブジェクトを取得
let mut pac = pac::Peripherals::take().unwrap();
let core = pac::CorePeripherals::take().unwrap();
// Watchdog ドライバーの取得と、クロックの初期化
let mut watchdog = Watchdog::new(pac.WATCHDOG);
let clocks = init_clocks_and_plls(
bsp::XOSC_CRYSTAL_FREQ,
pac.XOSC,
pac.CLOCKS,
pac.PLL_SYS,
pac.PLL_USB,
&mut pac.RESETS,
&mut watchdog,
)
.ok()
.unwrap();
// ディレイオブジェクトの生成
let mut delay = cortex_m::delay::Delay::new(core.SYST, clocks.system_clock.freq().to_Hz());
// GPIO ピンの初期化
let sio = Sio::new(pac.SIO);
let pins = bsp::Pins::new(
pac.IO_BANK0,
pac.PADS_BANK0,
sio.gpio_bank0,
&mut pac.RESETS,
);
let mut led_pin = pins.gpio28.into_push_pull_output();
// メインループ
loop {
// 出力ピンをハイに
info!("high");
led_pin.set_high().unwrap();
delay.delay_ms(500);
// 出力ピンをローに
info!("low");
led_pin.set_low().unwrap();
delay.delay_ms(500);
}
}
$ cargo run
こんな感じで L チカ ができました!