Smalight OSを使おう 9タスク目 イベントフラグの使用1 [Smalight OS]
この記事は、某SNSで書いているものを転載しています。
今回は、使うと便利なイベントフラグの説明に入ります。
イベントフラグは、特定の待ちパターン(ビットパターン)を送る側と、受ける側に別れてタスクの同期を取る為に利用します。
受ける側は必ずタスクでなければなりません。送る側はタスクの場合と、OS管理下のタスク以外の状態(簡単に言えばOS管理下の割り込みハンドラ)から可能です。
送る時は、符号無し16bitの中の任意のbitを1にセットします。
例えば0x0001を送る側のサービスコールであるset_flg等の引数とし、bit0をセットします。
この時0x0001を連続で10回セットすれば、相手側も10回起床するか?と言えば答えはNOで、実際にはOS内部のフラグに毎回引数がORされる為、相手が即座に起床してそのビットがセットされた事を認識できれば10回可能ですが、相手が起床する前にこちらが上書きしてしまう可能性も有ります。
つまり相手がちゃんとこのイベントを認識したかどうかの保証は、プログラマが別の手段で確保する必要が有ります。
受ける時は、符号無し16bitの中の任意のビットがセット条件になる事を待ちます。
この時、ビットの待ち方には二つの方法が有ります。
1.複数のビットの内の1ビットでもセットされれば、待ち状態から解除される物:OR条件。
2.複数のビットの全部がセットされるまで、待ち状態を継続する物:AND条件。
※既に該当ビットがセットされていたら、待ち状態に入らずに戻って来る。
この二つの使い分けは、ユーザー初期化関数(uinit)実行時に決定されます。
具体的には「EVTFLG_ATTR」マクロの記述で属性に
EVFLG_TA_OR:OR条件
EVFLG_TA_AND:AND条件
の何れかを記述します。
ついでに他の属性を説明します。
EVFLG_TA_CLR:受信側が待ち状態から解除される時、ビットパターンをOSがクリアする。
※この属性は一見便利に見えますが、複数のタスクが一つのイベントフラグの待ち状態になる場合、注意して使わないと思わぬバグになります。
EVFLG_TA_TFIFO:タスクの待ち状態からの解除を、先着順にする。
EVFLG_TA_TPRI:タスクの待ち状態からの解除を、優先度順にする。
の何れかを記述します。
これら属性の指定方法は、以下の様に記述します。
EVTFLG_ATTR(1, (EVFLG_TA_ANDまたはEVFLG_TA_OR) | EVFLG_TA_CLR | (EVFLG_TA_TFIFOまたはEVFLG_TA_TPRI));
※属性の先頭の1はフラグID番号。
なお、初期状態の属性は
EVFLG_TA_TFIFO | EVFLG_TA_AND
となっています。
では具体的にsmalight osに用意されているサービスコールの使い方を説明します。
※ちょっと説明に手抜きしています。
void set_flg( UB FLG_ID、UH flg_ptn );
戻り値:有りません。
UB FLG_ID:フラグID
UH flg_ptn:ビットパターン(複数ビットを一度にセットしてもOK)
適用:タスク部で使用可能。割り込みから使用する時は、先頭に"i"を付けたiset_flgを使用します。
W wai_flg( UB FLG_ID、UH flg_ptn );
戻り値:E_OK(=0)と待ち解除時のパターンのOR。符号付32bitである事から、戻り値の上位16bitが0x0000であれば正常に受信したと判断でき、下位の16bitを待ち解除時のパターンとして取得できます。
※マニュアルの記述は間違っていますね。
UB FLG_ID:フラグID
UH flg_ptn:待ちパターン(複数ビットを一度にセットしてもOK)
適用:タスク部で使用可能。初期化関数のマクロで生成した待ち条件が成立するまでWAITとなります。例えば待ちパターンを0x00ffとし、待ち条件がAND条件ならば、下位の8bit全てセットされるまでWAITを継続します。待ち条件がOR条件ならば、下位8bitの何れかがセットされた時点でWAIT状態が解除されます。
W twai_flg( UB FLG_ID、UH flg_ptn, W rel_tim );
戻り値:E_OK(=0)、またはE_TMOUT(=0x00CE0000L)と待ち解除時のパターンのOR。符号付32bitである事から、戻り値の上位16bitが0x0000であれば正常に受信したと判断でき、下位の16bitを待ち解除時のパターンとして取得できます。
逆に上位16bitが0でなければタイムアウトしたと判断できます。
UB FLG_ID:フラグID
UH flg_ptn:待ちパターン(複数ビットを一度にセットしてもOK)
適用:基本的にwai_flgと同じですが、時間管理の仕様はtslp_tskと同じです。
W clr_flg( UB FLG_ID、UH flg_ptn );
戻り値:有りません。
UB FLG_ID:フラグID
UH flg_ptn:クリアパターン
適用:タスク部で使用可能。EVFLG_TA_CLR属性を使用していない時、OSが管理しているフラグと引数のクリアパターンのAND積を取ります。
つまりですね、bit0をクリアしたい時は、~0x0001をクリアパターンとして引数に代入すればよいと言う事です。
それでは具体的にプログラムを組んでイベントフラグの動作を実感してみましょう。
コンパイラの設定は、
EVENTFLGを有効
config.cの設定は、
tsk01:優先度1、起動時READY
tsk02:優先度2、起動時READY
ユーザー初期化関数uinitの設定は、
EVTFLG_ATTR(FID_1, (EVFLG_TA_ANDまたはEVFLG_TA_OR) | EVFLG_TA_TFIFO );
#define TID_1 1 /**/ #define TID_2 2 /**/ #define FID_1 1 /**/ void uinit(void) { /* initialize */ /* event flag initialize */ evtflg_init(); #if 0 EVTFLG_ATTR(FID_1, EVFLG_TA_AND | EVFLG_TA_TFIFO ); #else EVTFLG_ATTR(FID_1, EVFLG_TA_OR | EVFLG_TA_TFIFO ); #endif } void tsk01( void ) { W ercd; while( 1 ) { ercd = wai_flg( FID_1, 0x00ff ); clr_flg( FID_1, ~ercd ); /*ここにブレークポイント*/ } } void tsk02( void ) { int i; UH flg_ptn; for( i = flg_ptn = 0; i < 16; i++ ) { flg_ptn >>= 1; flg_ptn |= 0x8000; set_flg( FID_1, flg_ptn ); /*ここにブレークポイント*/ } while( 1 ) ; /*ここにブレークポイント*/ }
uinitの中の#if~#elseの条件を切り替えてオンチップデバッカーで試してみて下さい。
コメントでブレークを貼る位置を指定しておきますので、そこにブレークポイントを設定します。
またこの時、tsk02のflg_ptnをWATCHしておくと、その動作がよく判ります。
まず#if 1とした時ですが、この時はAND属性となる為に、待ち側では指定したビットの全てがセットされるまで待つ事になります。
tsk01は起動直後のwai_flgによってWAITに遷移します。
続いてtsk02が起動し、変数flg_ptnの左側から1で埋めていきます。
ループを継続している間set_flgにてセットパターンを送信していますが、tsk01の起床条件と一致しない為に、ディスパッチが発生していません。
最後のループでflg_ptnが0xffffとなり、そのセットパターンが送信されると、tsk01がWAITからREADYに遷移し、更に優先度からプリエンプトしてclr_flgの位置で停止します。
tsk01は、ループが回って再びwai_flgのところでWAITに遷移しますので、READYであったtsk02の実行が再開され、while文のところで停止します。
次に#if 0とした時ですが、この時はOR属性となる為に、待ち側では指定したビットの何れかがセットされるまで待つ事になります。
tsk01は起動直後のwai_flgによってWAITに遷移します。
続いてtsk02が起動し、変数flg_ptnの左側から1で埋めていきます。
ループを継続している間set_flgにてセットパターンを送信していますが、tsk01の起床条件と一致しない為に、ディスパッチが発生していません。
9回ループが回ったところででflg_ptnが0xff80となり、そのセットパターンが送信されると、tsk01がWAITからREADYに遷移し、更に優先度からプリエンプトしてclr_flgの位置で停止します。
その後7回clr_flgの位置で停止します。
RTOS理論系
助かりました。
ありがとうございます。
by 方 (2012-10-16 17:35)
お役にたててなによりです。
by hamayan (2012-10-16 22:15)