前回ConditionalVariableをヤバイなどと言ってしまいましたが、どうもそんなこと無いみたいです。メモリの使い方がOS2とOS6で違うだけだったみたい。ボードもOSも「2種併用中」で混乱してました。さて、今回はEventFlagsです。スレッド間、あるいは割り込みサービスルーチンからイベントを他に簡単に伝えることができるもの。
※「モダンOSのお砂場」投稿順Indexはこちら
本「モダンOSのお砂場」シリーズでは、
Nucleo-F401REボードをターゲットに Mbed OS 6
別シリーズ「手習ひデジタル信号処理」シリーズでは、
Nucleo-F446REボードをターゲットに Mbed OS 2
を利用させていただいております。どちらもArm Mbed OSですが、バージョン違うのでところどころ違うところがあります。混乱しないように注意したいと思います。
EventFlags
さて今回「お砂場」してみるのは、OS6の EventFlags です。公式のリファレンスマニュアルは以下に
平行に走っているスレッドや割り込みサービスルーチン(ISR)から他のスレッドに何かイベントが発生したことを知らせるAPIです。1個のイベントに対して31個までのフラグを設定でき、
-
- どれか一つでもフラグが立ったら(any)
- 全部のフラグが立ったら(all)
といった条件を付けてイベント発生を待つことが可能です。なお、待ち合わせのタイムアウト設定(指定時間内にイベント来なければ戻る)も可能です。
実験に使用したコード
実験では以下の3スレッドを平行に動作させました。
-
- w1スレッド。無限ループの中で、イベント待ちのタイムアウトは無限設定で、イベントを待ち続けます。イベントフラグのどれかが立ったら、その様子を標準出力に報告します。なおイベントフラグを検出したら、該当フラグを自動クリアします。
- t1スレッド。無限ループの中で、3333ms毎に一回、t1用のイベントフラグを立てます。
- mainスレッド。上記2つのスレッドを起動後、無限ループの中で、7777ms毎に一回、main用のイベントフラグを立てます。
実験コードはこんな感じ。
#include "mbed.h" using namespace std::chrono; #define MAIN_FLAG (1UL) #define T1_FLAG (256UL) EventFlags event_flags; #define ARRAYSIZE (2000) int volatile work[ARRAYSIZE]; void w1_thread() { uint32_t flags_read = 0; while (true) { flags_read = event_flags.wait_any((MAIN_FLAG | T1_FLAG), osWaitForever, true); printf("W1 flags_read: 0x%08x\r\n", flags_read); } } void t1_thread() { while (true) { ThisThread::sleep_for(3333ms); event_flags.set(T1_FLAG); } } void initWork() { for (int i=0; i<ARRAYSIZE; i++) { work[i]=0; } } int main() { initWork(); Thread w1; w1.start(mbed::callback(w1_thread)); Thread t1; t1.start(mbed::callback(t1_thread)); while (true) { ThisThread::sleep_for(7777ms); event_flags.set(MAIN_FLAG); } }
ビルド結果
ビルド結果は、左のようです。前回、RAMが使い切られているように見えてヤバイなどと書いてしまいましたが。ZI Data(ゼロ初期化データ)でRAMの大部分を埋めてしまってます。しかし、以下を参照すると
大域変数は勿論、main、timer関係のスタックなどもその領域に確保されているみたいです。
いずれにせよ、現状のコードは何もオプティマイズしていないので、必要に応じて調整する必要がありそうです。
とりあえず、コンパイル通る間は大丈夫だあ、ということで練習進めてしまいますが。
実機動作確認
Nucleo-F401REボードにオブジェクトを書き込んで動作を確認してみました。ソースにあるようにEventFlagの割り付け的には
-
- main関数側、ビット0
- t1スレッド側、ビット8
です。以下の動作例で、ビット0が立って0x00000001であれば、main側のイベントフラグセットに対応して表示されたもの、ビット8が立って0x00000100であれば、t1側のイベントフラグセットに対応です。
仮想シリアルポートに出力されてくる結果をTeratermで観察した結果が以下に。
main側は7777ms周期、t1側が3333ms周期に設定なので、t1側の2回から3回に対して、main側が1回の割合でイベントが検出されていることが分かります。
まあ、予定どおりでないかい。