Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

スピンウェイト

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 から始めるのです。