目次
前
次
内部動作
RTOSを実現するには、スケジューラ、ディスパッチャ、システムコールを
どう記述するかを考えなければなりません。
スケジューラ、ディスパッチャは、Cのmain関数で実現します。
システムコールは、個々に関数を定義しています。
スケジューラ、ディスパッチャ
USOは、PLC(Programable Logic Controller)の動作を参考にしています。
PLCは、全回路をスキャンして、必要であれば、その回路を動かします。
USOのスケジューラ、ディスパッチャは、タスクの状態をスキャンして
READYであれば、対応する関数を実行します。
run_tsk = TSK_ID0 ;
while ( ON ) {
/* タスクの状態がREADYであれば、タスク実行 */
/* スキャン */
}
タスクの状態をスキャンするために、変数run_tskを用意します。
変数run_tskを、TSK_ID0からTSK_ID_MAX-1まで増やし、タスクの
状態を調べていきます。
run_tsk = TSK_ID0 ;
while ( ON ) {
/* タスクの状態がREADYであれば、タスク実行 */
/* スキャン */
run_tsk++;
if ( run_tsk == TSK_ID_MAX ) {
run_tsk = TSK_ID0 ;
}
}
該当タスクの状態が、READYかを確認する必要があります。
タスクのIDを与えて、READYであればYESを返す関数を定義
して、使います。この関数を、is_tsk_readyとします。
run_tsk = TSK_ID0 ;
while ( ON ) {
if ( is_tsk_ready( run_tsk ) == YES ) {
/* タスク実行 */
}
/* スキャン */
run_tsk++;
if ( run_tsk == TSK_ID_MAX ) {
run_tsk = TSK_ID0 ;
}
}
タスク実行は、関数の開始アドレスをテーブルに入れておき
開始アドレスを取り出す方式が楽なので、ポインタを使って
タスク関数に分岐します。
まとめると、次のようになります。
run_tsk = TSK_ID0 ;
while ( ON ) {
pcur_tsk = tcb[run_tsk] ;
if ( is_tsk_ready( run_tsk ) == YES ) {
(*(pcur_tsk.tsk))();
}
run_tsk++;
if ( run_tsk == TSK_ID_MAX ) {
run_tsk = TSK_ID0 ;
}
}
配列tcbは、別の章で説明します。
状態管理変数
タスクの状態を管理するための変数が必要になります。
タスクは、RUN、READY、SUSPEND、WAIT、DORMANTのいずれか
の状態を取るので、1タスクあたり3ビットで管理できます。
メモリの少ないマイコンでは、状態管理のためだけに多くの変数を
使えないので、工夫して1ビットの組み合わせで状態を表現します。
5状態あるうちで、RUNはREADYであることがわかれば、遷移する
必要はないので、変数は4個あれば状態を管理できます。
従って、READYを表現するための変数readyを用意します。
RUNを除くと、READY、SUSPEND、WAIT、DORMANTと4状態を表現
できればよいので、変数は3個で足ります。
READY、SUSPEND、DORMANTを表現する変数を各々
とします。
タスクrが、READYかどうかは、変数readyのrビット目で判断します。
rビット目が1であれば、タスクrはREADY状態にあります。
タスクsが、SUSPENDかどうかは、変数suspendのsビット目で判断します。
sビット目が1であれば、タスクsはSUSPEND状態にあります。
タスクvが、USOで管理できるか否かは、変数vldtskのvビット目
で判断します。
vビット目が1であれば、タスクvはUSOで管理できます。
タスクwが、WAITかどうかは、3変数ready、suspend、vldtskの
論理演算で求めた結果のwビット目で判断します。
wビット目が1であれば、タスクwはWAIT状態にあります。
システムコールcre_tsk
cre_tskは、タスクIDとタスク関数を関連づけ、タスク関数の
実行開始アドレスをTCB(Task Control Block)に設定します。
タスクIDとタスク関数名をパラメータとします。
cre_tsk(TSK_ID0,tsk0_proc);
cre_tsk(TSK_ID1,tsk1_proc);
内部は、次のように処理します。
変数vldtskのタスクIDに相当するビットに、1を設定。
タスクIDで指定する変数wcountを、0クリア。
変数wcountは、WAITを実現するためのカウンタで、10msのN倍
(1〜65535)を記憶します。
以下のように定義します。
void cre_tsk(UBYTE tid,void (*tsk)(void))
{
if ( tid >= TSK_ID_MAX ) return ;
vldtsk |= (1 << tid) ;
tcb[tid].tsk = tsk;
tcb[tid].wcount = 0;
}
システムコールsta_tsk
sta_tskは、タスクIDを使い、タスクの初期状態を指定します。
タスクIDとタスク状態をパラメータとします。
sta_tsk(TSK_ID0,TTS_READY) ;
sta_tsk(TSK_ID1,TTS_SUSPEND);
指定された状態に合わせて、変数readyとsuspendのタスクIDで
指定するビットを0あるいは1にします。
以下のように定義します。
void sta_tsk(UBYTE tid,UBYTE sta)
{
if ( tid >= TSK_ID_MAX ) return ;
if ( sta == TTS_READY ) {
ready |= (1 << tid);
suspend &= ~(1 << tid);
}
if ( sta == TTS_WAIT ) {
ready &= ~(1 << tid);
suspend &= ~(1 << tid);
}
if ( sta == TTS_SUSPEND ) {
ready &= ~(1 << tid);
suspend |= (1 << tid);
}
}
システムコールrsm_tsk
rsm_tskは、タスクIDを使い、該当タスクをREADY状態にします。
タスクIDをパラメータとします。
rsm_tsk(TSK_ID1) ;
変数readyは、タスクIDで指定するビット位置の値を1にします。
変数suspendは、タスクIDで指定するビット位置の値を0にします。
以下のように定義します。
void rsm_tsk(UBYTE tid)
{
if ( tid >= TSK_ID_MAX ) return ;
ready |= (1 << tid);
suspend &= ~(1 << tid);
}
システムコールsus_tsk
sus_tskは、タスクIDを使い、該当タスクをSUSPEND状態にします。
タスクIDをパラメータとします。
sus_tsk(TSK_ID2) ;
変数readyは、タスクIDで指定するビット位置の値を0にします。
変数suspendは、タスクIDで指定するビット位置の値を1にします。
以下のように定義します。
void sus_tsk(UBYTE tid)
{
if ( tid >= TSK_ID_MAX ) return ;
ready &= ~(1 << tid);
suspend |= (1 << tid);
}
システムコールslp_tsk
slp_tskは、タスクが自身をSUSPEND状態にするときに使います。
slp_tsk() ;
タスクがCPUを使っているとき、タスクIDは変数run_tskに入っています。
システムコールsus_tskに、run_tskを入力しタスク自身の状態を変更します。
以下のように定義します。
void slp_tsk(void)
{
sus_tsk(run_tsk);
}
システムコールwai_tsk
wai_tskは、タスクが自身をWAIT状態にするときに使います。
wai_tsk( 500 ) ;
USOでは、WAITは、自身で時間待ちをしている状態とします。
変数ready、suspendは、変数run_tskで指定するビット位置の値を0にします。
変数wcountにカウント値を設定します。
カウント値は、タイマー割込みでカウントダウンします。
カウント値が0になると、タイマー割込み内部で、該当タスクを
WAITからREADYに状態遷移させます。
システムコールそのものは、以下のように定義します。
void wai_tsk(UWORD x)
{
ready &= ~(1 << run_tsk);
suspend &= ~(1 << run_tsk);
tcb[run_tsk].wcount = x ;
}
WAITからREADYへの状態遷移の仕掛けは、別の章で説明します。
目次
前
次