3.高度なライントレース2 TOPへ 5.マルチタスク

4.OILファイルについて


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


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

下に示したのは、0章で用いたサンプルプログラムの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オブジェクト(OS,APPMODE,TASKなど)の集合で構成されています。
OILオブジェクトは、種類、名前の順で定義します。例えば、"CPU"がオブジェクトの種類、"ATMEL_AT91SAM7S256"がCPUオブジェクトの名前です。
上の例で用いているOILオブジェクトを解説します。

オブジェクト名 説明
CPU CPUはすべてのオブジェクトのコンテナとして使われます。
OS OSプロパティを定義するのに使用されるオブジェクトです。
APPMODE アプリケーションモードを定義します。一つ以上のアプリケーションモードが必要です。
TASK タスクの実行の仕方を指定するオブジェクトです。

OILファイルのコードは実装部とアプリケーション部の大きく二つに分けられます。
OSオブジェクトは、標準的な実装仕様を示す定義を記述した実装部、APPMODE,TASKオブジェクトは、アプリケーションの構造を定義したアプリケーション部です。

実装部は、ハードウェアによって決まるので、NXTを扱う以上、コードは変わりません。
アプリケーション部のTASKオブジェクトの記述を変えることによって、プログラムの実行の仕方を変えることができます。
TASKオブジェクト内の記述を次のように変えてみてください。

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

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

TASK Task1{}
"Task1"という名前のタスクを定義します。


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


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


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


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


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


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



2.カウンタとアラーム

アプリケーション部には、APPMODEやTASKのほかにも、オブジェクトを記述することが出来ます。
今回は、COUNTERオブジェクトと、ALARMオブジェクトを紹介します。
次のプログラムを実行してください。
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();			/* 処理終了 */
}

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{}; 		/* アプリケーションモードを定義 */

  TASK Task1			/* Task1 を定義 */
  {
    AUTOSTART = FALSE;
    PRIORITY = 1;
    ACTIVATION = 1;
    SCHEDULE = FULL;
    STACKSIZE = 512;
  };


  COUNTER SysTimerCnt		/* SysTimerCntを定義 */
  {
    MAXALLOWEDVALUE = 10000;
    TICKSPERBASE = 1;		/* 1ティックは 1msec */ 
    MINCYCLE = 1;
  };


  ALARM cyclic_alarm1		/* 周期起動アラームを定義 */
  {
    COUNTER = SysTimerCnt;
    ACTION = ACTIVATETASK
    {
      TASK = Task1;
    };
    AUTOSTART = TRUE
    {
      ALARMTIME = 1;
      CYCLETIME = 1000;		/* Task1は1秒ごとに起動 */
      APPMODE = appmode1;
    };
  };
};

プログラムでは、変数iを繰り返しインクリメントし、表示させています。
しかし、ソースコードにはwhile文やfor文などのループ処理は記述されていません。
また、systick_waitを記述していないにもかかわらず、iの値はゆっくりと増えていきます。

この動作は、カウンタとアラームを用いることにより、実現されています。
OILファイルを見ると、COUNTERオブジェクトと、ALARMオブジェクトが追加されています。
COUNTERオブジェクトは、プログラム実行時間をカウントするオブジェクトです。
ALARMオブジェクトは、カウンタに貼り付けて用い、様々な動作をさせることが出来るオブジェクトです。
COUNTERオブジェクトと、ALARMオブジェクト内の記述について詳しく説明します。

・ COUNTER

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


TICKSPERBASE = 1;
カウンタが1単位に達するまでのティックを指定します。
"1"を指定した場合、1ティック毎に一つカウントアップします。
※ティックとは、NXTOSEKの場合、1ms毎に1づつ増えていく数値です。


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



・ ALARM

COUNTER = SysTimerCnt;
貼り付けるカウンタを指定します。


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以下の値を指定します。
上のプログラムの場合、1秒毎にTASK1が起動されます。


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




3.課題

1.サンプルプログラムをNXTにダウンロードし、動作を確認せよ。
2.サンプルプログラムを応用し、タッチセンサでスタート・ストップするストップウォッチを作成せよ。ストップウォッチの時間分解能は10msecとする。



3.高度なライントレース2 TOPへ 5.マルチタスク