こんにちは、ナナです。
リアルタイムOSにおける時間管理の仕組みを紹介しましょう。
ここまでにタスクの実行を遅延させるdly_tskや周期ハンドラなど、時間を意識したサービスコールが登場しました。このような時間をどのようにリアルタイムOSは把握しているのでしょうか?
本記事では次の疑問点を解消する内容となっています。
では、OSタイマの仕組みを学んでいきましょう。
OSタイマの役割
組み込み機器で時間を作り出すとは
マイコン入門編のタイマ機能を実施された方は、マイコンにおいて時間を作り出すことは専門的な知識が必要になることを経験されていることでしょう。
マイコンで時間を作り出すためには、マイコンが持つタイマ機能を利用するしかありません。
しかし、組み込み開発用の機器はマイコンが異なることなんて当たり前のことであり、マイコン毎に時間を作りだす仕組みは変化します。
リアルタイムOSといっても時間を作り出すことはできない
リアルタイムOSといっているくらいですから「時間を作ることなんて当たり前にできるでしょ」と思われるかもしれません。
しかし、世の中に無数にある組み込み機器に対して、「ITRONカーネルを動かしたから、時間は自動で作り出してくれるんだよね」なんて夢の機構は存在しません。
機器ごとに異なる時間生成の仕組みを、ITRONカーネル側のプログラムで管理することなどできるわけがないのです。
やはりそこにはリアルタイムOSが時間を管理するための明確なカラクリが存在します。
それがOSタイマです。
OSタイマとは
OSタイマはリアルタイムOSに対して時間を供給するためのタイマ機構のことです。
リアルタイムOSはOSタイマからの時間供給を元にシステム時間を管理します。
対象の組み込み機器に対してリアルタイムOSをポーティングする作業者が、OSタイマの設定をする必要があります。これを忘れるとdly_tskといった時間に関わるサービスコールが一切使えないため注意が必要です。
OSタイマの定義方法
イメージだけ語っていてもなかなか理解できないため、具体的なプログラムからOSタイマを知っていきましょう。
OSタイマの詳細と具体例
皆さんが動かしているビュートローバーのシステムにはすでにこの仕組みが組み込まれています。それがHEWのプロジェクトに登録されているostimer.cです。
ostimer.c
//------------------------------------------------
// 概 要:OSタイマ初期化
//------------------------------------------------
void Ostimer_init(VP_INT exinf)
{
// 1msのオーバーフロー割り込み用の設定
TB1.TMB1.BIT.RLD = 1; // オートリロード機能有効
TB1.TMB1.BIT.CKS = 4; // 分周φ/16を選択
TB1.TCB1 = 256 - 187; // リロードカウント値
// タイマB1オーバーフロー割り込み許可
IENR2.BIT.IENTB1 = 1;
return;
}
//------------------------------------------------
// 概 要:オーバーフロー割り込みハンドラ
// 1ms間隔で割り込みハンドラからコールされる
// OSタイマとしてOSへタイムティックを供給
// 引 数:なし
//------------------------------------------------
void Ostimer_interruptHandler(VP_INT exinf)
{
// 割り込み要求フラグクリア
IRR2.BIT.IRRTB1 = 0;
// 1msのタイムティック供給
isig_tim();
}
Ostimer_init関数の解説
Ostimer_init関数はH8/36064マイコンに搭載されたタイマB1機能の初期化を行っています。この設定により約1ms間隔でタイマオーバーフロー割り込みが入るようになっています。
レジスタの詳細に関してはマイコン入門編のタイマ機能の記事を見てください。1msを作り出す仕組みが解説してあります。
Ostimer_interruptHandler関数の解説
この関数はタイマオーバーフローに対する割り込みハンドラです。
よく見ていただきたいのが、isig_timというサービスコールを呼んでいることです。割り込みハンドラのため「i」が先頭に付いているサービスコールになっていますね。
isig_timサービスコールとは
このisig_timサービスコールこそがITRONカーネルに対して時間を供給するためのものです。ITRONの仕様をみてみましょう。
システム時刻とはITRONが管理するシステムの時間のことです。
この時間の更新はアプリケーションからisig_timによって更新されるとされています。
つまり、時間更新の役目はリアルタイムOSではなく、皆さんの作るアプリケーションにて実現しなさいとされています。
この時間供給のことを「タイムティックの供給」と呼びます。
OSタイマの初期化と割り込みハンドラの登録方法
Ostimer_init関数とOstimer_interruptHandler関数を定義したのはよいですが、関数というのは呼ばれないと動きません。
これらの関数はいつ呼ばれるようになっているのでしょうか?
実はコンフィギュレーションファイルに呼ばれる仕組みが設定されています。コンフィギュレーションファイルが何かわからない方は、こちらの記事を参照してください。
コンフィギュレーションファイルの中身を見ていきましょう。
初期化処理の定義
システムには起動時に初期化したいものってよくあります。グローバル変数だったり、特定のハードウェアのレジスタ値だったり、初期化時にはこうなっていてほしいという要望はあるものです。
ITRONにはコンフィギュレーションファイルで静的APIによる初期化処理が定義できるようになっています。
system.cfg(一部抜粋)
//------------------------------------------------
// 初期化
//------------------------------------------------
ATT_INI({TA_HLNG, 0, Main_init});
ATT_INI({TA_HLNG, 0, Ostimer_init});
ATT_INIで指定された関数はITRONカーネルが起動したタイミングで1度だけ呼ばれる仕組みになっています。ITRON仕様では次のように説明されています。
本例ではmain.cに定義されたMain_init関数とostimer.cに定義されたOstimer_init関数が登録されています。皆さんも初期化処理をしたければ同じように追加することができます。
これがOstimer_init関数が呼ばれる仕組みです。
割り込みハンドラの呼び出し定義
ostmer.cのOstimer_interruptHandler関数の定義はありますが、割り込みハンドラというものをタイマB1のオーバーフロー割り込み発生時に呼ばせる仕組みが必要です。
マイコン入門編ではintprg.cというHEW独自の割り込みハンドラ登録処理の仕組みを使いましたが、HOSには割り込みハンドラを簡単に登録できる仕組みが用意されています。
//------------------------------------------------
// 割り込み
//------------------------------------------------
ATT_ISR({TA_HLNG, 0, 29, Ostimer_interruptHandler});
特徴的なのがintnoとして指定する割り込み番号です。タイマB1のオーバーフローは割り込み番号29に紐づいているためその番号を指定しています。
つまり、皆さんがその他の割り込みハンドラを利用したければ、この割り込み番号をマイコンのマニュアルから調べて同じように登録すればよいのです。
割り込み番号の詳細が知りたい方は、H8/36064のマニュアル「3.1 例外処理要因とベクタアドレス」を見よう!
タイムティック供給値の調整方法
ITRONのシステム時刻はミリ秒という単位が基本になっています。dly_tskといったサービスコールも時間の単位はミリ秒になっていますね。
そのため、OSタイマで供給するisig_timの呼び出し間隔は1ミリ秒になるようにタイマを設定するのがよいです。
しかし、ハードウェアによってはタイマの精度上の問題で3ms周期で割り込みをいれるのが精いっぱいなんてこともあったりします。そんな時は次のHOS独自の静的APIにて時間調整をします。
HOS_TIM_TIC(1, 1); // タイムティックの設定(省略時 1/1 )
左がタイムティック周期の分子、右がタイムティック周期の分母の数字です。
もしも3ms周期の割込みの場合はHOS_TIM_TIC(3, 1); とすることで、3 ÷ 1 = 3msとなり1回の割込みで3msのシステム時刻が加算されるようになります。
3.3333msといった小数点を含むような割り込み周期だった場合はこの時間を作り出す分子と分母の組み合わせを指定します。10 ÷ 3 = 3.33333… のような数字ですね。
この場合はHOS_TIM_TIC(10, 3);と指定することでタイムティックを供給することができます。