割込みと WFI 命令を使った sleep の実装
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 扱いされてだいぶウザい。
このエントリを参照するエントリ
関連エントリー
- mruby を Raspberry Pi 上で bare metal で動かすまで bare metal という言葉を最近知って、おもしろそうだなあと思ったので Raspberry Pi 上で試してみた。bare metal...
- OCXO と GPS 1PPS その2 OCXO の VFC ピンでの可変範囲を GPS の 1PPS を使って調べた | tech - 氾濫原 でとりあえず可変範囲ぐらいはわかっ...
- Raspberry Pi で bare metal している blinker05 の ARM ブートコードを読む Raspberry Pi で bare metal をやっているこのコードを読んで理解したいと思います。README に殆ど書いてありますが...
- STM32F072 で、ユーザーコードから DFU モードに入る。 STM32 には出荷時点でブートローダーが入っていて、様々な方法ですぐ書きこめるようになっている。ブートローダーは、書き換えできない「システ...