6.分割プログラミング TOPへ 8.マルチタスク

7.OILファイルと周期実行タスク


いままでずっと同じ内容のものを使ってきたOILファイルですが、このファイルを変更することで、いろいろなことが出来るようになります。
"OIL"とは、"OSEK Implementation Language"の略です。
この章では、OILファイルの記述ついて学習し、プログラム(タスク)の動かし方を変えてみましょう。


1.OILファイルとオブジェクト

下に示したのは、これまで用いてきたプログラムのOILファイル test.oil です。
これを元に、OILファイルの解説をします。

test.oil
#include "implementation.oil"

CPU ATMEL_AT91SAM7S256
{
  OS LEJOS_OSEK
  {
    STATUS = EXTENDED;
    STARTUPHOOK = FALSE;
    SHUTDOWNHOOK = FALSE;
    PRETASKHOOK = FALSE;
    POSTTASKHOOK = FALSE;
    USEGETSERVICEID = FALSE;
    USEPARAMETERACCESS = FALSE;
    USERESSCHEDULER = FALSE;
  };

  /* Definition of application mode */
  APPMODE appmode1{}; 

   TASK Task1						/* Task1 を定義する */
  {
    AUTOSTART = TRUE { APPMODE = appmode1; };
    PRIORITY = 1;
    ACTIVATION = 1;
    SCHEDULE = FULL;
    STACKSIZE = 512;
  };
};

OIL記述は、OILオブジェクト(CPU, OS, APPMODE, TASKなど)の集合で構成されています。
OILオブジェクトは、種類、名前の順で定義します。例えば、"CPU"がオブジェクトの種類、"ATMEL_AT91SAM7S256"がCPUオブジェクトの名前です。
上の例で用いているOILオブジェクトを解説します。

オブジェクト名 説明
CPU CPUはすべてのオブジェクトのコンテナ(入れ物)として使われます。
OS OSプロパティを定義するのに使用されるオブジェクトです。 OSオブジェクトは nxtOSEK を扱うかぎりコードは変わりません。
APPMODE アプリケーションモードを定義するオブジェクトです。アプリケーションモードを定義し、それをTASKに割り当てます。
TASK タスクの実行の仕方を指定するオブジェクトです。

TASKオブジェクトの記述を変えることによって、プログラムの実行の仕方を変えることができます。
TASKオブジェクト内の記述を次のように変えてみてください。

   TASK Task1
  {
    AUTOSTART = FALSE;
    PRIORITY = 1;
    ACTIVATION = 1;
    SCHEDULE = FULL;
    STACKSIZE = 512;
  };
};

NXTにプログラムを転送し、実行してみると、画面にはなにも表示されません。
これは、nxtOSEKが起動したものの、タスクが何も実行されていないからです。
"AUTOSTART"とは、プログラムを実行したときに、自動的にタスクを実行させる機能です。
これを"FALSE"すなわち"偽"にしたことで、タスクが実行されず何の動作しなかった訳です。
TASKオブジェクト内の記述について、下に詳しく記します。

TASK Task1{}
"Task1"という名前のタスクを定義します。この名前はプログラムの Task 関数の引数で与えるTask名と対応します。

AUTOSTART = TRUE or FALSE
プログラム実行時に、タスクを自動起動するかを指定します。
TRUEとする場合、続けて{}内にアプリケーションモードを指定します。
例:AUTOSTART = TRUE { APPMODE = appmode1; };

PRIORITY = 1
タスク優先度を指定します。
タスクが複数あるプログラムの場合、優先度が高いプログラムが優先して実行されます。
数字が大きいほど優先度が高く、範囲は1〜16までです。

ACTIVATION = 1
多重起動の最大数を指定します。
"1"は多重起動なしの設定です。(拡張タスクは多重起動できません。)

