GBAのタイマーは4つ用意されており、カウントアップにはCPUのクロック数が基準になっています。人間の体感では秒より下数桁はなかなか知覚できない世界です。少し背中がむず痒くなるかもしれません。さて、GBAのCPUクロック数は16.78Mhzです。Hzにすると16,780,000になります。クロック数を1秒で割り算すると1サイクル時間がわかるという寸法です。結果は0.00000005959475566となりました。これらを人間に理解できる単位に直してみましょう。
秒 | s | 0.00000005959475566 |
ミリ秒 | ms | 0.00005959475566 |
マイクロ秒 | us | 0.05959475566 |
ナノ秒 | ns | 59.59475566 |
秒を1000倍したのがミリ秒、ミリ秒を1000倍したのがマイクロ秒、マイクロ秒を1000倍したのがナノ秒という単位です。約59ns(ナノ秒)がCPUの1クロック数ということがわかりました。さて、タイマーは1, 64, 256, 1024クロックごとにインクリメント(カウントアップ)する設定が可能です。掛け算をして表にすると以下のようになります。
1クロック | 1サイクル時間(ns) |
1 | 59.59475566 |
64 | 3814.064362 |
256 | 15256.25745 |
1024 | 61025.0298 |
そろそろ頭痛くなってきました。書いている中の人もこの数値に意味を持たせることはできるのだろうかと思ってしまいます。もう少しお付き合いください。このタイマーカウンターは16bitである為、0x0から0xffffという範囲が決まっています。この入れ物を溢れさせる数0x10000(10進数は65536)を計算したものが次の表です。
クロック * 0x10000 | ns | us | ms | s |
1 * 0x10000 | 3905601.907 | 3905.601907 | 3.905601907 | 0.003905601907 |
64 * 0x10000 | 249958522.1 | 249958.5221 | 249.9585221 | 0.2499585221 |
256 * 0x10000 | 999834088.2 | 999834.0882 | 999.8340882 | 0.9998340882 |
1024 * 0x10000 | 3999336353 | 3999336.353 | 3999.336353 | 3.999336353 |
ナノ秒の世界からようやく秒の世界に戻ってきました。上記の表でいう256クロックでカウンタを0から開始すると、約1秒後(0.99834...)にカウンタが溢れるということがわかります。赤字の部分です。このことから0.5秒後に入れ物を溢れさせたいなら初期値を0x10000/2にすれば良いということもわかります。
The GBA includes four incrementing 16bit timers. Timer 0 and 1 can be used to supply the sample rate for DMA sound channel A and/or B. 4000100h - TM0CNT_L - Timer 0 Counter/Reload (R/W) 4000104h - TM1CNT_L - Timer 1 Counter/Reload (R/W) 4000108h - TM2CNT_L - Timer 2 Counter/Reload (R/W) 400010Ch - TM3CNT_L - Timer 3 Counter/Reload (R/W)
タイマーの値はu16(16bit)なので65536(0x10000)まで増加すると次は0になります。このことをオーバーフローといいます。オーバーフロー時、割り込みを発生させることやカスケード機能(後述)を利用することができます。なお0以外の初期値を入れておくとオーバーフロー時、その初期値に戻ります。
4000102h - TM0CNT_H - Timer 0 Control (R/W) 4000106h - TM1CNT_H - Timer 1 Control (R/W) 400010Ah - TM2CNT_H - Timer 2 Control (R/W) 400010Eh - TM3CNT_H - Timer 3 Control (R/W) Bit Expl. 0-1 Prescaler Selection (0=F/1, 1=F/64, 2=F/256, 3=F/1024) 2 Count-up Timing (0=Normal, 1=See below) ;Not used in TM0CNT_H 3-5 Not used 6 Timer IRQ Enable (0=Disable, 1=IRQ on Timer overflow) 7 Timer Start/Stop (0=Stop, 1=Operate) 8-15 Not used When Count-up Timing is enabled, the prescaler value is ignored, instead the time is incremented each time when the previous counter overflows. This function cannot be used for Timer 0 (as it is the first timer). F = System Clock (16.78MHz).
下位0-1bitにて1, 64, 256, 1024クロックのどのタイミングでインクリメントするかを決めます。下位2bit目は、カスケード機能と呼ばれていて下位のタイマー(タイマー1なら0、タイマー2なら1)がオーバーフローしたときに、上位のタイマーの値がインクリメントされるという機能です。カスケード機能を使うと上位のタイマー自身は周期によってインクリメントされないようになります。ちなみにタイマー0は下位のタイマーがないのでカスケード機能は使えません。6bit目はタイマーがオーバーフローしたときに割り込みを発生させるかの設定です。7bit目はセットした瞬間にタイマーが開始されます。順番としてはTMxCNT_Lで値を入れてからTMxCNT_Hを実行してください。
int main(void) { // モード設定 SetMode(MODE_0 | OBJ_ENABLE | OBJ_1D_MAP); SpriteInit(); SpriteSetDatChr((u16*)&sprTiles, sprTilesLen); SpriteSetDatPal((u16*)&sprPal); u32 i; // タイマー0 5個のスプライトを使用 for(i=0; i<5; i++) { SpriteSetSize (i, OBJ_SIZE(Sprite_8x8), OBJ_SQUARE, OBJ_16_COLOR); SpriteSetChr (i, 0); SpriteMove (i, 20+i*8, 20); SpriteSetPalNo(i, 0); } // タイマー1 5個のスプライトを使用 for(i=5; i<10; i++) { SpriteSetSize (i, OBJ_SIZE(Sprite_8x8), OBJ_SQUARE, OBJ_16_COLOR); SpriteSetChr (i, 0); SpriteMove (i, 40+i*8, 20); SpriteSetPalNo(i, 0); } // タイマー設定 REG_TM0CNT_L = 0; REG_TM1CNT_L = 0; REG_TM0CNT_H = TIMER_FREQ_PER_256 | TIMER_START; REG_TM1CNT_H = TIMER_CASCADE | TIMER_START; for(;;) { WaitForVsync(); SpriteShowNumber(0, REG_TM0CNT_L); SpriteShowNumber(5, REG_TM1CNT_L); } }
スプライトを使って数字を表示しています。左側がタイマー0、右側がタイマー1です。