mruby で Raspberry Pi の GPIO をいじるというやつで、sleep をビジーループにしていたのがどうしてもひっかかっていた。
どうも ARM には割込みが起きるまで眠る命令があるみたいなので、それを使ってみることにした。コード全体
static mrb_value mrb_mruby_raspberrypi_gpio_gem_delay_us(mrb_state* mrb, mrb_value self) {
mrb_int delay;
mrb_get_args(mrb, "i", &delay);
// Reset timer flags
PUT32(ARM_TIMER_CONTROL, 0x3E0020);
// Load count down timer value
PUT32(ARM_TIMER_LOAD, delay-1);
PUT32(ARM_TIMER_RELOAD, delay-1);
// predevider = (apb_clk - freq) / freq
PUT32(ARM_TIMER_PRE_DIVIDER, 250 - 1);
PUT32(ARM_TIMER_IRQ_CLEAR_ACK, 0);
PUT32(ARM_TIMER_CONTROL,
(0x3E<<16) | // default free-running pre-scaler
(1<<7) | // timer enabled
(1<<5) | // timer interrupt enabled
(1<<1) // 23-bit counter
);
// Enable ARM Timer IRQ
PUT32(ARM_INTERRUPT_ENABLE_BASIC_IRQS, 1);
while ((GET32(ARM_INTERRUPT_IRQ_BASIC_PENDING) & 1) == 0) {
// Waiting For Interrupt
asm volatile ("wfi");
}
// Disable ARM Timer IRQ
PUT32(ARM_INTERRUPT_DISABLE_BASIC_IRQS, 1);
return mrb_nil_value();
}
コメントにある通りだけど、割込みを設定して、ARM_INTERRUPT_IRQ_BASIC_PENDING のフラグを見つつ、セットされるまでは wfi 命令で継続的に眠る、というようにしてみた。他に割込みを設定していないので、while は1回で抜けるつもり……
wfi 命令はオプショナルな命令らしく、ハードによっては nop として解釈されるらしい。なのでこのような実装の場合、フラグをポーリングするようなコードを併用したほうが安全そう。
というか、実際 wfi 命令がちゃんと動いているかを確かめる方法が面倒くさい。電流を測るしかなさそう。電流を今回測るところまでやってないので、ちゃんと動いてないのかもしれない。ただ、挙動として割込みをポーリングで待つ、というのはできいてるっぽい。
どうでもいいけど wfi で検索しても wifi 扱いされてだいぶウザい。