SCHEDULE = FULL or NON;
ノンプリエンプティブ"NON"もしくはフルプリエンプティブ"FULL"を設定します。
ノンプリエンプティブは、現在実行しているタスクが優先される方式です。
フルプリエンプティブは、優先度の高いタスクが優先される方式です。。

STACKSIZE = 512;
スタックのサイズをバイト数で指定します。

TASKオブジェクトの記述は、タスクが複数あるプログラムの場合、大変重要になります。
これは、実践編:マルチタスクにて解説します。



2.タスクの周期実行のためのオブジェクト

OILファイルに COUNTERオブジェクトALARMオブジェクトを定義することにより、タスクを周期実行させるプログラムについて解説します。
まず、このときに使用する OIL ファイル alarm_test.oil について説明します。
alarm_test.oil
#include "implementation.oil"

CPU ATMEL_AT91SAM7S256
{
  OS LEJOS_OSEK
  {
     STATUS = EXTENDED;
     STARTUPHOOK = FALSE;
     SHUTDOWNHOOK = FALSE;
     PRETASKHOOK = FALSE;
     POSTTASKHOOK = FALSE;
     USEGETSERVICEID = FALSE;
     USEPARAMETERACCESS = FALSE;
     USERESSCHEDULER = FALSE;
  };


  APPMODE appmode1{}; 		/* APPMODEオブジェクト appmode1 を定義 */

  TASK Task1			/* TASKオブジェクト Task1 を定義 */
  {
     AUTOSTART = FALSE;
     PRIORITY = 1;
     ACTIVATION = 1;
     SCHEDULE = FULL;
     STACKSIZE = 512;
  };


  COUNTER SysTimerCnt		/* COUNTERオブジェクト SysTimerCntを定義 */
  {
     MAXALLOWEDVALUE = 10000;
     TICKSPERBASE = 1;		/* ベースクロック(=1msec)毎のカウント数 */ 
     
     MINCYCLE = 1;
  };


  ALARM cyclic_alarm1		/* アラームオブジェクト cyclic_alarm1 を定義 */
  {
     COUNTER = SysTimerCnt;
     ACTION = ACTIVATETASK
     {
        TASK = Task1;
     };
     AUTOSTART = TRUE
     {
        ALARMTIME = 1;
        CYCLETIME = 1000;		/* カウントk数がこの値に達したら割込みを発生させる */
        APPMODE = appmode1;
     };
  };

};

オブジェクト名 説明
COUNTER COUNTERオブジェクトは、プログラムの実行時間をカウントするオブジェクトです。
ALARM ALARMオブジェクトは、カウンタに貼り付けて用い、様々な動作をさせることが出来るオブジェクトです。

COUNTERオブジェクトと、ALARMオブジェクト内の記述について以下に詳細を説明します。

・ COUNTER

MAXALLOWEDVALUE = 10000;
カウンタの最大値を指定します。"0"を指定することは出来ません。

TICKSPERBASE = 1;
タイマ割込み(=ベースクロック)当たりのカウンタのカウントアップ数を指定します。nxtOSEKの場合タイマ割り込みは1msとなっています。
一般にティック(tick)とはタイマ割込に対応するイベントを意味し、ここではそのカウントアップ数を意味します。
したがって、この値に"5"を指定した場合は、カウンタは1msec毎に5づつカウントアップします。

MINCYCLE = 1;
周期としての最小単位を指定します。
TICKSPERBASEより大きく、MAXALLOWEDVALUE以下の値を指定します。


・ ALARM

COUNTER = SysTimerCnt;
この ALRAM で使用するカウンタを指定します。

ACTION = ACTIVATETASK{ … }
アラームがexpire、すなわち満期になった際の動作を指定します。
今回は、ACTIVATETASK、すなわちタスクを起動する動作を指定しています。
{}内に、起動したいタスク名を指定します。
動作の種類には他に、SETEVENT、ALARMCALLBACKなどがあります。
詳しくは、「TOPPERS_OSEKカーネルSG取扱説書」の「6.7.4. ALARMオブジェクト」を参照してください。
「TOPPERS_OSEKカーネルSG取扱説書」は、nxtOSEK\toppers_osek\docにあります。

