目次

システムタイマー

 USOでは、10msごとにタスクが指定したカウント値を−1します。

 このデクリメント操作で、カウント値が0になれば、対応するタスクを
 WAITからREADYに状態遷移させます。

 カウント値のデクリメントとタスクの状態遷移は、関数timer_handlerが
 担当します。関数timer_handlerの定義は、以下です。
  void timer_handler(void)
  {
    UWORD tmp;
    UBYTE i ;

    tmp = (ready ^ vldtsk) ^ suspend ;
    for ( i = 0 ; i <= TSK_ID_MAX ; i++ ) {
      if ( tmp & 1 ) {
        tcb[i].wcount-- ;
        if ( tcb[i].wcount == 0 ) { rsm_tsk(i); }
      }
      tmp >>= 1 ;
    }
  }

 10msごとに関数timer_handlerをコールするシステム
 タイマーが必要です。

 タイマー割込みは、マイコンの仕様とコンパイラ仕様により、様々
 ですが、10msごとに関数timer_handlerをコールできれば、USO
 が時間待ちを代行します。

 一般に、タイマー割込み利用には、次の2つを定義します。


 ここでは、H8/Tiny、H8/3048F、AVRの10msタイマー
 割込み実現例を説明します。


H8/Tinyの10msタイマー割込み

 H8/Tinyは、タイマーA、V、Wの3種のタイマー/カウンタを持ちます。  タイマーVを利用し、16MHzのシステムクロックで動作とします。  タイマーVは、プリスケーラで、16MHzをより低い周波数に変換  できる機能を持ちます。16MHzを64分周して、250kHzを  生成します。  このクロックが、8ビットカウンタに入ります。250kHz=4us  を250回カウントすると、1msになります。ここまでがタイマーV  単体で実現できることです。0から250までカウントしたならば、  また0に戻すことができるので(コンペアマッチ処理と呼ばれます)  コンペアマッチモードで動作させます。  1msごとにタイマー割込みを発生できるので、10回数えて10ms  ごとのイベントとします。このためにカウンタ変数os_cntを用意します。  タイマーVのモード指定は、次の関数で実現します。 void init_timerV(void) { TV.TCRV0.BYTE = 0x4b ; TV.TCRV1.BIT.ICKS = 0 ; TV.TCSRV.BIT.CMFA = ON ; /* set compare match value */ TV.TCORA = 250 ; TV.TCORB = 255 ; /* real time monitor counter */ os_cnt = 0 ; }  1msごとにタイマー割込みが発生するので、変数os_cntをインクリメント  し、10になったならば0に戻します。そのときに、関数timer_handlerの  内容を実行します。 void int_timerv(void) { UBYTE dummy ; UWORD tmp; /* dummy read */ dummy = TV.TCSRV.BIT.CMFA; TV.TCSRV.BIT.CMFA = OFF ; /*-----------------*/ /* check WAIT task */ /*-----------------*/ os_cnt++ ; if ( os_cnt == 10 ) { os_cnt = 0 ; /* find tasks in WAIT */ tmp = (ready ^ vldtsk) ^ suspend ; for ( dummy = 0 ; dummy < TSK_ID_MAX ; dummy++ ) { if ( tmp & 1 ) { tcb[dummy].wcount-- ; if ( tcb[dummy].wcount == 0 ) { rsm_tsk(dummy); } } tmp >>= 1 ; } } }  

