はじめに
以下の資料を参考にLチカしてみる
でも、正直これはやるだけなので、この記事ではノンブロッキングAPIを使ったLチカにも触れていく
最終成果物のリポジトリはこれ。
Cargo.tomlとかはここに入ってるのでビルドできないときは使うと良いかも。
環境構築
前提として、Windowsで動作させたいので参考記事に従ってWindows用のセットアップをしていく
ARMクロスコンパイラの導入
インストールディレクトリのarm-none-eabi-*
のバイナリに対してPathを通すだけの簡単な作業なので割愛する。
プレフィックスのついてないやつも有るけど、プレフィックスなしにパスを通すと競合するので一般的にクロスコンパイラ導入では非推奨だと思う。
デバッグ環境の構築
どうせならVSCodeでやれるほうがいい。
以下の記事の「Using Visual Studio Code」を参考に設定してみた。
チュートリアルLチカ
このあたりを見て頑張る。
同じことを書くことはしないけど、触ってみた。
概要
ブロッキング版のDisplayAPIを使ってLチカするだけ。
このブロッキングAPIは、設定した秒数を超えると消灯するという挙動になっている
ブロッキング版のdisplay.clear()
の意味ってなんだろ
暇が有ったらソース読んでみようかな。
microbitクレート
この記事のチュートリアル的な話では、micro:bitの操作は基本的に抽象化されたAPIでやっていく。
抽象化されたAPIは以下のクレートが提供しているので、ありがたく使わせてもらうことにする。
Displayのブロッキングとノンブロッキング
ここまでのチュートリアルのLチカは基本的にDisplayのブロッキングAPIを使っていた。
ブロッキングAPI
microbit::display::blocking::Display
ノンブロッキングAPI
microbit::display::nonblocking::Display
ブロッキングAPI
メリット
ハードウェア的な難しいことをまるっとやってくれているので楽
デメリット
あんまり自由じゃない感がある。
直感的に動作しない(display.clearの意味よ)
ノンブロッキングAPI
メリット
なんかそこそこ思った通りのことが出来る(直感的に書ける)
micro:bit入門にちょうど良さそうな難易度
デメリット
ハードウェア的なことをやらないといけない
割り込みベクタの初期化処理(優先度設定や割り込み許可)を書く必要がある。
機械いじりをしている感じが有って楽しい(デメリットとは・・・?)
アセンブラで書くよりは圧倒的に楽なのでデメリットではない気がしてきた。
この記事のスタンス
ノンブロッキングDisplayAPI microbit::display::nonblocking::Display
を使ったやつを一つ一つ動作を掘っていく。
公式が提供しているノンブロッキングAPIに関するチュートリアルのソースはこちら
一つ一つ挙動を確認していく。
とりあえずディスプレイに四角をグラデーションで書いてみる
#![no_main]
#![no_std]
use cortex_m_rt::entry;
use panic_halt as _;
use core::cell::RefCell;
use cortex_m::interrupt::Mutex;
use microbit::{
board::Board,
display::nonblocking::{Display, GreyscaleImage},
pac::{self, interrupt, TIMER1},
};
static DISPLAY: Mutex<RefCell<Option<Display<TIMER1>>>> = Mutex::new(RefCell::new(None));
#[entry]
fn main() -> ! {
if let Some(mut board) = Board::take() {
// ディスプレイを初期化する
let mut display = Display::new(board.TIMER1, board.display_pins);
display.show(&create_image(9,7,1));
cortex_m::interrupt::free(move |cs| {
*DISPLAY.borrow(cs).borrow_mut() = Some(display);
});
// 優先度の設定をした後割り込みを許可する。
unsafe {
board.NVIC.set_priority(pac::Interrupt::TIMER1, 64);
pac::NVIC::unmask(pac::Interrupt::TIMER1);
}
}
loop {}
}
#[interrupt]
fn TIMER1() {
cortex_m::interrupt::free(|cs| {
if let Some(display) = DISPLAY.borrow(cs).borrow_mut().as_mut() {
display.handle_display_event();
}
});
}
fn create_image(b: u8,b1: u8,b2: u8) -> GreyscaleImage {
GreyscaleImage::new(&[
[b, b, b, b, b],
[b, b1, b1, b1, b],
[b, b1, b2, b1, b],
[b, b1, b1, b1, b],
[b, b, b, b, b],
])
}
実行結果(点灯したまま)
更新処理はしっかりTIMER1が走らせているらしく、一発設定するだけでずっと点灯している。
GreyscaleImageは「明度」を設定できるが、これはPWM制御で行っている。
TIMER1のクロックを使ってPWM制御を暗黙的に実施している様子がスマホのカメラでフリッカー現象として観察できる。
(肉眼では高速すぎて観察できない。)
アニメーションを付けてみる
オリジナルのソースではこちらが記載されていたのでちょっと改造してみた。
// #![deny(unsafe_code)]
#![no_main]
#![no_std]
use cortex_m_rt::entry;
use panic_halt as _;
use core::cell::RefCell;
use cortex_m::interrupt::Mutex;
use microbit::{
board::Board,
display::nonblocking::{Display, GreyscaleImage},
hal::{
clocks::Clocks,
rtc::{Rtc, RtcInterrupt},
},
pac::{self, interrupt, RTC0, TIMER1},
};
// TIMER1割り込みを使ってディスプレイを更新します
// TIMER1割り込みはRTC0よりも割り込み優先度を高くします。
static DISPLAY: Mutex<RefCell<Option<Display<TIMER1>>>> = Mutex::new(RefCell::new(None));
static ANIM_TIMER: Mutex<RefCell<Option<Rtc<RTC0>>>> = Mutex::new(RefCell::new(None));
#[entry]
fn main() -> ! {
if let Some(mut board) = Board::take() {
// ペリフェラルを使うときは、halにペリフェラルのアドレスを食わせて使う。
// RTCに必要な低周波ハードウェアクロックを起動する
Clocks::new(board.CLOCK).start_lfclk();
// RTC を 16Hz (32_768 / (2047 + 1))で設定する
// 62.5ms 毎に割り込みが発生するようになる
let mut rtc0 = Rtc::new(board.RTC0, 2047).unwrap();
rtc0.enable_event(RtcInterrupt::Tick);
rtc0.enable_interrupt(RtcInterrupt::Tick, None);
rtc0.enable_counter();
// ディスプレイを初期化する
let display = Display::new(board.TIMER1, board.display_pins);
cortex_m::interrupt::free(move |cs| {
*DISPLAY.borrow(cs).borrow_mut() = Some(display);
*ANIM_TIMER.borrow(cs).borrow_mut() = Some(rtc0);
});
// 優先度の設定をした後割り込みを許可する。
unsafe {
// 元々のプログラムではRTC0が64、TIMER1が128になっていた。
// 変えた理由は後述する。
board.NVIC.set_priority(pac::Interrupt::RTC0, 128);
board.NVIC.set_priority(pac::Interrupt::TIMER1, 64);
pac::NVIC::unmask(pac::Interrupt::RTC0);
pac::NVIC::unmask(pac::Interrupt::TIMER1);
}
}
loop {}
}
#[interrupt]
fn TIMER1() {
cortex_m::interrupt::free(|cs| {
if let Some(display) = DISPLAY.borrow(cs).borrow_mut().as_mut() {
display.handle_display_event();
}
});
}
fn square_image(b: u8) -> GreyscaleImage {
let b1=b;
let b2=b;
GreyscaleImage::new(&[
[b, b, b, b, b],
[b, b1, b1, b1, b],
[b, b1, b2, b1, b],
[b, b1, b1, b1, b],
[b, b, b, b, b],
])
}
// RTC0でアニメーションさせる。
#[interrupt]
unsafe fn RTC0() {
// STEPを管理する。
// STEP=この関数が何回呼ばれたか=割り込み回数
// 数値は0-17の範囲を取る。
static mut STEP: u8 = 0;
cortex_m::interrupt::free(|cs| {
if let Some(rtc) = ANIM_TIMER.borrow(cs).borrow_mut().as_mut() {
rtc.reset_event(RtcInterrupt::Tick);
}
});
// 明度
// STEPが0-8なら明度を減少させる(暗くなる)
// STEPが9-17なら明度を増加させる(明るくなる)
let inner_brightness = match *STEP {
0..=8 => 9 - *STEP,
9..=17=> *STEP-8,
_ => 0,
};
// ディスプレイに画像を表示する(明度を引数に取る)
cortex_m::interrupt::free(|cs| {
if let Some(display) = DISPLAY.borrow(cs).borrow_mut().as_mut() {
display.show(&square_image(inner_brightness));
}
});
*STEP += 1;
if *STEP == 18 {
*STEP = 0
};
}
割り込み優先度の話
オリジナルのプログラムでは、優先度がそれぞれ
RTC0 が 64
TIMER1 が 128
になっていた。
コンピュータの優先度って基本的に数字が低いほうが優先な気がしたので、おかしいなと思って調べてみた。
micro:bitはARM Cortex M0のはずなので、公式文書を読む。
ARM公式文書
原文
Each priority field holds a priority value, 0-192. The lower the value, the greater the priority of the corresponding interrupt. The processor implements only bits[7:6] of each field, bits [5:0] read as zero and ignore writes. This means writing 255 to a priority register saves value 192 to the register.
日本語
各優先順位フィールドには、優先順位の値 (0 ~ 192) が保持されます。
値が小さいほど、対応する割り込みの優先度が高くなります。
プロセッサは各フィールドのビット [7:6] のみを実装し、ビット [5:0] はゼロとして読み取られ、書き込みは無視されます。
これは、プライオリティ レジスタに 255 を書き込むと、値 192 がレジスタに保存されることを意味します。
多分、例を書いた人は単純に間違えたんだと思う。
トラブルシュート
Rustに慣れてくると厄介なのがリンク時のエラーメッセージが読めなくなってくる。
なので参考までに書いておこうと思う。(試行錯誤してた時に出たやつ)
RTTモジュールのリンク(インポート)が不足している場合
> cargo embed --features v1
Compiling microbit_test v0.1.0 (D:\Users\segfo\Documents\vmShare\testCodes\microbit_test)
error: could not compile `microbit_test` due to 2 previous errors
error: linking with `rust-lld` failed: exit code: 1
|
= note: "rust-lld" "-flavor" "gnu" "C:\\Users\\segfo\\AppData\\Local\\Temp\\rustcOcJSwI\\symbols.o" "D:\\Users\\segfo\\Documents\\vmShare\\testCodes\\microbit_test\\target\\thumbv6m-none-eabi\\debug\\deps\\microbit_test-589b3a70f66c0138.1p0hme0pblsi21r5.rcgu.o" "D:\\Users\\segfo\\Documents\\vmShare\\testCodes\\microbit_test\\target\\thumbv6m-none-eabi\\debug\\deps\\microbit_test-589b3a70f66c0138.1w6yahvq7e8l2yrr.rcgu.o" "D:\\Users\\segfo\\Documents\\vmShare\\testCodes\\microbit_test\\target\\thumbv6m-none-eabi\\debug\\deps\\microbit_test-589b3a70f66c0138.3iwq35nwfmpa0nx9.rcgu.o" "D:\\Users\\segfo\\Documents\\vmShare\\testCodes\\microbit_test\\target\\thumbv6m-none-eabi\\debug\\deps\\microbit_test-589b3a70f66c0138.3t65g1dj21dde26k.rcgu.o" "D:\\Users\\segfo\\Documents\\vmShare\\testCodes\\microbit_test\\target\\thumbv6m-none-eabi\\debug\\deps\\microbit_test-589b3a70f66c0138.3uvb0xn12zsfnzoi.rcgu.o" "D:\\Users\\segfo\\Documents\\vmShare\\testCodes\\microbit_test\\target\\thumbv6m-none-eabi\\debug\\deps\\microbit_test-589b3a70f66c0138.450rft179jgk3att.rcgu.o" "D:\\Users\\segfo\\Documents\\vmShare\\testCodes\\microbit_test\\target\\thumbv6m-none-eabi\\debug\\deps\\microbit_test-589b3a70f66c0138.470ysigz77fliwrh.rcgu.o" "D:\\Users\\segfo\\Documents\\vmShare\\testCodes\\microbit_test\\target\\thumbv6m-none-eabi\\debug\\deps\\microbit_test-589b3a70f66c0138.5fmw4kr3grdgba7c.rcgu.o" "D:\\Users\\segfo\\Documents\\vmShare\\testCodes\\microbit_test\\target\\thumbv6m-none-eabi\\debug\\deps\\microbit_test-589b3a70f66c0138.5g5mvbh2bin2vih4.rcgu.o" "D:\\Users\\segfo\\Documents\\vmShare\\testCodes\\microbit_test\\target\\thumbv6m-none-eabi\\debug\\deps\\microbit_test-589b3a70f66c0138.6dhnk81vtsbriae.rcgu.o" "D:\\Users\\segfo\\Documents\\vmShare\\testCodes\\microbit_test\\target\\thumbv6m-none-eabi\\debug\\deps\\microbit_test-589b3a70f66c0138.dwwfs6483kvqwud.rcgu.o" "D:\\Users\\segfo\\Documents\\vmShare\\testCodes\\microbit_test\\target\\thumbv6m-none-eabi\\debug\\deps\\microbit_test-589b3a70f66c0138.px8le92cl1et6t7.rcgu.o" "D:\\Users\\segfo\\Documents\\vmShare\\testCodes\\microbit_test\\target\\thumbv6m-none-eabi\\debug\\deps\\microbit_test-589b3a70f66c0138.rps1918xy6vyd0a.rcgu.o" "D:\\Users\\segfo\\Documents\\vmShare\\testCodes\\microbit_test\\target\\thumbv6m-none-eabi\\debug\\deps\\microbit_test-589b3a70f66c0138.titeelixwxi1ccf.rcgu.o" "D:\\Users\\segfo\\Documents\\vmShare\\testCodes\\microbit_test\\target\\thumbv6m-none-eabi\\debug\\deps\\microbit_test-589b3a70f66c0138.xgdg6ny1wva34y5.rcgu.o" "D:\\Users\\segfo\\Documents\\vmShare\\testCodes\\microbit_test\\target\\thumbv6m-none-eabi\\debug\\deps\\microbit_test-589b3a70f66c0138.y2lwhk8x0f1qth9.rcgu.o" "--as-needed" "-L" "D:\\Users\\segfo\\Documents\\vmShare\\testCodes\\microbit_test\\target\\thumbv6m-none-eabi\\debug\\deps" "-L" "D:\\Users\\segfo\\Documents\\vmShare\\testCodes\\microbit_test\\target\\debug\\deps" "-L" "D:\\Users\\segfo\\Documents\\vmShare\\testCodes\\microbit_test\\target\\thumbv6m-none-eabi\\debug\\build\\cortex-m-991092799a13d205\\out" "-L" "D:\\Users\\segfo\\Documents\\vmShare\\testCodes\\microbit_test\\target\\thumbv6m-none-eabi\\debug\\build\\cortex-m-rt-76150794546c3c5e\\out" "-L" "D:\\Users\\segfo\\Documents\\vmShare\\testCodes\\microbit_test\\target\\thumbv6m-none-eabi\\debug\\build\\defmt-75e389a79ac761ea\\out" "-L" "D:\\Users\\segfo\\Documents\\vmShare\\testCodes\\microbit_test\\target\\thumbv6m-none-eabi\\debug\\build\\nrf51-hal-de5dcb07290f19c2\\out" "-L" "D:\\Users\\segfo\\Documents\\vmShare\\testCodes\\microbit_test\\target\\thumbv6m-none-eabi\\debug\\build\\nrf51-pac-50f4f75eaf4c5128\\out" "-L" "C:\\Users\\segfo\\.rustup\\toolchains\\stable-x86_64-pc-windows-msvc\\lib\\rustlib\\thumbv6m-none-eabi\\lib" "-Bstatic" "D:\\Users\\segfo\\Documents\\vmShare\\testCodes\\microbit_test\\target\\thumbv6m-none-eabi\\debug\\deps\\libmicrobit-c4bd1c06cae35280.rlib" "D:\\Users\\segfo\\Documents\\vmShare\\testCodes\\microbit_test\\target\\thumbv6m-none-eabi\\debug\\deps\\libmicrobit_common-265e828c2699c5bb.rlib" "D:\\Users\\segfo\\Documents\\vmShare\\testCodes\\microbit_test\\target\\thumbv6m-none-eabi\\debug\\deps\\libtiny_led_matrix-0a01c8be4dd5c60d.rlib" "D:\\Users\\segfo\\Documents\\vmShare\\testCodes\\microbit_test\\target\\thumbv6m-none-eabi\\debug\\deps\\libnrf51_hal-9e5861dfab7976e0.rlib" "D:\\Users\\segfo\\Documents\\vmShare\\testCodes\\microbit_test\\target\\thumbv6m-none-eabi\\debug\\deps\\libnrf_hal_common-aa6255cd7b09a196.rlib" "D:\\Users\\segfo\\Documents\\vmShare\\testCodes\\microbit_test\\target\\thumbv6m-none-eabi\\debug\\deps\\libcast-47dfb1c1697382fc.rlib" "D:\\Users\\segfo\\Documents\\vmShare\\testCodes\\microbit_test\\target\\thumbv6m-none-eabi\\debug\\deps\\libfixed-4562514bf943ca4a.rlib" "D:\\Users\\segfo\\Documents\\vmShare\\testCodes\\microbit_test\\target\\thumbv6m-none-eabi\\debug\\deps\\libtypenum-f32ef043faf45562.rlib" "D:\\Users\\segfo\\Documents\\vmShare\\testCodes\\microbit_test\\target\\thumbv6m-none-eabi\\debug\\deps\\libbytemuck-d4055f4563c7eb4d.rlib" "D:\\Users\\segfo\\Documents\\vmShare\\testCodes\\microbit_test\\target\\thumbv6m-none-eabi\\debug\\deps\\libhalf-59298774f2910e74.rlib" "D:\\Users\\segfo\\Documents\\vmShare\\testCodes\\microbit_test\\target\\thumbv6m-none-eabi\\debug\\deps\\libaz-3331f6f53ae93cda.rlib" "D:\\Users\\segfo\\Documents\\vmShare\\testCodes\\microbit_test\\target\\thumbv6m-none-eabi\\debug\\deps\\librand_core-c16565f89b00c660.rlib" "D:\\Users\\segfo\\Documents\\vmShare\\testCodes\\microbit_test\\target\\thumbv6m-none-eabi\\debug\\deps\\libcfg_if-50694addfb4d698c.rlib" "D:\\Users\\segfo\\Documents\\vmShare\\testCodes\\microbit_test\\target\\thumbv6m-none-eabi\\debug\\deps\\libnrf51_pac-7dbf31c122b46f89.rlib" "D:\\Users\\segfo\\Documents\\vmShare\\testCodes\\microbit_test\\target\\thumbv6m-none-eabi\\debug\\deps\\libpanic_rtt_target-c4e23ddc6402cc0e.rlib" "D:\\Users\\segfo\\Documents\\vmShare\\testCodes\\microbit_test\\target\\thumbv6m-none-eabi\\debug\\deps\\librtt_target-e5c83d3996fc7c00.rlib" "D:\\Users\\segfo\\Documents\\vmShare\\testCodes\\microbit_test\\target\\thumbv6m-none-eabi\\debug\\deps\\libcortex_m-d6232b903cb8b747.rlib" "D:\\Users\\segfo\\Documents\\vmShare\\testCodes\\microbit_test\\target\\thumbv6m-none-eabi\\debug\\deps\\libembedded_hal-05f76b440f9836ce.rlib" "D:\\Users\\segfo\\Documents\\vmShare\\testCodes\\microbit_test\\target\\thumbv6m-none-eabi\\debug\\deps\\libvoid-ee0982aa527621b5.rlib" "D:\\Users\\segfo\\Documents\\vmShare\\testCodes\\microbit_test\\target\\thumbv6m-none-eabi\\debug\\deps\\libnb-cd9e11e9cca523c3.rlib" "D:\\Users\\segfo\\Documents\\vmShare\\testCodes\\microbit_test\\target\\thumbv6m-none-eabi\\debug\\deps\\libnb-119c50ac6ee4939c.rlib" "D:\\Users\\segfo\\Documents\\vmShare\\testCodes\\microbit_test\\target\\thumbv6m-none-eabi\\debug\\deps\\libcritical_section-ec72564ee445e345.rlib" "D:\\Users\\segfo\\Documents\\vmShare\\testCodes\\microbit_test\\target\\thumbv6m-none-eabi\\debug\\deps\\libvolatile_register-6c4ae172cacf9a57.rlib" "D:\\Users\\segfo\\Documents\\vmShare\\testCodes\\microbit_test\\target\\thumbv6m-none-eabi\\debug\\deps\\libvcell-342864a26a27a96a.rlib" "D:\\Users\\segfo\\Documents\\vmShare\\testCodes\\microbit_test\\target\\thumbv6m-none-eabi\\debug\\deps\\libbare_metal-f726872701b76cf6.rlib" "D:\\Users\\segfo\\Documents\\vmShare\\testCodes\\microbit_test\\target\\thumbv6m-none-eabi\\debug\\deps\\libufmt_write-64ed9210dd796db6.rlib"
"D:\\Users\\segfo\\Documents\\vmShare\\testCodes\\microbit_test\\target\\thumbv6m-none-eabi\\debug\\deps\\libcortex_m_rt-617591d1200194f5.rlib" "C:\\Users\\segfo\\.rustup\\toolchains\\stable-x86_64-pc-windows-msvc\\lib\\rustlib\\thumbv6m-none-eabi\\lib\\librustc_std_workspace_core-0c8a86280ccb5fe4.rlib" "C:\\Users\\segfo\\.rustup\\toolchains\\stable-x86_64-pc-windows-msvc\\lib\\rustlib\\thumbv6m-none-eabi\\lib\\libcore-2d6eb4189217e250.rlib" "C:\\Users\\segfo\\.rustup\\toolchains\\stable-x86_64-pc-windows-msvc\\lib\\rustlib\\thumbv6m-none-eabi\\lib\\libcompiler_builtins-cdc7197b9e25d57a.rlib" "-Bdynamic" "--eh-frame-hdr" "-znoexecstack" "-L" "C:\\Users\\segfo\\.rustup\\toolchains\\stable-x86_64-pc-windows-msvc\\lib\\rustlib\\thumbv6m-none-eabi\\lib" "-o" "D:\\Users\\segfo\\Documents\\vmShare\\testCodes\\microbit_test\\target\\thumbv6m-none-eabi\\debug\\deps\\microbit_test-589b3a70f66c0138" "--gc-sections" "-Tlink.x"
= note: rust-lld: warning: section type mismatch for .got
>>> <internal>:(.got): SHT_PROGBITS
>>> output section .got: SHT_NOBITS
rust-lld: warning: section type mismatch for .got.plt
>>> <internal>:(.got.plt): SHT_PROGBITS
>>> output section .got: SHT_NOBITS
rust-lld: warning: section type mismatch for .got
>>> <internal>:(.got): SHT_PROGBITS
>>> output section .got: SHT_NOBITS
rust-lld: error: undefined symbol: _SEGGER_RTT
>>> referenced by lib.rs:0 (src\lib.rs:0)
>>> rtt_target-e5c83d3996fc7c00.rtt_target.ec3e4467-cgu.4.rcgu.o:(rtt_target::UpChannel::conjure::hf37a078eeb603b6d) in archive D:\Users\segfo\Documents\vmShare\testCodes\microbit_test\target\thumbv6m-none-eabi\debug\deps\librtt_target-e5c83d3996fc7c00.rlib
解決策
以下をインポートする。
use rtt_target::rtt_init_print;
Cargo.tomlは、以下を記述する。
rtt-target = { version = "0.3.1", features = ["cortex-m"] }
RTT(Real-Time Transfer)使ってないと何だこれってなるやつ。
何だこれって成った。
そもそもの原因
パニックハンドラを実装するクレートで panic-rtt-target
を使っている場合に、オブジェクトリンク時の依存関係としてrtt-target
も必須になるので、「panic-rtt-targetを使ってるけど、リンクすべきrtt-targetのライブラリがないんだが???」というのがこのリンクエラーの言いたいこと。
だから、追加すると直るという話。
チュートリアルだからと、コピペでやろうとするとたまにこうなる
ちなみにこのクレートは、panic時にシリアルを通してエラーメッセージをホストに送ってくれるという最高に便利な物らしい。
チュートリアル程度のプログラムじゃそんなにpanicしないので使わんので要らんが。
おわりに
エコシステムが発達してきてRustで色々なことが簡単にできるようになってきた。
いいぞ、もっとやれ