目次
前
次
内部動作
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) ) ;
}
目次
前
次