目次

内部動作

 RTOSを実現するには、次の3つの記述方法を考えなければなりません。
  スケジューラ
  ディスパッチャ
  システムコール

 スケジューラ、ディスパッチャは、Cのmain関数で実現します。

 システムコールは、個々に関数を定義しています。

 USOを新しくしても、スケジューラ、ディスパッチャは変えません。
 システムコールの内部を変更します。


状態管理変数

 タスクの状態を管理するための変数が必要になります。  タスクは、RUN、READY、SUSPEND、WAITのいずれかの状態を  取るので、1タスクあたり2ビットで管理できます。  メモリの少ないマイコンでは、状態管理のためだけに多くの変数を  使えないので、工夫して1ビットの組み合わせで状態を表現します。  4状態あるうちで、RUNはREADYであることがわかれば、遷移  させられます。余事象の考え方で、3変数あれば状態を管理  できます。  RUNを除けば、READY、SUSPEND、WAITと3状態を表現できれば  よいので、変数は3個で足ります。  READY、SUSPEND、WAITを表現する変数を各々  とします。  タスクrが、READYかどうかは、変数readyのrビット目で判断します。  rビット目が1であれば、タスクrはREADY状態にあります。  タスクsが、SUSPENDかどうかは、変数suspendのsビット目で判断します。  sビット目が1であれば、タスクsはSUSPEND状態にあります。  タスクwが、WAITかどうかは、変数waitqの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);  以下のように定義します。  void cre_tsk(UBYTE tid,void (*tsk)(void))  {   tcb[tid].tsk = tsk;   tcb[tid].wcount = 0;  }  システムコールcre_tskを使うだけでは、タスクの状態は不定です。

システムコールsta_tsk定義

 sta_tskは、タスクIDを使い、タスクの初期状態を指定します。  タスクIDとタスク状態をパラメータとします。 sta_tsk(TSK_ID0,TTS_READY) ; sta_tsk(TSK_ID1,TTS_SUSPEND);  指定された状態に合わせて、変数ready、suspend、waitqの  タスクIDで指定するビットを0あるいは1にします。  以下のように定義します。  void sta_tsk(UBYTE tid,UBYTE sta)  { UWORD tmp ; tmp = (1 << tid);   if ( sta == TTS_READY ) { ready |= tmp; }   if ( sta == TTS_SUSPEND ) { suspend |= tmp; }   if ( sta == TTS_WAIT ) { waitq |= tmp; }  }  メモリに余裕がある場合、32バイト分の配列を用意して  次のように記述できます。  void sta_tsk(UBYTE tid,UBYTE sta)  { unsigned long tmp ; /* get bit pattern */ tmp = *(bpat+tid) ; /* set */   if ( sta == TTS_READY ) { ready |= tmp; }   if ( sta == TTS_SUSPEND ) { suspend |= tmp; }   if ( sta == TTS_WAIT ) { waitq |= tmp; }  }  該当するビットだけが0あるいは1であるパターンを  配列に入れて、シフトにかかる時間を節約します。

システムコールrsm_tsk定義

 rsm_tskは、タスクIDを使い、該当タスクをREADY状態にします。  タスクIDをパラメータとします。 rsm_tsk(TSK_ID1) ;  変数readyは、タスクIDで指定するビット位置の値を1にします。  変数suspendは、タスクIDで指定するビット位置の値を0にします。  以下のように定義します。  void rsm_tsk(UBYTE tid)  { UWORD tmp ; tmp = (1 << tid);   ready |= tmp;   suspend &= ~tmp;   waitq &= ~tmp;  }  メモリに余裕がある場合、32バイト分の配列を用意して  次のように記述できます。  void rsm_tsk(UBYTE tid)  { unsigned long tmp ; unsigned long tmpx ; /* get bit pattern */ tmp = *(bpat+tid) ; tmpx = tmp ^ 0xffff ; /* bit operate */   ready |= tmp;   suspend &= tmpx;   waitq &= tmpx;  }

