TIMER0による周期タイマー割り込み
周期タイマーを利用する方法として、Tick Timer を用いるもの TIMER0/1/2 を用いるもの、Wakeup Timer を用いるものがありますが、ここでは TIMER0 の利用例を示します。以下の例では、プリスケーラを 2^14 = 16384 とし、16MHz クロックでは1周期約1msとなり、TIMER_TICK_MS周期ごとに割り込みを発生させます。
以下にAppQAPI による実行例およびvAHI_Timer0RegisterCallback()による割り込みハンドラ例を示します。
※DIOの出力 (PWM 出力) と割り込みを同時に使う事は出来ません。
※時間保証は有りません。vAHI_Timer0RegisterCallback() により直接コールバック関数を記述する事で改善はされますが、無線部 MAC 層などの処理などが優先される場合があり、非常に短い周期(1ms未満では顕著)での実行では、割り込みが飛ばされたり、ジッターが散見されます。
AppQAPI によるコード
AppQAPIは、割り込み遅延実行を行うため、割り込み時の処理には制限がありません(無線処理もここで行う)。反面、割り込みハンドラからメインループの処理まで待たされるため、遅延の影響が大きくなります。
#define TIMER_TICK_MS 5 PRIVATE void vTimerConfig(void) { vAHI_TimerFineGrainDIOControl(0xFF); // disabple all DIOs for timer use vAHI_TimerEnable(E_AHI_TIMER_0, 14, // プリスケーラ 2^14 (1.024ms) FALSE, TRUE, // タイマーの終了区間で割り込み発生 FALSE); vAHI_TimerClockSelect(E_AHI_TIMER_0, FALSE, // 内部の 16MHz クロックを利用する TRUE); vAHI_TimerStartRepeat(E_AHI_TIMER_0, 0x0000, TIMER_TICK_MS); // タイマー期間 } /* 以下は AppQAPI の場合 * JenNet の場合は、同様に vJenie_CbHwEvent() が割り込み遅延実行処理として呼び出されます。 */ PUBLIC void AppColdStart(void) { ... /* register hardware interrupt handler */ (void)u32AppQApiInit(NULL, NULL, NULL); // 直接の割り込み処理は、AppQAPI 内部で処理される ... vMain(); } PRIVATE void vMain(void) { while(1) { vAHI_CpuDoze(); vCheckEventQueues(); ... そのほかの処理 ... } } PRIVATE void vCheckEventQueues(void) { ... do { psAHI_Ind = psAppQApiReadHwInd(); if (psAHI_Ind != NULL) { vProcessIncomingHwEvent(psAHI_Ind); vAppQApiReturnHwIndBuffer(psAHI_Ind); } } while (psAHI_Ind != NULL); ... } /* 割り込み遅延実行のハンドラ */ PRIVATE void vProcessIncomingHwEvent(AppQApiHwInd_s *psAHI_Ind) { switch (psAHI_Ind->u32DeviceId) { case E_AHI_DEVICE_TIMER0: { ... タイマー割り込みの遅延実行 ... } break; } }
vAHI_TimerXRegisterCallback()によるコード
vAHI_TimerXRegisterCallback() を用いた例です(Xは0,1,2)。ハードウェアからの割り込みハンドラですから、割り込み発生後最短で呼び出されるため、遅延が少ないことが特徴です。同時に、ハンドラ内の処理も最短で行います。
例えばIOピンの L/H を変更するといった単純なものは許容されますが、時間のかかる処理、IOも処理によっては動作しない事が考えられます。 また、スタックAPI(JenNet)の呼び出しや、無線に関連する処理(送信手続きなど)もここでは行いません。
ただし遅延が少ないとはいえ、MAC層(無線部)での処理は最優先され、その影響は免れません。目安として1KHz を超えるような高周期の場合、顕著になります。
#define TIME_PRESCALSE 0 #define TICK_PERIOD_COUNT 320 // タイマー割り込み開始 PUBLIC void vTime_Init(void) { u32TimerTicks = 0; vAHI_TimerEnable(E_AHI_TIMER_0, TIMER_PRESCALE, FALSE, TRUE, FALSE); vAHI_TimerClockSelect(E_AHI_TIMER_0, FALSE, FALSE); vAHI_Timer0RegisterCallback(vTime_TimerISR); vAHI_TimerStartRepeat(E_AHI_TIMER_0, 0, TICK_PERIOD_COUNT); vAHI_TimerDIOControl(E_AHI_TIMER_0, FALSE); } // 割り込みハンドラ PRIVATE void vTime_TimerISR(uint32 u32Device, uint32 u32ItemBitmap) { u32TimerTicks++; // 簡単な処理のみ実施し、速やかに関数から抜ける。 }