TASK = Task1;
上のACTIVATETASKの{}内において、タスクを指定するときに使う記述です。
今回は、アラームによってTASK1を起動します。


AUTOSTART = TRUE{ … }
プログラム起動時にアラームを自動的に起動させるかを指定します。
させる場合は"TRUE"とします。{}内にサブ属性を記載します。

ALARMTIME = 1;
アラームが最初に expire する(=アラームをあげる)カウンタの値です。
0以上、MAXALLOWEDVALUE以下の値を指定します。
"1"とした場合、プログラム開始直後にカウンタがexpireします。

CYCLETIME = 1000;
アラームがexpireする(=アラームをあげる)周期を指定します。
MAXALLOWEDVALUE以下の値を指定します。
このOILファイルの場合、1ms x 1000 で1秒毎にTASK1を起動させることになります。

APPMODE = appmode1;
タスクを起動させる際のアプリケーションモードを指定します。
APPMODEオブジェクトで定義したアプリケーションモードを指定します。

次に、この COUNTER オブジェクトと ALRAM オブジェクトを利用するアプリケーションプログラムについて説明します。

alarm_test.c
#include "kernel.h"
#include "kernel_id.h"
#include "ecrobot_interface.h"

DeclareCounter(SysTimerCnt);		/* SysTimerCntを宣言 */
DeclareTask(Task1);			/* Task1を宣言 */

void ecrobot_device_initialize(){}	/* OSEK起動時の処理(何もしない)*/

void ecrobot_device_terminate(){}	/* OSEK終了時の処理(何もしない)*/

void user_1ms_isr_type2(void)		/* タイマ割り込み用フック関数 */
{
	SignalCounter(SysTimerCnt);	/* カウンタをIncrementする */
}


TASK(Task1)
{

	static int i=0;				/* 変数iを定義と初期化 */

	display_clear(0);
	display_goto_xy(0,1);
   	display_string("I=");		/* 変数の内容を表示 */
	display_int(i, 5);
	display_update();

	i++;				/* iをインクリメントする */

	TerminateTask();			/* 処理終了 */

}

OILファイルで定義したCOUNTERオブジェクト SysTimerCnt を使うためには、上記のように DeclareCounter 関数による 宣言と、タイマ割込み用フック関数内の SiglalCounter 関数によりこのカウンタを利用する設定が必要です。

上記のプログラムは、上記のoilファイル(alarm_test.oil)と組み合わせて、1秒毎に 変数 i を繰り返しインクリメントして表示さるものですが、 その動作を、while文のループ処理とsystick_wait(1000) によって行うのではなく、 OILファイルに追加したCOUNTERオブジェクトとALARMオブジェクトにより実現しています。

なお、ここで説明したタスクの実行周期は、while() と systick_wait_ms()関数を用いたこれまでのプログラムと異なり、ループ内の処理時間に依存せず、OILファイルで指定した実行周期に厳密に一致しています。すなわち、指定した時間内に処理を行うことを保証する(ハード)リアルタイム性を実現しています。OSEKはもともとエンジンやブレーキの制御を含む自動車の制御のプログラム仕様ですので、このようなリアルタイム性を保証する仕組みが標準で提供されています。


3.課題

1.サンプルプログラム (alam_test.c, alarm_test.oil) を用いて作成した実行ファイルを、NXTにダウンロードし、その動作を確認せよ。
 注意点 2.「基礎編 8.超音波センサ」のサンプルプログラム sonar.c の 100msec 周期の超音波計測を、このページの解説にあるのと同様に COUNTERオブジェクトと、ALARMオブジェクトを使って行うように変更し、その動作を確認せよ。



6.分割プログラミング TOPへ 8.マルチタスク