システムコールsus_tsk定義

 sus_tskは、タスクIDを使い、該当タスクをSUSPEND状態にします。  タスクIDをパラメータとします。 sus_tsk(TSK_ID2) ;  変数readyは、タスクIDで指定するビット位置の値を0にします。  変数suspendは、タスクIDで指定するビット位置の値を1にします。  以下のように定義します。  void sus_tsk(UBYTE tid)  { UWORD tmp ; tmp = (1 << tid);   ready &= ~tmp;   suspend |= tmp;   waitq &= ~tmp;  }  メモリに余裕がある場合、32バイト分の配列を用意して  次のように記述できます。  void sus_tsk(UBYTE tid)  { unsigned long tmp ; unsigned long tmpx ; /* get bit pattern */ tmp = *(bpat+tid) ; tmpx = tmp ^ 0xffff ; /* bit operate */   ready &= tmpx;   suspend |= tmp ;   waitq &= tmpx;  }

システムコール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にします。  変数waitqは、変数run_tskで指定するビット位置の値を1にします。  変数wcountにカウント値を設定します。  カウント値は、タイマー割込みでカウントダウンします。  カウント値が0になると、タイマー割込み内部で、該当タスクを  WAITからREADYに状態遷移させます。  システムコールそのものは、以下のように定義します。  void wai_tsk(UWORD x)  { UWORD tmp ; tmp = (1 << run_tsk);   ready &= ~tmp;   suspend &= ~tmp;   waitq |= tmp;   tcb[run_tsk].wcount = x ;  }  メモリに余裕がある場合、32バイト分の配列を用意して  次のように記述できます。  void wai_tsk(UWORD x)  { unsigned long tmp ; unsigned long tmpx ; /* get bit pattern */ tmp = *(bpat+tid) ; tmpx = tmp ^ 0xffff ; /* bit operate */   ready &= tmpx;   suspend &= tmpx;   waitq |= tmp ; /* update counter */   tcb[run_tsk].wcount = x ;  }  WAITからREADYへの状態遷移の仕掛けは、タイマー割込み  を利用します。  詳細は、別の章で説明します。

RTOS初期化

 RTOSを使う場合には、タスクの管理が必要になります。  タスク状態を管理する変数を初期化しなければなりません。  USOでは、関数init_osをコールします。  内部は、単純に3変数に0を設定しています。  void init_os(void)  {   ready = 0;   suspend = 0;   waitq = 0;  }  メモリに余裕がある場合、64バイト分の配列を用意して  次のように記述できます。 UWORD bpat[16] ;  void init_os(void)  { UBYTE i; /* clear queue */   ready = 0;   suspend = 0;   waitq = 0; /* initialize bit pattern */ for ( i = 0 ; i < 16 ; i++ ) { *(bpat+i) = (1 << i ); }  }
 3つの状態を扱う変数は、ひとつの関数を用意すれば  次のようにまとめて処理できます。 #define MASKFFFF 0xffff void update_tsk_state(UBYTE tid,UBYTE sta) { UWORD tmp ; UWORD tmpx ; /* get bit pattern */ tmp = (1 << tid); tmpx = tmp ^ MASKFFFF ; /* READY */ if ( sta == TTS_READY ) { ready |= tmp ; suspend &= tmpx; waitq &= tmpx; } /* SUSPEND */ if ( sta == TTS_SUSPEND ) { ready &= tmpx; suspend |= tmp; waitq &= tmpx; } /* WAIT */ if ( sta == TTS_WAIT ) { ready &= tmpx; suspend &= tmpx; waitq |= tmp; } }  上の関数を使うと、他のシステムコールは、ラッパー関数として  扱えるようになります。 void cre_tsk(UBYTE tid,void (*tsk)(void)) { tcb[tid].tsk = tsk ; tcb[tid].wcount = 0 ; } void sta_tsk(UBYTE tid,UBYTE sta) { update_tsk_state(tid,sta); } void rsm_tsk(UBYTE tid) { update_tsk_state(tid,TTS_READY); } void sus_tsk(UBYTE tid) { update_tsk_state(tid,TTS_SUSPEND); } void slp_tsk(void) { sus_tsk(run_tsk); } void wai_tsk(UWORD x) { update_tsk_state(run_tsk,TTS_WAIT); tcb[run_tsk].wcount = x ; } UBYTE is_tsk_ready(UBYTE tid) { return( ready & (1 << tid) ) ; }
目次

inserted by FC2 system