目次

利用例

 RTOSに限らず、OSやモニタを使う場合には、利用例があると
 使い方を理解しやすいものです。

 自分の場合、はじめてμITRONのシステムコール説明を
 読んだとき、どう利用するかイメージできませんでした。

 そこで、それまで作ったファームウエアを、RTOSを利用して
 書き換えたところ、システムコールの使い方がわかりました。

 ここでは例をあげて、USOシステムコールを説明します。


システム仕様

 次の機能をもつ装置を実現します。   周囲が暗くなると、LEDを点滅する。   周囲が明るいときは、LEDを消灯する。  上のような機能をもった防犯装置があります。  RTOSの使い方を知りたいときは、このように身近な装置の動作を  システムコールを利用して記述してみるとよいでしょう。  身近な装置の動作を、RTOSを利用して作成する場合は  次の手順に従ってみると、案外簡単に記述できます。
  1. 機能を洗い出します。
  2. ひとつの機能を、ひとつのタスクに割り当てします。
  3. タスクを、システムコールを利用して記述します。
  4. 起動時のタスク状態を、定義します。
  5. プログラム全体をまとめます。
 この手順に従うと、機械的にファームウエアを作成できます。  では、この装置の動作を、ファームウエアとして作成します。

機能分割

 機能を分割するには、要求仕様を見直します。   周囲が暗くなると、LEDを点滅する。   周囲が明るいときは、LEDを消灯する。  このシステムでは、最低3つの機能が必要になります。
  1. 周囲の明暗を判断しています。
  2. LEDの点滅を制御しています。
  3. LEDの消灯を扱っています。
 タスクを分割できたので、タスクを実現する関数を割り当てます。

タスク割当て

 タスクを実現する関数を、tsk0_proc、tsk1_proc等に割り当てます。
  1. 周囲の明暗を判断しています -> tsk0_proc
  2. LEDの点滅を制御しています -> tsk1_proc
  3. LEDの消灯を扱っています -> tsk2_proc
 タスクを実現する関数を決めたので、内部を作成します。

タスク記述

 タスクは、面倒なものから記述します。
  1. 周囲の明暗を判断しています -> tsk0_proc
  2. LEDの点滅を制御しています -> tsk1_proc
  3. 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);

プログラム全体

 タスクの定義をしただけでは、動作できないので、以下の  内容を定義します。
  1. I/O(Input and Output)操作
  2. 10msタイマー割込み定義
  3. 内部変数定義と初期化
 ファームウエアを実装するマイコンにより、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 ; } } }
目次

inserted by FC2 system