目次
前
次
システムタイマー
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 ) ;
}
目次
前
次