はじめに
Raspberry Pi Pico(Pico)は4つの64ビットタイマーを持っている。それぞれ割り込みを発生させることができる。
ここまででSysTickやその割り込みを使ってLチカをしてきたが、すべてビジーループでタイミングを測っていた。
今回はWFIを使ってスリープしてみる。使用するタイマーはTIMER0。
タイマー
リセット
タイマーを使用するにはまず最初にタイマーをリセットする必要がある。リセットにはRESETSレジスタを使用する。RESETSレジスタは下記のようになっている。
割り込み
SysTickとは違いタイマーの割り込みはペリフェラルの割り込みとなる。
NVIC、IRQ
ペリフェラルの割り込みはNested Vectored Interrupt Controller(NVIC)に接続され、それぞれの機能がIRQ(Interrupt ReQuest)に割り当てられている。
TIMER0の割り込みはIRQ0に割り当てられていることがわかる。
IRQ0の割り込みが発生するとベクターテーブルのIRQ0に飛んでくる。
NVIC割り込みの操作には下記のレジスタを使用する。
タイマー割り込み
NVICは接続されているペリフェラルの割り込みを処理するだけのものなので、IRQ0を有効化するだけではタイマーの割り込みは発生しない。
タイマーの割り込みを発生させるには、ペリフェラル側でも下記のような操作が必要になる。
- 割り込みの有効化
- 割り込み条件の設定
タイマーのレジスタ構成は下記のようになっている。
割り込みの有効化
タイマーの割り込みを有効化するにはINTE
レジスタをを使用する。INTEレジスタは下記のようになっている。
割り込み条件の設定
割り込み条件の設定にはALARM0〜3のレジスタを使用する。
ALARM0〜3に書き込まれた値がタイマーのカウンタであるTIMELRと一致すると4つのタイマーのうちの対応する割り込みが発生する。
タイマー0で使用するALARM0は下記のようになっている。
割り込みのクリア
割り込みが発生したら、割り込みハンドラの中でその割り込みをクリアする必要がある。割り込みをクリアするにはINTRレジスタの対応するビットをクリアする。
実装
main.cの中でそれぞれ実装する。
タイマーの初期化
タイマーの初期化処理では下記のことを行う。
- タイマーのリセット
- リセット完了待ち
- タイマー割り込みの有効化
- IRQ0の有効化
init_timer()
として下記のように実装する。
void init_timer() { /* reset TIMER */ write_reg_op(RESETS_RESET, (1<<21), OP_CLR); /* wait for reset TIMER to be done */ while (!read_reg(RESETS_RESET_DONE)&(1<<21)) ; /* Enable timer interrupt */ write_reg_op(TIMER_INTE, 1, OP_SET); /* Enable IRQ */ irq_set_enabled(0, true); /* TIMER_IRQ_0=0 */ }
割り込み条件の設定
sleep_us()
として下記のように実装する。
void sleep_us(uint32_t us) { uint32_t wakeup = read_reg(TIMER_TIMERRAWL); wakeup += us; write_reg(TIMER_ALARM0, wakeup); /* Wait For Interupt */ __asm__ __volatile__("wfi"); }
sleep_usでは下記の処理を行っている。
- 現在のタイマーの値を取得
- 待ちたい時間を加算した値をALARM0に書き込む
- WFIを使って割り込み待ち状態(sleep)にする
WFIはWait For Interrupt命令のことで、割り込みが入るまでCPUがスリープ状態になる。 割り込み(この場合はタイマー割り込み)が発生すると、スリープが解除されCPUが復帰する。
割り込みハンドラ
isr_timer0()
として下記のように実装する。
void isr_timer0() { /* Clear the timer0 irq */ write_reg_op(TIMER_INTR, 1, OP_CLR); }
isr_timer0
はstart.S
の割り込みベクタの定義のIRQ0の位置に登録しておく。
/* vector table */ .section .vectors, "ax" .align 2 .global __vectors __vectors: .word 0x20001000 .word reset .word hang .word hang .word hang .word hang .word hang .word hang .word hang .word hang .word hang .word hang .word hang .word hang .word hang .word hang .word isr_timer0
メイン処理
main
関数は下記のようになっている。
int main(void) { ///////////////////// // This initialise code is ported from https://github.com/dwelch67/raspberrypi-pico/blob/master/blinker00/notmain.c // release reset on IO_BANK0 write_reg_op(RESETS_RESET, 1<<5, OP_CLR); //IO_BANK0 //wait for reset to be done while(1) { if((read_reg(RESETS_RESET_DONE)&(1<<5))!=0) break; } write_reg_op(RESETS_RESET, (1<<8), OP_CLR); //PADS_BANK0 while(1) { if((read_reg(RESETS_RESET_DONE)&(1<<8))!=0) break; } ///////////////////// // GPIO init write_reg(SIO_GPIO_OE_CLR, (1ul<<25)); write_reg(SIO_GPIO_OUT_CLR, (1ul<<25)); uint32_t ra = read_reg(PADS_GPIO25); write_reg_op(PADS_GPIO25, (ra^0x40)&0xC0, OP_XOR); write_reg(IO_GPIO25_CTRL, 0x5); // Timer0 init init_timer(); // Blink write_reg(SIO_GPIO_OE_SET, (1ul<<25)); while (1) { sleep_us(100*1000); //100ms write_reg(SIO_GPIO_OUT_XOR, (1ul<<25)); } return 0; }
sleep_us関数を使って100msごとにLEDを点滅させている。
波形
問題なくPluseが100msとなっている。
まとめ
タイマーペリフェラルの割り込みでスリープを実装した。
- NVICで割り込みを有効化
- TIMERで割り込みを有効化
- TIMERで割り込みの発生条件を設定
これで使える。