Smalight OSを使おう 10タスク目 イベントフラグの使用2 [Smalight OS]
この記事は、某SNSで書いているものを転載しています。
さて今回はイベントフラグの隠れた機能である、タスク間通信について書いてみます。
以前通信機能が無い!と書いてしまいましたが、実はリザルトコードの中にこっそり、待ち解除時の受信パターンが存在する事に気が付きまして、ならばタスク間通信についてもご紹介しておこうと言う次第です。
まず受信側のサービスコールであるwai_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状態が解除されます。
イベントフラグは符号無し16ビットなので、戻り値の符号付32ビットに収納可能です。
普通に
UH rcv_ptn;
rcv_ptn = ercd;
とすれば、受信パターンが取得できます。
※twai_flgの場合は、場合に拠っては上位側のエラーコードを調べる必要が有るかもしれません。
さて、この様にデータの受け渡しが可能なのですが、唯一転送できないデータが有ります。
元々イベントフラグは、任意のビットの何れかをセットする事で相手タスクとの同期を取りますので、16bitのいずれもセットされていないと、相手にイベントの通知をしません。
つまり0x0000は送れないということになります。
また、前回も書きましたが、ビットパターンは上書きされますので、送るタイミングと受け取るタイミングも重要になって来ます。
※単にイベントの発生のみ通知するなら、つまりスイッチが押されている等の”状態”をイベントとする場合は、上書きされてもあまり影響は無いかもしれません。しかし押された数をカウントする場合は、上書きされてしまう事で、数が少なくカウントされる可能性が有ると言う事です。
とまあ、ここまでの説明でなんとなく判ったと思いますので、サンプルプログラムに入ってみたいと思います。
コンパイラの設定は、
EVENTFLGを有効
config.cの設定は、
tsk01:優先度1、起動時READY
tsk02:優先度2、起動時READY
ユーザー初期化関数uinitの設定は、
EVTFLG_ATTR(FID_1, (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(); EVTFLG_ATTR(FID_1, EVFLG_TA_OR | EVFLG_TA_TFIFO ); } void tsk01( void ) { UH rcv_ptn; while( 1 ) { rcv_ptn = wai_flg( FID_1, 0xffff ); clr_flg( FID_1, ~rcv_ptn ); /*ここにブレークポイント*/ } } void tsk02( void ) { int i; for( i = 10; i >= 0; i-- ) { set_flg( FID_1, i ); } while( 1 ) ; }
コメントでブレークを貼る位置を指定しておきますので、そこにブレークポイントを設定します。
またこの時、tsk01のrcv_ptnをWATCHしておくと、その動作がよく判ります。
tsk01は起動直後のwai_flgによってWAITに遷移します。
この時の起床条件ですが、OR属性で、待ちパターンが0xffffであることから、16bitのいずれかがセットされた場合、待ち状態が解除される事が判ります。
tsk01が待ち状態に入ったので、tsk02が起動し、ループの中で10~0までの数字を一回ずつセットします。
tsk02で10~1までの数字が送られる度にtsk01の待ち状態が解除され、ブレークポイントの位置で停止しますので、WATCHしているrcv_flgを見てみます。ちゃんと10~1まで受け取っているでしょうか?。
最後に0を送信していますが、tsk01は起床されず、またtsk02もその後whileループに入ってしまいますので、結局0を受け取る事は出来ませんでした。
上の例題は、受ける側のタスクの優先度を高く、送る側の優先度を低く設定している為に、とりあえず10~1までは正常に受ける事ができました。
では、この優先度を逆にしてしまうとどうなるのか試して見ます。つまり、tsk01が送る側であり、tsk02が受ける側です。
以下の様にプログラムを変更します。
void tsk01( void ) { int i; for( i = 10; i >= 0; i-- ) { set_flg( FID_1, i ); } slp_tsk(); } void tsk02( void ) { UH rcv_ptn; while( 1 ) { rcv_ptn = wai_flg( FID_1, 0xffff ); clr_flg( FID_1, ~rcv_ptn ); /*ここにブレークポイント*/ } }
今回の結果は、まずブレークポイントでの停止が一回しか行われず(※set_flgを発行してもWAITには遷移しない為)、また受信したパターンを見ると、0x000fとなっている事が判ります。つまり、10~0までのすべてのビットがORされてしまった結果です。
この事を踏まえて、あらためてタスク間通信の方法を考えてみると、例えば以下の様な方法が有ります。
送る側の優先度を低く、受ける側の優先度を高く設定する。
送るデータは0~0x7fffの範囲とし、最上位ビットはイベントとして利用する。
つまり送る側では0x8000 | dataとし、受ける側は待ちパターンを0x8000とすれば良いと言う事です。
送る側
set_flg( FID_1, 0x8000 | data );
受ける側
rcv_ptn = wai_flg( FID_1, 0x8000 );
CQ出版のセミナーで講師をされている方の本です。
リアルタイム/マルチタスクシステムの徹底研究―組み込みシステムの基本とタスクスケジューリング技術の基礎
- 作者: 藤倉 俊幸
- 出版社/メーカー: CQ出版
- 発売日: 2003/07
- メディア: 単行本
コメント 0