目次
前
次
利用例
RTOSに限らず、OSやモニタを使う場合には、利用例があると
使い方を理解しやすいものです。
自分の場合、はじめてμITRONのシステムコール説明を
読んだとき、どう利用するかイメージできませんでした。
そこで、それまで作ったファームウエアを、RTOSを利用して
書き換えたところ、システムコールの使い方がわかりました。
ここでは例をあげて、USOシステムコールを説明します。
システム仕様
次の機能をもつ装置を実現します。
周囲が暗くなると、LEDを点滅する。
周囲が明るいときは、LEDを消灯する。
上のような機能をもった防犯装置があります。
RTOSの使い方を知りたいときは、このように身近な装置の動作を
システムコールを利用して記述してみるとよいでしょう。
身近な装置の動作を、RTOSを利用して作成する場合は
次の手順に従ってみると、案外簡単に記述できます。
- 機能を洗い出します。
- ひとつの機能を、ひとつのタスクに割り当てします。
- タスクを、システムコールを利用して記述します。
- 起動時のタスク状態を、定義します。
- プログラム全体をまとめます。
この手順に従うと、機械的にファームウエアを作成できます。
では、この装置の動作を、ファームウエアとして作成します。
機能分割
機能を分割するには、要求仕様を見直します。
周囲が暗くなると、LEDを点滅する。
周囲が明るいときは、LEDを消灯する。
このシステムでは、最低3つの機能が必要になります。
- 周囲の明暗を判断しています。
- LEDの点滅を制御しています。
- LEDの消灯を扱っています。
タスクを分割できたので、タスクを実現する関数を割り当てます。
タスク割当て
タスクを実現する関数を、tsk0_proc、tsk1_proc等に割り当てます。
- 周囲の明暗を判断しています -> tsk0_proc
- LEDの点滅を制御しています -> tsk1_proc
- LEDの消灯を扱っています -> tsk2_proc
タスクを実現する関数を決めたので、内部を作成します。
タスク記述
タスクは、面倒なものから記述します。
- 周囲の明暗を判断しています -> tsk0_proc
- LEDの点滅を制御しています -> tsk1_proc
- LEDの消灯を扱っています -> tsk2_proc
3タスクのうち、「LEDの点滅制御」が最も面倒に
見えるので、このタスクから記述します。
LEDの点滅制御(tsk1_proc)
LEDが、どこかのビットに接続されているとして
そのビットをLED_BITとします。
周期的に、点灯、消灯を繰り返せばよいので、システム
コールを使って、1秒単位で点灯、消灯を繰り返します。
ここまで仕様を決めたなら、スケルトンを記述します。
void tsk1_proc(void)
{
/* 点滅 */
wai_tsk( 100 ); /* 1000ms */
}
LEDが、点灯、消灯を1秒ごとに、繰り返すにはカウンタ
を用意します。
カウンタのLSB(最下位ビット)値を、出力すれば、点灯
消灯を繰り返えして、点滅になります。
カウンタは、1秒ごとに+1すればよいでしょう。
動作を考えたので、完成させます。
typedef unsigned char UBYTE ;
UBYTE count ;
#define INTERVAL1 100
void tsk1_proc(void)
{
LED_BIT = count & 1 ; /* 点滅 */
count++ ; /* カウンタ値更新 */
wai_tsk( INTERVAL1 ); /* 1000ms */
}
明暗を判断(tsk0_proc)
明暗は、外部ハードウエアが1と0を出力してくると
仮定して、接続ピンをBLIGHT_BITとします。
BLIGHT_BITが1ならば、明るいのでLEDを消灯させます。
LEDの消灯は、タスク2が担当しているので、タスク2を
起動して消灯させればよいでしょう。
LEDの点滅は、タスク1が担当しているので、タスク1を
SUSPENDにして点滅をさせないようにします。
BLIGHT_BITが0ならば、暗いのでLEDを点滅させます。
LEDの点滅は、タスク1が担当しているので、タスク1を
起動して点滅させればよいでしょう。
1分ごとにでも、明暗判定すればよいので、周期的に動作
するように指定します。
ここまで仕様を決めたなら、スケルトンを記述します。
void tsk0_proc(void)
{
/* 判定 */
if ( BLIGHT_BIT ) {
sus_tsk( TSK_ID1 ) ;
rsm_tsk( TSK_ID2 ) ;
} else {
rsm_tsk( TSK_ID1 ) ;
}
wai_tsk( 60*100 ); /* 1 minutes */
}
一度タスクの状態を決定したならば、何度もタスクの状態を
変更するのは無駄です。
状態変数を利用して、無駄な動作をさせないようにします。
UBYTE sflag ;
#define TURN_OFF 0
#define FLASHING TURN_OFF+1
#define INTERVAL0 6000
void tsk0_proc(void)
{
/* 判定 */
if ( BLIGHT_BIT ) {
if ( sflag == FLASHING ) {
sflag = TURN_OFF ;
sus_tsk( TSK_ID1 ) ;
rsm_tsk( TSK_ID2 ) ;
}
} else {
if ( sflag == TURN_OFF ) {
sflag = FLASHING ;
rsm_tsk( TSK_ID1 ) ;
}
}
wai_tsk( INTERVAL0 ); /* 1 minutes */
}
LEDの消灯(tsk2_proc)
LEDはLED_BITに接続されているので、消灯のコード
を出力します。
一度だけ処理を実行して、そのままSUSPENDになればよい
での、システムコールslp_tskを使います。
ここまで仕様を決めたなら、タスクを記述します。
#define LED_OFF 1
void tsk2_proc(void)
{
LED_BIT = LED_OFF ;/* 消灯 */
slp_tsk();
}
タスクを実現する関数を定義したので、起動時の
タスクの状態を決めます。
起動時のタスク状態定義
タスクを実現する関数の定義から、タスク1、2は
タスク0からREADYにされています。
タスク1、2は、起動時にSUSPENDでよいでしょう。
タスク0は、起動時はREADYとします。
sta_tsk(TSK_ID0,TTS_READY) ;
sta_tsk(TSK_ID1,TTS_SUSPEND);
sta_tsk(TSK_ID2,TTS_SUSPEND);
プログラム全体
タスクの定義をしただけでは、動作できないので、以下の
内容を定義します。
- I/O(Input and Output)操作
- 10msタイマー割込み定義
- 内部変数定義と初期化
ファームウエアを実装するマイコンにより、I/Oの内容は
異なるので、I/O操作はマイコンに合わせて作成します。
各タスクで利用する内部変数定義と初期化をしておかないと
希望する動作を実現できません。動作に合わせて作成します。
これらは、関数user_initialize内部で記述するとよいでしょう。
タスクは、10msの倍数のWAIT状態を必要とするので、10ms
タイマー割込みを定義し、タスクの時間待ちを管理します。
タイマーハンドラと呼ぶ関数を定義して対応します。
その他にRTOSが動作するために必要となる初期化や定義は、関数
init_osに記述します。
mainは、以下のように定義します。
void main(void)
{
TCBP pcur_tsk ;
/* initialize monitor */
init_os();
cre_tsk(TSK_ID0,tsk0_proc);
cre_tsk(TSK_ID1,tsk1_proc);
cre_tsk(TSK_ID2,tsk2_proc);
sta_tsk(TSK_ID0,TTS_READY) ;
sta_tsk(TSK_ID1,TTS_SUSPEND);
sta_tsk(TSK_ID2,TTS_SUSPEND);
user_initialize();
/* loop */
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 ;
}
}
}
目次
前
次