スピンウェイト
LED を点滅させるには、状態が切り替わるたびに約 0.5 秒待つ必要があります。どうすればよいでしょうか?
では、まずは間抜けなやり方があります。良いやり方ではありませんが、出発点にはなります。examples/spin-wait.rs を見てみましょう。
#![no_main]
#![no_std]
use cortex_m::asm::nop;
use cortex_m_rt::entry;
use embedded_hal::digital::OutputPin;
use nrf52833_hal::{gpio, pac};
use panic_halt as _;
fn wait() {
for _ in 0..4_000_000 {
nop();
}
}
#[entry]
fn main() -> ! {
let peripherals = pac::Peripherals::take().unwrap();
let p0 = gpio::p0::Parts::new(peripherals.P0);
let mut row1 = p0.p0_21.into_push_pull_output(gpio::Level::High);
let _col1 = p0.p0_28.into_push_pull_output(gpio::Level::Low);
loop {
wait();
row1.set_high().unwrap();
wait();
row1.set_low().unwrap();
}
}
cargo run --release --example spin-wait でこれを実行してください。ここでは --release が本当に重要です。すると、MB2 の LED が だいたい 1 秒に 1 回のペースで点いたり消えたりするはずです。
気になるかもしれないこと:
-
その数値中の
_文字は何ですか? Rust では数値の中にこれを入れることができ、無視されます。 大きな数を読みやすくするのにとても便利です。ここでは、これをカンマ(あるいは、 あなたの国で 3 桁ごとの区切りに使う任意の区切り記号)として使っています。 -
nRF52833 は 64MHz で動いているのに、なぜ待機ループは 4M 回しか反復しないのですか? 32M になるべきでは? 待機ループは、1 回まわるごとに複数の命令を実行します。
nop(次の節を参照)、 いくつかの管理処理、そしてループの先頭への分岐です。生成されるコードは、最初のwait()呼び出しでは おおよそ次のようになります.LBB1_4: adds r3, #1 nop cmp r3, r2 bne .LBB1_4そして 2 回目では次のようになります
.LBB1_6: subs r3, #1 nop bne .LBB1_6これは 3 命令か 4 命令しかありませんが、後方分岐には少し余計なコストがかかるかもしれません。 これらが 同じではない ことに注意してください。コンパイラは、最初と 2 回目の待機ループで異なる命令を 出力することを選んでいます。下の「状況によって変わる」を見てください。
それでも、ループ 1 回あたり約 4 命令を実行しています。つまり、64MHz の CPU では、0.5 秒のスピンは 完了までに 64M/2/4 = 8M 回の反復が必要なはずです。ということは、何かが私たちを 2 倍遅くしています。 何かって? さあ。これ全体がひどいのです。
-
なぜ
--releaseがそんなに重要なのですか? これなしで試してみてください。LED はまだ点いたり消えたり していますが、その周期は 何秒も になります。待機ループが最適化されておらず、1 回まわるたびに多くの命令を 実行しているためです。 -
その
nop()呼び出しとは何で、なぜそこにあるのですか? これについては次の節で答えます。 -
なぜこれを「間抜けなやり方」と呼ぶのですか?
-
正確ではありません。 そのループを調整して毎回きっちり 0.5 秒に合わせるというのは……あまり現実的では ありません。
-
状況によって変わります。 CPU が違う? コンパイルフラグが違う? 実際、何かが違う? それだけでタイミングが 変わります。
-
電力を食います。 CPU は、その場にとどまるだけのために、できる限り高速に命令を実行し続けています。 ほかにやることがないなら、再び必要になるまで静かにスリープすべきです。USB 給電なら、これはそれほど 問題になりません。しかし、バッテリーパックで MB2 をつないでいると、これを本当に実感するでしょう。
-
次の節では、nop() について説明します。その後で、この blinky のほかに改善が必要な点についてさらに話します。
こんなに単純なプログラムにしては、これはかなり複雑なプログラムです。だからこそ、最初に blinky から始めるのです。