H8/3048Fの10msタイマー割込み

 H8/3048Fは、ITUと呼ぶ5個のタイマー/カウンタを持ちます。   ITU => Integrated Timer Unit  ITU0を利用し、16MHzのシステムクロックで動作とします。  ITU0は、16ビットのカウンタをもち、このカウンタで  16MHzをより低い周波数に変換できる機能を持ちます。  16MHzを16000分周して、1kHz=1msを生成します。  ここまでがITU0が単体で実現できることです。16000までカウントした  ならば、また0に戻すことができるので(コンペアマッチ処理)、その  モードで動作させます。  1msごとにタイマー割込みを発生できるので、10回数えて10ms  ごとのイベントとします。このためにカウンタ変数os_cntを用意します。  ITU0のモード指定は、次の関数で実現します。 unsigned char os_cnt ; void init_timer0(void) { /* stop timer */ ITU.TSTR.BIT.STR0 = OFF ; /* TOER : Timer Output Enable Register */ ITU.TOER.BYTE = 0 ; /* TIOR : Timer I/O Control Register */ ITU0.TIOR.BYTE = 0 ; /* TCR : Timer Control Register */ ITU0.TCR.BYTE = 0x20 ; /* TIER : Timer Interrupt Enable Register */ ITU0.TIER.BIT.IMIEA = ON ; /* reference */ ITU0.GRA = 16000 ; ITU0.GRB = 65535 ; /* counter */ ITU0.TCNT = 0 ; /* start timer */ ITU.TSTR.BIT.STR0 = ON ; /* set os counter */ os_cnt = 10 ; }  1msごとにタイマー割込みが発生するので、変数os_cntをインクリメント  し、10になったならば0に戻します。そのとき、同時に関数timer_handler  を起動します。 #pragma interrupt int_imia0 void int_imia0(void) { unsigned char dummy ; /* clear flag */ dummy = ITU0.TSR.BIT.IMFA ; ITU0.TSR.BIT.IMFA = OFF ; /* os handling */ os_cnt-- ; if ( os_cnt > 0 ) return ; os_cnt = 10 ; timer_handler(); }

AVRの10msタイマー割込み

 AVRシリーズは、タイマー0とタイマー1を必ずもつので、どちらを  利用するかを先に決めておきます。  ここでは、タイマー0を利用します。  タイマー0は、プリスケーラで、システムクロックを8、64、256、  1024のいずれかに分周できます。10.24MHzをシステムクロックに  分周比を1024とすると、10kHzを生成します。  タイマー0には、8ビットのカウンタがあるので、このカウンタでさらに  100分周して、100Hzを生成します。この100Hzが、10ms  ごとの割込み周期になります。  タイマー0では、オーバーフロー割込みのみ利用できるで、255から0に  カウンタの値が遷移するときに、割込みが発生します。割込みが発生したら  関数timer_handlerをコールします。  また、カウンタの値を256−100=156に再設定します。 SIGNAL( SIG_OVERFLOW0 ) { /* set timer0 counter value */ TCNT0 = 156 ; /* call timer handler */ timer_handler(); }  タイマー0のプリスケーラの分周比を設定し、オーバーフロー割込みが  発生するように初期化する関数を定義します。 void init_timer0(void) { /* system clock divided by 1024 */ TCCR0 = 0x05 ; /* set timer0 counter value */ TCNT0 = 156 ; /* enable interrupt */ TIMSK |= (1 << TOIE0) ; }

起動してからの経過時間

 USOを自分で利用してみると、1ms単位の待ち時間が欲しい  場面が多発しました。  LCDの初期化やA/Dコンバータの待ち時間を作り出したい場面  では、10msは意外に使いにくかったのです。  そこで、WindowsのAPIにあるような、システムが起動してからの  経過時間を参照できる関数を定義します。 typedef unsigned long ULONG ; ULONG milli_cnt ; ULOGN milli_sec(void) { return milli_cnt ; }  変数milli_cntは、1msごとにタイマー割込みを発生させて  +1していきます。  このカウンタがあると、次のようにミリ秒単位の待ちを生成  する関数delay_msを定義できます。 typedef unsigned short UWORD ; void delay_ms(UWORD x) { ULONG mstarget ; /* calculate target interval */ mstarget = milli_sec() + x ; /* delay */ while ( milli_sec() < mstarget ) ; }
目次

inserted by FC2 system