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++; // 簡単な処理のみ実施し、速やかに関数から抜ける。
}