組込システムを目指したリアルタイムOS入門
μITorn実装のHOSを用いたAKI-H8/3052マイコンによるリアルタイムOS入門
DownLoad |
1.組込システム | |
パーソナルコンピュータ,サーバ(メールサーバ,FTPサーバ,計算サーバ,DNSサーバ,ファイルサーバなどなど)は画面に作業結果を表示したり,紙に印刷したり,コンピュータがコンピュータにサービスをしているコンピュータシステムである。これに対して,自動車や飛行機といった機械,テレビやビデオカメラなどの家電機器,プリンタやFAX,携帯電話などの情報機器においては,コンピュータが機械や機器の動作を支えている。最近の自動車にはCPUが100個以上使われるようになっている。機械や機器内にあって,それらを制御しているコンピュータシステムは組込システムと呼ばれている。
2.リアルタイムシステム | |
3.リアルタイムOS | |
パーソナルコンピュータなどでは,マルチタスクOSが多く用いられている。マルチタスクOSでは複数のタスクを一定のタイムスライス(一定間隔)で切り替えながら実行している。タスクに優先順位をつける場合には,タスク切り替え順を平等にしない工夫をしている。最優先のタスクに対しては作業が終わるまでCPUを独占させるなどの工夫をしている。優先タスクであれば,待ちを行う場合でも,CPUを手放すことはない。
リアルタイムOSはマルチタスクOSの1つである。リアルタイムOSでは,作業(タスク)ごとに優先順位が定められており,忙しい作業が生じた場合は他の作業を遮って,必要な作業にCPUパワーをすべて傾けることができるようになっている。このようにすることで,ある刺激に対して,素早く対応しなければならない作業がいつ終了するのかがわかるようになる。また優先順位の高いタスクにおいても作業中に何かの出来事を待つ場合にはCPUを明け渡し,他の忙しい仕事をさせ,待つ理由がなくなったら作業を再開するといったこともできる。
いずれにしてもタスク切り替えにはメモリや時間を余計に必要になる。それ以上のメリットが感じられる場合にリアルタイムOSを用いればよい。
各タスクはタイムスライス(一定間隔)で
切り替わる。優先順位を最高にして,CPU
を独占させることもできる。Fig. 3.1 マルチタスクOS
Fig. 3.2 リアルタイムOS
4.μITRON | |
TRONは「The Real-time Operating system
Nucleus」の略で,1984年に東京大学坂村健教授が立ち上げたプロジェクトのコンピュータアーキテクチャ名である。 ITRONはその組込部門がITRONである。ITRONはすでに多くの組込システムに採用されている。(TRON協会のまとめ)
μITRONはマイクロコンピュータ向けの簡略版である。μITRONは仕様のみ与えており,実装はそれぞれの作り手にゆだねられている。
μITRONの普及はT-Engineフォーラムが主体となって活動している。
T-Engineフォーラム公式ページ http://www.t-engine.org/ja/
商品としてのμITRONの実装も発売されているが,無料で公開されている実装としては名古屋大学高田広章教授のTOPPERSとm-arai氏らのHOS(Hyper
Operating System)がよく知られている。
TOPPERS公式ページ http://www.toppers.jp/index.html
HOSプロジェクト http://sourceforge.jp/projects/hos/
現在では,実装についても規格化したT-Kernel規格と実際のマイクロコンピュータに実装するT-Engineプロジェクトも進んでいる。
「μitron4.0仕様」は本ページで使用されているシステムコールに関するマニュアルでもある。「itron4.0仕様」で検索すると,「http://www.t-engine.org/ja/specifications」のところにpdfが見つかるので,ダウンロードして使いなさい。(Dec2011現在,μITRON 4.0仕様 Ver. 4.03.03が最新である)
5.HOS | |
HOS(Hyper Operating
System)はルネサスのH8マイクロコンピュータやSHマイクロコンピュータ向けに開発されたμITRONの実装OSである。バージョンを細かく見るとHyper
Operating System V4(HOS-V4)は μITRON4.0
仕様の組み込み向けリアルタイムOSとなっている。HOSはH8やSH以外のマイクロコンピュータもサポートするようになっている。
開発にはGCCおよび日立の評価版コンパイラ(現在H8マイコンは株式会社ルネサステクノロジが製造販売しているが,以前は日立が製造販売していた)が使用できる。
(TOPPERSのH8への実装は16MbyteRAMが想定されているため,本Webページでは扱っていない)
(H8/300Hシリーズでは4kbytesのRAMの製品が多いが,本Webページでは8kBytes以上を想定し,最低でもH8/3052の使用を想定している)
6.プロジェクト | |
使用マイコンは,ルネサスのH8マイコンを想定している。当初H8/3048(内蔵RAM:4kbyte,内蔵ROM:256kbyte,16MHz)のモード7を想定していたが,RAM容量不足で演習できないことが分かり,H8/3052(内蔵RAM:8kbyte,内蔵ROM:512kbyte,25MHz))のモード7など内蔵RAM8kbyte以上のCPUを想定する。
本解説では,AKI-H8/3052とマザーボードAE-HMBを使用する。
開発には,GCCおよび日立の評価版コンパイラV2を想定する。
なお,スタートアップルーチン,リンカスクリプト,MakeスクリプトなどはHOSプロジェクト提供のものそのままか,または一部チューニングしたものを使用している。
1つのプロジェクトはHOSを使って1つの実行例を実現するために,1つのフォルダで構成している。ソースファイルの名前はsample.cに固定し,通常変更を要するのはsample.hとsystem.cfg(テキストファイルなのでエディタで編集可能)である。コンパイル&リンク作業はmake(UNIXの世界では有名)を使用する。コンパイル作業は,マイコンを書き込みモードにして,実行プログラム受け入れ可能状態にして,doubleClickAndGo.cmdをダブルクリックすることで可能である。
(演習用PCはコンパイラおよびHOSシステムがインストールされているが,本ページ下部のコンパイラシステムが使われている)
(説明ではhitachコンパイラV2を使うことを想定しているが,GCCでもまったく同じである)
マザーボード(改造AE-H8MB)上のAKI-H8/3052 | マザーボード(TNCTマザーボード)上のAKI-H8/3052 |
6.1 演習の方法(サンプルプロジェクト)
組込システムのプログラミングは,PC上でプログラミングを行い,クロスコンパイラでコンパイルし,実行形式のプログラムを生成して,マイコンのROM領域(実際はフラッシュメモリ)に書き込み,実行させる。この時,PCと通信を行い,ターミナルソフト(Windowsの場合はハイパーターミナルなど)で,実行の様子をフォローするという手順となる。
最初のプロジェクトは「h83052_00_firstsample」を使う。作業手順は次のようである。
(1)マイコンの電源がONになっていたらOFFにする。
(2)マイコンのモード切り替えスイッチを「書き込みモード」にする。
(書き込みモードは,Writeモード,bootモード,programモードとも呼ばれる)
(3)マイコンの電源をONにする。
(電源がONになる瞬間にモード切り替えスイッチが読み込まれる)
(4)PCの画面でdoubleClickAndGo.cmdをダブルクリックする。
(コンパイルから実行プログラムのマイコンへの転送まで行われる。)
(不具合が起こると途中で止まってしまう。)
(5)マイコンの電源をOFF
(6)マイコンのモード切り替えスイッチを「実行モード」にする。
(実行モードは,RUNモード,通常モードなどとも呼ばれる)
(7)PCでto_H8.htをダブルクリックしてハイパーターミナルを起動
(8)マイコンの電源ON
(この後,ハイパーターミナル上に実行結果が現れる)
ハイパーターミナル利用上の注意
(1)PCとマイコンをつなげているシリアル通信は1本しかなく,
これを,実行プログラムを書き込むプログラムとハイパーターミナルアプリが
共用している。よって,コンパイル時には(実際にはプログラム書き込み時)
ハイパーターミナルを切断状態にしておく必要がある。
ハイパーターミナルの接続/切断の切り替えは,上段のメニューアイコンで出来る。
自分で確かめておくこと。
(2)大量の実行結果を残す必要がある場合は,画面のコピー&ペーストではなく,
転送メニュー中のテキストのキャプチャを利用するとよい。
テキストのキャプチャとは,画面に表示される内容を,そのままテキストファイルに
保存することである。転送メニュー中のテキストのキャプチャを選ぶと,
保存対象のファイル名を設定できるようになっている。
既存のテキストファイルを選ぶと,追記になるので注意
このフォルダの内容は次のようになっている。
3052f.h H8/3052のIO定義ファイル
crt0.src スタートアップルーチン(アセンブリ言語ソース)
vector.src 割り込みベクタ(アセンブリ言語ソース)
MinSys3052.h h8/3052共通関数のプロトタイプ宣言
MinSys3052.c h8/3052共通関数
sample.h ユーザプログラムのヘッダ
sample.c ユーザプログラム
system.cfg ユーザが定義する構成ファイル
doubleClickAndGo.cmd コンパイル&リンク&コンバート&書き込みコマンド
link.sub リンクスクリプト
akih8.mak make定義ファイル
to_H8.ht ハイパーターミナルの設定ファイル(ダブルクリックでハイパーターミナルが起動)
6.2 新しいプロジェクト
新しいプロジェクトを始めるには,すでにあるプロジェクトのフォルダを丸ごとコピーして,sample.c,sample.h,system.cfgを書き換えればよい。
ただし,「void
preStartHos()」と「void OnHosStart(VP_INT
exinf)」の名前は書き換えないことを奨励する。
もし,ファイル名をsampleから別のものに変更する場合や,複数ソースファイルにする場合は,akih8.makの該当箇所を変更する必要がある。(makeの動作を理解してから行うこと)
6.3 HOSの起動時に起こっていること
(スタートアップルーチンの仕組みと割り込みベクタ0の役割を学んでいたら読むこと)
OSを含まないシステムとHOSを含むシステムの違いを比べてみる。
スタートアップルーチンは,マイコン起動時の動作を記述している。
(1)割り込みベクタ0
割り込みベクタ0はマイコン起動時に実行すべきアドレス,すなわちスタート
アップルーチンの先頭を指しており,これに従って,スタートアップルーチン
が起動する。
ここまではどのようなシステムでも同じである。
(2)スタートアップルーチン
(2.1)OSを含まないシステムのスタートアップルーチン
OSを含まないシステムにおいては,スタートアップルーチンは次のような構成
になっている。
1)スタックポインタの設定
2)初期値を持つstatic変数(RAM領域)に初期値(ROM領域)をコピー
3)初期値を持たないstatic変数(RAM領域)に初期値0を書き込む
4)main()を呼び出す
(2.2)HOSを含むシステムの実装例
しかし,リアルタイムOSを含むシステムにおいては,さまざまな実装があるが,
ここで紹介するシステムでは,OS起動前にハードウェア初期化を行い,かつ,
OS起動直後に,システム設計者に1つだけ自由に記述できる関数を与えるように
実装している。
1)スタックポインタの設定
2)初期値を持つstatic変数(RAM領域)に初期値(ROM領域)をコピー
3)初期値を持たないstatic変数(RAM領域)に初期値0を書き込む
4)void
preStartHos()を呼び出す
ここではOS起動前に行うべき作業を行う
5)void
preStartHos()から戻ったら,sta_hos()(HOSのカーネル)を呼び出す。
HOSのカーネルが起動するとスタートアップルーチンへは戻ってこない。
HOSのカーネル起動とともに,初期化関数void
OnHosStart(VP_INT
exinf)
などが起動する。
このようにHOSなどのμITRONはライブラリ関数群として与えられるため,
その関数を呼び出すことによりμITRONの機能を利用できるようになっている
7.最初のプログラム [h83052_00_firstsample] | |
これは約1秒ごとに,シリアル通信でターミナルに何かを表示するプログラムである。
プログラムは,List 7.1 sample.cおよび,List
7.2 system.cfgのようになっている。
このプログラムの要点はList 7.1 sample.cの関数void printTime(VP_INT
exinf)中のwhile文
で構成されている無限ループである。
この無限ループ中にdly_tsk(1000)というのがあるが,これは通常の関数呼び出しではなく,
システムコールと呼ばれるもので,意味はdelay_taskすなわち,1000msecの間,CPUを返上し,
1000msec経過したら再び,作業の続きをやりますという意味である。しかもCPUを返上するの
で,1000msec待機している間に,他の作業をしてもよいという意味を含んでいる。他の作業を
斡旋するのはリアルタイムOSのカーネルの仕事である。
もし,dly_tsk()の代わりに,無意味な繰り返しを行う関数を使用しても,同様に1000msecの
待ちを行うことができるが,その場合は,他の作業を行うことはできない。(Fig.
7.1,7.2参照)
:
dly_tsk(1000);
:void msecwait(int msec)
{
int i,j;
for (i=... ) {
}
}:
masewait(000)
:Fig. 7.1 OSシステムコールのdly_tskの呼び出し
このタスクが待っている間,他の作業をOSが行わ
せることもできる。Fig. 7.2 無駄なループ作業での時間つぶしの待ち
自分のタスク内の作業なので,OSに制御を戻さないため,
OSは他の作業を行わせることができない
プログラムの実体は,コンフィグレーションファイルsystem.cfgとプログラム本体であるsample.cより出来ている。
μITRONはメモリ資源の小さなマイクロコンピュータを対象にしているので,μITRONの機能のうち必要なものを組
み込んで,ROMイメージを作るようになっている。必要な機能を定義しているのが,コンフィグレーションファイル
system.cfgである。
List 7.1 system.cfg INCLUDE("\"sample.h\""); /* ←←← ここにユーザのヘッダファイルを記述 */
INCLUDE("\"MinSys3052.h\"");
/* HOS 独自の設定 */
HOS_KERNEL_HEAP(0); /* カーネルヒープの設定(省略時 0) */
HOS_TIM_TIC(1, 1); /* タイムティックの設定(省略時 1/1 ) */
HOS_MAX_TPRI(8); /* 最大優先順位(省略時 16) */
HOS_MIN_INTNO(0); /* 割り込み番号の最小値(省略時 0) */
HOS_MAX_INTNO(256); /* 割り込み番号の最大値(省略時 0) */
HOS_MAX_TSKID(8); /* 最大タスクID番号(省略時静的生成に必要なだけ) */
/* SystemTimer */
ATT_INI({TA_HLNG, 0, initSystemTimer}); /*初期化関数静的起動*/
ATT_ISR({TA_HLNG, 0, 24, SystemTimerHandler}); /*割り込みハンドラの登録*//* SCI */
ATT_ISR({TA_HLNG, 0, 57, SCI1_RXI_handler}); /*割り込みハンドラの登録*/
ATT_ISR({TA_HLNG, 0, 56, SCI1_ERI_handler}); /*割り込みハンドラの登録*//* idle タスク*/
CRE_TSK(tskID_Z, {TA_HLNG | TA_ACT, 1, idletask, 8, 128, NULL});/* サンプルプログラム */
ATT_INI({TA_HLNG, 0, OnHosStart}); /*初期化関数静的起動*/
CRE_TSK(TSKID_0, {TA_HLNG, 1, printTime, 1, 256, NULL}); /*静的生成*/List 7.2 sample.c #include "kernel.h"
#include "kernel_id.h"
#include "MinSys3052.h"
#include "sample.h"
/* スタートアップルーチンから最初に呼び出される初期化関数 */
/* OS起動前にすること(SCIの初期化など)を行っておく */
/* この関数からスタートアップルーチンに戻った後に, */
/* スタートアップルーチンはHOSを呼び出す */
void preStartHos()
{
initSCI1(); /* SCIの初期化 */
SCI1_printf("\npreStartHos: start!\n"); /* 開始メッセージ */
initLed(); /* LEDの初期化 */
initPushSW(); /* PushSWの初期化*/
init8BitSW(); /* 8bitSWの初期化 */
}/* HOS下で最初に起動するハンドラ*/
/* system.cfg中で静的に起動される */
void OnHosStart(VP_INT exinf)
{
putStringSCI1("OnHosStart: starting!\n");
act_tsk(TSKID_0);
}/* 1msecごとに表示するタスク */
/* system.cfg中で静的に生成され,OnHosStart()中で起動されている */
void printTime(VP_INT exinf)
{
SYSTIM time;while (1) { /*永久ループ*/
/* タイマ値取得 */
get_tim(&time);
/* タイマ値出力 */
SCI1_printf("printTime:%8ld\n",(unsigned long int)time.ltime);
/* 1000msec待つ(CPU利用権を一時的にOSへ返し,時間になったら戻してもらう) */
dly_tsk(1000);
}
}Snap 7.1 terminal display preStartHos: start!
OnHosStart: starting!
printTime: 1
printTime: 1006
printTime: 2010
printTime: 3014
printTime: 4018
printTime: 5022
実際の動作を細かく見ると,次のFig.
7.3のようになっている。
crt0.src中のスタートアップルーチンは変数領域の初期化ののち,関数preStartHos()を呼び出す。
関数preStartHos()ではマイコンのハードウェアの初期化を行う。異なるマイコンシステムにおいて
は異なる初期化をするため,このようにユーザから見える関数数preStartHos()を呼び出して,その
中でハードウェアの初期化をするようになっている。
次にsta_hos()を呼び出してHOSのカーネルを起動する。
カーネルが起動するとカーネルは起動直後にOnHosStart()を呼び出す。
これはsystem.cfgの「ATT_INI({TA_HLNG,
0,
OnHosStart});」で生ずることになっている,起動直
後の呼び出しである。
OnHosStart()はタスクprintTimeを起動している。このタスクは永久ループであるが,この中で
システムコールdly_tsk()を呼び出し,1000msecの間HOSにCPUを返し,このタスクは待ちに入る。
HOSカーネルは,返されたCPUを有効に使うよう努力するが,idletask()が実行を待っていたので
idletaskにCPU使用権を与える。idletaskはsystem.cfg中で
CRE_TSK(tskID_Z,
{TA_HLNG | TA_ACT, 1, idletask, 8, 128,
NULL});
によって生成され,すぐに動けるようになっている。(参考3参照)
idletaskの優先順位は8である。
(重要)優先順位は番号が小さいほど高いことを示している。
1000msec経過したらHOSカーネルはidletaskからCPU使用権を奪い返し,タスクprintTimeに与える。
以下繰り返しとなる。
タスクprintTimeは起動後はrunningであるかまたはwaitingになる。
タスクidletaskは起動後はreadyであるかまたはrunningになる。Fig. 7.3 タスク実行の様子
参考1 sample.cの先頭部分にある#includeについて
kernel.h μITRON自身の定義ファイル,HOSシステムのhos_v4/includeフォルダ内にある kernel_id.h system.cfgから生成される MinSys3052.h Minsys3052.cで必要な定義ファイル,関数プロトタイプを含んでいる sample.h sample.cの定義ファイル,関数プロトタイプを含んでいる
参考2 作業フォルダ内のファイルについて
HitachiCVer2版 GCC版 内容 sample.c sample.c 題材のCソースファイル sample.h sample.h sample.cの定義ファイル,関数プロトタイプを含んでいる system.cfg system.cfg 題材のコンフィグレーションファイル MinSys3052.c MinSys3052.c 最低限の関数群 MinSys3052.h MinSys3052.h MinSys3052.cの定義ファイル,関数プロトタイプを含んでいる vector.src vector.s 割り込みベクタテーブル 3052f.h 3052f.h H83052のレジスタ定義ファイル crt0.src crt0.s スタートアップルーチン link.sub h83052.x リンクスクリプト,リンカで用いられる akih8.mak gcc.mak make.exeへの生成情報 doubleClickAndGo.cmd doubleClickAndGo.cmd コンパイル&転送を行うwindowsコマンド to_H8.ht to_H8.ht ハイパーターミナル起動
参考3 静的タスク生成定義「CRE_TSK」について
CRE_TSK(tskID_Z, {TA_HLNG | TA_ACT, 1, idletask, 8, 128, NULL});
*1 *2 *3 *4 *5 *6 *7
*1 タスクID ここでtskID_Zはコンフィグレイタによって番号が割り当てられる
*2 タスク属性 ここでTA_HLANG:高級言語で記述されている RA_ACT:生成後即起動
*3 タスクの拡張情報(タスクが起動した時に引数として受け取る値)
*4 タスクの起動アドレス(タスクに対応する関数名)
*5 タスクの起動時優先度(値が大きいほど優先度は低い。)
system.cfgにて優先度を表す値は0-8にしている。
*6 タスクのスタック領域のサイズ(bytes)
*7 タスクのスタック領域の先頭アドレス(NULLにすると,自動割り当て)
課題 7 |
(1)system.cfgに見られるinitSystemTimer,SystemTimerHandler,SCI1_RXI_handler,SCI1_ERI_handler,idletaskは関数の名前である。
関数の定義(関数の実体)は,同じフォルダ内のファイルに書いてある。どこに書いてあるか調べなさい。
(2)タスクの起動(act_tsk())では,タスクの関数名を指定せずに,タスクIDを用いている。
タスクを表す関数名(ここではprintTime)とタスクID(ここではTSKID_0)の関係はどこで定められているのか,まとめなさい。
printTimeとidletaskはタスクを表す関数名であるのでこの2つについて調べなさい。
コンパイル作業時に作成されるkernel_id.hをみるとタスクIDを表す名前(TSKID_0)について#define文で具体的な値が割り当てられているのがわかる。
8.タスクの優先順位と遷移状態 | |
複数のタスクが,作業できる状態にある時,CPUは1つなのでカーネルはどのタスクにCPU使用
権を与えて作業をさせるかを決めている。
その際,考慮するのが,タスクの優先順位である。「7.」のサンプルプログラムでは,タスク
printTimeの優先順位は1でタスクidletaskは優先順位8であった。数値が小さいほど優先順位は上位で
ある。カーネルは優先順位が高い順にタスクに作業を行わせるようになっている。同じ優先順位を持
つタスクの場合には,先に実行準備が整ったタスクから順に,作業させる。
タスクの状態は,Fig.
8.1に示すように4つの状態がある。(μITRONのマニュアルにはもっと多
くの状態が定義されている)
「7.」のサンプルプログラムでは,タスクidletaskはsystem.cfg中で静的にCRE_TSK(create
task)
で作られ,オプションTA_ACTで,ready状態になっている。(オプションTA_ACTがついていなければ,
dormant状態になっている。)
タスクprintTimeはsystem.cfg中で静的にCRE_TSK(create
task)で作られ,dormant状態になってい
るが,関数OnHosStart中でact_tsk(activate
task)でready状態にされる。
ここでカーネルは,2つのタスクがreadyになっているので優先順位の高いタスクprintTimeを実行
し,タスクprintTimeがシステムコールdly_tskでcpu使用権を返してきた時すなわち,タスク
printTimeがwaitingになった時に,readyであるタスクidletaskを実行させる。カーネルは1000msec
経過したところで,タスクprintTimeをwaitingからrunningにするために,idletaskをreadyに戻す。
この作業が繰り返されていた。
ところでreadyからrunningにタスクを移行させることをディスパッチ(dispatch,派遣する,送
り出す)と言い,runningからreadyにタスクを移行させることをプリエンプト(preempt,取って
代わる)という。ディスパッチとプリエンプトはカーネルが自動的に行う作業なので,強制的に
行う命令やシステムコールは存在しない。
Fig. 8.1 タスクの遷移状態(本Webテキストで説明している範囲) |
実行可能状態(ready)になったタスクは,すぐに実行できない場合は,タスクレディキューに登録される。
ただし,優先順位の高いものほどタスクレディ-キューの前の方に登録され,同じ優先順位なら先着順に登録される。
課題 8 |
(1)タスクの優先順位の設定はList
7.1,7.2ではどこで行われているか。調べなさい。
また,マニュアルの該当するシステムコール関数についてまとめなさい。
(2)「7.」のプログラムで,タスクprintTimeの優先順位を8,タスクidletaskの優先順位を7に設定しなさい。
そして,実行してみた結果どうなったかを書きなさい。またどうしてそうなるのかを考察しなさい。
(3)タスクprintTimeとタスクidletaskの遷移状態の変化を説明しなさい。
「7.」のプログラムそのままの場合と,タスクprintTimeの優先順位を8,タスクidletaskの優先順位を7に
設定した場合について説明しなさい。
9.タスクの動的生成 [h83052_01_dynamic] | |
「7.」のプログラムでは,タスクprintTimeはsystem.cfg中で静的に生成されており,OSの起動とともに生成していた。List
9.1では,タスクprintTimeをプログラム実行中に動的に生成する。動的に生成する場合はタスクの使用するメモリを動的に確保するため,カーネルヒープ(カーネルがタスクに動的に割り当てるメモリ)をsystem.cfg中で確保しなければならない。
ここではメモリ確保のための関数malloc,callocなどは明示的には呼び出していないが,内部では使われている。メモリ確保の領域はheapと呼ばれている。system.cfgに,heap領域をどれだけ確保するかの設定がある。HOS_KERNEL_HEAPの設定がこれにあたり,2048と書いてある場合には2048バイトをこの用途のために確保するという意味になる。
List 9.1 system.cfg |
INCLUDE("\"sample.h\""); /* ←←←
ここにユーザのヘッダファイルを記述 */
/* SCI */ /* idle タスク*/
|
List 9.2 sample.c |
#include "kernel.h"
ID tskID1; /*タスクID*/ /* HOS下で最初に起動するハンドラ*/ /* 1msecごとに表示するタスク */ while (1) {
/*永久ループ*/ |
Snap 9.1 terminal display |
preStartHos: start! OnHosStart: starting! printTime: 1 printTime: 1006 printTime: 2010 printTime: 3014 printTime: 4018 printTime: 5022 |
タスクprintTimeは起動後はrunningであるかまたはwaitingになる。 タスクidletaskは起動後はreadyであるかまたはrunningになる。 |
Fig. 9.1 タスク実行の様子 |
課題 9 |
(1)CRE_TSK,cre_tsk,acre_tskの違いをマニュアルで調べ,まとめなさい。
(2)動的なタスク生成(create)では,関数名とタスクIDの関係はどこで定められているのか,説明しなさい。
なお,動的にタスク生成が行われているのはタスクprintTimeである。
ヒント act_tskの時には関数名でなく,タスクIDが用いられている。
(3)カーネルヒープの値を小さくしてゆくとエラーが起きる。どれくらい小さくするとエラーにあるのか,どのようなエラーが起こるのか試して,まとめなさい。
(4)タスクprintTimeとタスクidletaskの遷移状態の変化を説明しなさい。
10.タスクの優先順位設定と起動の例 [h83052_02_priority] | |
タスクの優先順位を変えると,実行の様子が異なることを確認する。
List 10.1 system.cfg |
INCLUDE("\"sample.h\""); /* ←←←
ここにユーザのヘッダファイルを記述 */
/* SCI */ /* idle タスク*/ /* サンプルプログラム */ |
List 10.2 sample.c |
#include "kernel.h"
/* HOS下で最初に起動するハンドラ*/ /* 1msecごとに表示するタスク */ while (1) {
/*永久ループ*/ /*重い作業をするタスクを想定*/ |
Snap 10.1 terminal display |
preStartHos: start! OnHosStart: starting! printTime: 1 HeavyTask: 0 1a80 HeavyTask: 1 3500 HeavyTask: 2 4f80 HeavyTask: 3 6a00 HeavyTask: 4 8480 printTime: 1006 HeavyTask: 5 9f00 HeavyTask: 6 b980 HeavyTask: 7 d400 HeavyTask: 8 ee80 HeavyTask: 9 0900 printTime: 2010 printTime: 3014 printTime: 4018 printTime: 5022 |
このプログラムの実行の様子をFig. 10.1に示す。
タスクprintTimeは起動後はrunningであるかまたはwaitingになる。 タスクheavytaskは起動後はreadyであるかまたはrunningになる。 |
Fig. 10.1 タスク実行の様子 |
課題 10 |
(1)List
10,1でタスクprintTimeとタスクheavetaskの優先順位を逆にしたらどのように動作するか確かめなさい。
また,そのようになった理由をまとめなさい。
(2)act_tsk,sta_tsk,ext_tsk,ter_tsk,dly_tskについてマニュアルを参考にしながらまとめなさい・
11.タスクの優先順位設定と起動の例 [h83052_03_taskpriority] | |
2回続けてact_tskで起動しようとしても,起動要求があったことをバッファにためておくことはできない。
このことは,タスクの優先順位が高いタスクから,低いタスクを起動しようとした時に生ずる。
List 11.1 system.cfg |
INCLUDE("\"sample.h\""); /* ←←←
ここにユーザのヘッダファイルを記述 */
/* SystemTimer */ /* SCI */ /* idle タスク*/
|
List 11.2 sample.c |
#include "kernel.h"
/* HOS下で最初に起動するハンドラ*/ void OnHosStart(VP_INT exinf)
putStringSCI1("TaskA: execuing act_tsk(tskID_B), 1st
time\n"); ext_tsk(); } void TaskB(VP_INT exinf) |
Snap 11.1 terminal display |
preStartHos: start! OnHosStart: starting! TaskA: starting TaskA: execuing act_tsk(tskID_B), 1st time TaskA: execuing act_tsk(tskID_B), 2nd time TaskA: can't start TaskB, 2nd time TaskB: starting 1 |
このプログラムの実行の様子をFig. 11.1に示す。TaskAが終わらないとTaskBは実行を開始できない。
Fig. 11.1 タスク実行の様子 |
課題 11 |
(1)List
11.1でタスクTaskAとタスクTaskBの優先順位を逆にしたらどのように動作するか確かめなさい。
また,そのようになった理由をまとめなさい。
12.タスクの優先順位設定と起動の例(タスクのダイナミック生成) [h83052_04_taskpriority] | |
「11」と同じ動作のタスクをダイナミック生成を行う。
List 12.1 system.cfg |
INCLUDE("\"sample.h\""); /* ←←←
ここにユーザのヘッダファイルを記述 */
/* SystemTimer */ /* SCI */ /* idle タスク*/
|
List 12.2 sample.c |
#include "kernel.h"
/*
↓優先順位 */ /* HOS下で最初に起動するハンドラ*/ void TaskA(VP_INT exinf) putStringSCI1("TaskA: execuing act_tsk(tskID_B), 1st
time\n"); ext_tsk(); } void TaskB(VP_INT exinf) |
Snap 12.1 terminal display |
preStartHos: start! OnHosStart: starting! TaskA: starting TaskA: execuing act_tsk(tskID_B), 1st time TaskA: execuing act_tsk(tskID_B), 2nd time TaskA: can't start TaskB, 2nd time TaskB: starting 1 |
課題 12 |
(1)「8」と「9」の関係と「11」と「12」の関係は,同様であり,静的にタスクを生成(create)するか,
動的にタスクを生成するかの違いである。あるタスクを静的に生成する場合と,動的に生成する場合の要点をまとめ
なさい。
(タスク起動は静的に行うか,動的に行うかどちらかの方法で行う)
(2)List
12.1でタスクTaskAとタスクTaskBの優先順位を逆にしたらどのように動作するか確かめなさい。
また,そのようになった理由をまとめなさい。
13.タスクの優先順位設定とslp_tsk,wup_tskを用いた同期の例 [h83052_05_wup] | |
あるタスクがslp_tsk(sleep task)で自分自身をsleepさせると,他からwup_tsk(wake up
task)で起こされるまで
waitingになる。この機能を用いて同期の機能を実現でき,次のような使い方となる。
List 13.1 slp_tsk,wup_tskを用いた同期 | |
待つ側のタスク |
起床要求するタスク wup_tsk(起床したいタスクID); |
List 13.2 system.cfg |
INCLUDE("\"sample.h\""); /* ←←←
ここにユーザのヘッダファイルを記述 */
/* SCI */ /* idle タスク*/ /* サンプルプログラム */ |
List 13.3 sample.c |
#include "kernel.h"
/* HOS下で最初に起動するハンドラ*/ void taskA(VP_INT exinf) void taskB(VP_INT exinf) |
Snap 13.1 terminal display |
preStartHos: start! OnHosStart: starting! taskB: starting! taskA: starting! taskA: wup_tsk 1st time taskB: waking up! taskA: wup_tsk 2nd time taskB: waking up! |
Fig. 13.1 タスク実行の様子 |
課題 13 |
(1)slp_tsk,wup_tskについてマニュアルで調べてまとめなさい。
(2)taskAとtaskBの優先順位を逆転させ,どのような結果になるか調べなさい。
また,どうしてそのような実行結果になるのかまとめなさい。
(ヒント)wup_tskのシステムコールすなわち起床要求は,複数個,保存される。
14.タスク間通信と同期(イベントフラッグ) [h83052_06_eventflag] | |
slp_tskとwup_tskを用いた同期方式は,「1つの条件で1つの作業が行われる」場合に用いることができる。「複数の条件で1つの作業が行われる」場合を実現するにはイベントフラッグが用いられる。
| ||||||
Fig. 14.1 イベントフラッグによる同期 |
次の例はTaskAとTaskBにTaskCが待たされている例である。
List 14.1 system.cfg |
INCLUDE("\"sample.h\""); /* ←←←
ここにユーザのヘッダファイルを記述 */ /* SystemTimer */ /* SCI */ /* idle タスク*/
|
List 14.2 sample.c |
#include "kernel.h" /*
↓優先順位 */ /* HOS下で最初に起動するハンドラ*/ void TaskA(VP_INT exinf) void TaskB(VP_INT exinf) void TaskC(VP_INT exinf) |
Snap 14.1 terminal display |
preStartHos: start! OnHosStart: starting! OnHosStart: A Pri,id= 5,8 OnHosStart: B Pri,id= 5,7 OnHosStart: C Pri,id= 4,6 OnHosStart: flgid=4 TaskC: starting TaskB: starting TaskB: flgptn=00 TaskB: flgptn=02 TaskA: starting TaskA: flgptn=02 TaskC: flgptn=03 TaskC: flgptn=00 TaskA: flgptn=00 |
課題 14 |
(1)sacre_flg,cre_flg,et_flg,wai_flg,clr_flg,ref_flgについてマニュアルで調べてまとめなさい。
(2)snap 14.1を参考に,このプログラムがどのようにふるまったか,各taskの遷移状態を
説明しなさい。
(3)taskA,taskBとtaskCの優先順位を逆転させ,どのような結果になるか調べなさい。
また,どうしてそのような実行結果になるのかまとめなさい。
(4)List 14.1,14.2ではタスクを動的に生成している。静的生成に変更しなさい。
15.周期ハンドラ [h83052_07_pushSWStatus] | |
あらかじめ設定しておいた周期ごとに,OSによって呼び出される関数は周期ハンドラと呼ばれ
る。周期ハンドラは,何かのタスクを起動してなにかの作業を行うようにし,自分自身はすぐに
作業を終えるようにしておくと,全体に対する負担が少なくなる。
なにかの様子を監視するなどの目的に使われることが多い。
次の例は4つのプッシュスイッチを監視するプログラムである。
List 15.1 system.cfg |
INCLUDE("\"sample.h\""); /* ←←←
ここにユーザのヘッダファイルを記述 */
/* SystemTimer */ /* SCI */ /* idle タスク*/
|
List 15.2 sample.c |
#include "kernel.h"
/* HOS下で最初に起動するハンドラ*/ void CYC_wupCButton(VP_INT exinf) void checkButton(VP_INT exinf) #define B1PUSH 0x03 /*0001 1*/
|
課題 15 |
(1)sta_tskについてマニュアルで調べてまとめなさい。またact_tskとの違いを明確にしなさい。
(2)List
15.1,15.2を元にして次のプログラムを作成しなさい。
プッシュスイッチ1を押して離すと,LED1が1秒周期で3回点滅し,プッシュスイッチ2を押し
て離すと,LED2が2秒周期で2回点滅する。ただし,LED1が与えられた演技動作を続けている
時にプッシュスイッチ1が押されても無視し,LED2が与えられた演技動作を続けている時に,
プッシュスイッチ2が押されても無視する。
(3)List
15.1,15.2を元にして次のプログラムを作成しなさい。
プッシュスイッチ1を押して離すと,LED1が1秒周期で3回点滅し,プッシュスイッチ2を押し
て離すと,LED2が2秒周期で2回点滅する。ただし,LED1が与えられた演技動作を続けている
時にプッシュスイッチ1が押されたら,現在の演技動作を中止し,演技動作を最初から実行す
るうようにする。また,LED2が与えられた演技動作を続けている時に,プッシュスイッチ2が
押されても,現在の演技動作を中止し,演技動作を最初から実行するうようにする。
16.イベントフラッグと周期ハンドラを用いたスロットマシン [h83052_08_slot] | |
3ケタの数字もったスロットマシンを作る,ただし,3つの数字がそろっても何もしない。
3ケタの数字はLCDに表示される。
3ケタの数字をそれぞれ,1つのタスクに割り振る。プッシュボタン1が押されて離されたら,スロッ
トマシンの3ケタの数字が動き出し,プッシュスイッチ2,3,4を押すと,各桁の数字の動作を止める
動作になる。3つの数字のそれぞれを支配するタスクは3個必要であるが,タスク定義は1つであり,別々
なタスクIDを持たせて3つのタスクを起動している。
List 16.1 system.cfg |
INCLUDE("\"sample.h\""); /* ←←←
ここにユーザのヘッダファイルを記述 */
/* SystemTimer */ /* SCI */ /* idle タスク*/
|
List 16.2 sample.c |
#include "kernel.h"
int isBusy=0; /*SCIの使用状況*/ /* HOS下で最初に起動するハンドラ*/ void CYC_wupCButton(VP_INT exinf) void checkButton(VP_INT exinf) #define B1PUSH 0x03 /*0001 1*/
void drumTask(VP_INT exinf) |
課題 16 |
(1)List 16.2では各桁に現れる数値は0から9までの10個であった。これを0,1,2の3個だけにしなさい。
(2)(1)で作成したプログラムをさらに改良し,3つの数字がそろったら,何かのパフォーマンスが実行
されるようにしなさい。
17.セマフォによる資源利用 | |
2つのタスクが同時にSCI(シリアルインターフェイス)で通信がしたくなった場合,2つの通信内容が混信した
内容になってしまう。これを回避するために排他制御の仕組みセマフォを用いる。セマフォはある資源を使おうと
思ったときに,セマフォを得ることにする。セマフォが得られたら,その資源を利用できるが,得られなかったら,
CPU使用権をOSに奪われるため,待ち状態となってしまう。セマフォが得られないとは,他のタスクがセマフォを得
ている状態になっているという状態すなわちその資源を他のタスクが使用中という意味である。他のタスクが資源を
使い終わって,セマフォを返したら,OSはこちらのタスクを再開させることになる。そのためタスク中では,セマフ
ォが獲得できる状態になったら,実行が再開するので,その資源を利用する部分の実行を行うように記述する。
次の例では同じ作業をするのに,セマフォを使った場合と使わない場合を比較できるようにしたものである。
List
17.2中で
#define USE_SEMAPHO
または
/*#define
USE_SEMAPHO*/
にしてセマフォを使った場合と使わない場合実行できる。
List 17.1 system.cfg |
INCLUDE("\"sample.h\""); /* ←←←
ここにユーザのヘッダファイルを記述 */
/* SCI */ /* idle タスク*/ /* サンプルプログラム */ /*セマフォ*/ |
List 17.2 sample.c |
#include "kernel.h"
/* HOS下で最初に起動するハンドラ*/ /*#define USE_SEMAPHO*/ #ifdef USE_SEMAPHO void taskB(VP_INT exinf) #else void taskB(VP_INT exinf) #endif |
Snap 17.1 terminal display (with semapho) |
preStartHos: start! OnHosStart: starting! taskA: starting! taskB: starting! Peter ran straight away to Mr. McGregor's garden, and squeezed under the gate! French beans Peter ran straight away to Mr. McGregor's garden, and squeezed under the gate! French beans Peter ran straight away to Mr. McGregor's garden, and squeezed under the gate! French beans French beans French beans French beans French beans French beans French beans French beans |
Snap 17.2 terminal display (without semapho) |
preStartHos: start! OnHosStart: starting! taskA: starting! taskB: starting! Peter French beans ran stFrench beans raighFrench beans t awayFrench beans to MrFrench beans . McGFrench beans regor'French beans s garFrench beans den, aFrench beans nd sqFrench beans ueezed under the gate! Peter ran straight away to Mr. McGregor's garden, and squeezed under the gate! Peter ran straight away to Mr. McGregor's garden, and squeezed under the gate! |
課題 17 |
(1)CRE_SEM,wai_sem,sig_semについてマニュアルで調べてまとめなさい。
解説 ここで使用しているμITRON実装 | |
μコンピュータ起動後の関数コールの順
(1)スタートアップルーチンの起動
(2)スタートアップルーチンからの,関数void
preStartHos()呼び出し
(3)スタートアップルーチンからの,HOS起動
(4)HOS起動時にsystem.cfg中のATT_INIで登録されていれば,その関数の起動
(5)HOS起動時にsystem.cfg中のATT_INIで登録された関数void
OnHosStart(VP_INT
exinf)の起動
(6)system.cfg中のCRE_TSKで登録された関数のうち,TA_ACTを持つものを起動
(優先度によりただちに起動するか,readyQueueに登録されるだけのどちらか)
(7)system.cfg中の設定を登録
(8)この後はHOSがタスクを管理
参考 Windows PCの準備 | |
Hitach評価版コンパイラ 校内のみ
6Mbyte Hitachコンパイラを使う時に必要
HOSV4 校内のみ
800kbyte Hitachコンパイラを使う時に必要(ライブラリ,コンフィグレータ構築済)
CygwinのGCC 校内のみ 45Mbyte CygwinおよびGCCを使う場合に必要(ライブラリ,コンフィグレータ構築済)
(cygwinは別途入手,cygwin1.dllをsystem32に入れておく)