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