ポーリング
GPIO 入力の読み取り方法を学んだので、次はこれらの読み取りを実際にどのように使えるかを考えてみましょう。たとえば、Button A が押されたときに LED を点灯し、Button B が押されたときに消灯するようにプログラムしたいとします。これは、ループの中で両方のボタンの状態をポーリングし、ボタンが押されていると読み取られたときにそれに応じて処理を行うことで実現できます。このプログラムは次のように書けます。
#![no_main]
#![no_std]
use cortex_m_rt::entry;
use embedded_hal::delay::DelayNs;
use embedded_hal::digital::{InputPin, OutputPin};
use microbit::hal::timer::Timer;
use microbit::{hal::gpio, Board};
use panic_rtt_target as _;
use rtt_target::rtt_init_print;
#[entry]
fn main() -> ! {
rtt_init_print!();
let board = Board::take().unwrap();
let mut timer = Timer::new(board.TIMER0);
// Configure buttons
let mut button_a = board.buttons.button_a;
let mut button_b = board.buttons.button_b;
// Configure LED (top-left LED at row1, col1)
let mut row1 = board
.display_pins
.row1
.into_push_pull_output(gpio::Level::Low);
let _col1 = board
.display_pins
.col1
.into_push_pull_output(gpio::Level::Low);
loop {
let on_pressed = button_a.is_low().unwrap();
let off_pressed = button_b.is_low().unwrap();
match (on_pressed, off_pressed) {
// Stay in current state until something is pressed.
(false, false) => (),
// Change to on state.
(true, false) => row1.set_high().unwrap(),
// Change to off state.
(false, true) => row1.set_low().unwrap(),
// Stay in current state until something is released.
(true, true) => (),
}
timer.delay_ms(10_u32);
}
}
このようにループの中で入力を繰り返し確認する方法は、ポーリングと呼ばれます。ある入力の状態を確認するとき、その入力を ポーリングしている と言います。この場合、Button A と Button B の両方をポーリングしています。
ポーリングは単純ですが、外界に応じて興味深いことを行えます。デバイスのすべての入力について、それらをループの中で「ポーリング」し、その結果に何らかの形で 1 つずつ応答できます。この種の方法は概念的に非常に単純で、多くのプロジェクトにとって良い出発点です。ポーリングがすべてのケース(あるいは大半のケース)にとって最善の方法ではないかもしれない理由は、すぐにわかるでしょう。しかし、まずは試してみましょう。
注 「ポーリング」という語は、粒度の異なる 2 つのレベルで使われることがよくあります。ひとつのレベルでは、「ポーリング」は、入力の状態がどうなっているかを(1 回だけ)問い合わせることを指します。より高いレベルでは、「ポーリング」、あるいは「ループの中でのポーリング」は、上で使ったような単純な制御フローで、入力の状態がどうなっているかを(繰り返し)問い合わせることを指します。このように、制御フローを指してこの語が使われるのはごく単純なプログラムだけであり、本番環境ではほとんど使われません(すぐにわかるように、実用的ではないためです)。そのため、一般に組み込みエンジニアがポーリングについて話すときは、前者、すなわち入力の状態がどうなっているかを(1 回だけ)問い合わせることを意味します。