TOPPERS/ASPのイベントフラグの使い方

##はじめに

TOPPERSが提供する各種 RTOSのうち、「TOPPERS新世代カーネル」とよばれるグループの仕様は「TOPPERS新世代カーネル統合仕様書」というドキュメントで提供されています。

・TOPPERS新世代カーネル
　TOPPERS/ASPカーネル
　TOPPERS/FMPカーネル
　TOPPERS/HRP2カーネル
　TOPPERS/SSPカーネル
　TOPPERS/ASP Safetyカーネル

「TOPPERS新世代カーネル統合仕様書」には複数のTOPPERS新世代カーネルの説明が記述され、機能やAPIがまとめられているため、１ファイルを持っているだけで必要な情報を持つことができる反面、特定のAPIの使い方を見ようとした場合に非常に探しにくいという一面もあります。

2021年のTOPPERSコンテスト用の作品を開発中に、締め切り時間まで間もなく分単位の時間で作業をしていてる状況で、イベントフラグを使ってタスク間の同期をとろうと考えました。
直ぐに「TOPPERS新世代カーネル統合仕様書」を開いてイベントフラグの使い方を確認しようと思ったのですが、焦った状況で直ぐにこのように使うといのが瞬時に把握できませんでした。

このため、今後の利用を考慮して、APIの利用方をサンプルプログラムとしてQiita上にまとめようと考えました。
今回はコンテスト作品の開発で欲しかった「イベントフラグ」のサンプルを作りました。

##イベントフラグの静的API

オブジェクトの生成情報や初期状態などを定義するために，システムコンフィギュレーションファイル中に記述するインタフェース。
システムコンフィギュレーションファイルは、TOPPERSに含まれるサンプルプログラムでは**sample1.cfg**になります。

```  
CRE_FLG( ID flgid, { ATR flgatr, FLGPTN iflgptn } )
```  

| 引数 | 内容 |
|:-----------------|:-------------|
| ID flgid | オブジェクトの識別子、プログラム内のAPIでこのマクロを使う |
| ATR flgatr | イベントフラグの属性 |
| FLGPTN iflgptn | イベントフラグの初期ビットパターン |

・イベントフラグの属性

| マクロ | 値 | 内容 |
|:----------|:-------------:|:-------------|
| TA_TPRI | 0x01U | 待ち行列をタスクの優先度順にする |
| TA_WMUL | 0x02U | 複数のタスクが待つのを許す |
| TA_CLR | 0x04U | タスクの待ち解除時にイベントフラグをクリアする |


##イベントフラグのプログラム内のAPI

###イベントフラグのセット
flgidで指定したイベントフラグ（対象イベントフラグ）のsetptnで指定したビットをセットする。

```  
ER ercd = set_flg(ID flgid, FLGPTN setptn)
```  

| 引数 | 内容 |
|:----------|:-------------|
| ID flgid | 対象イベントフラグのID番号 |
| FLGPTN setptn | セットするビットパターン |

参考：ハンドラ（非タスクコンテキスト）からの呼び出し

```  
ER ercd = iset_flg(ID flgid, FLGPTN setptn)
```  

###イベントフラグのクリア
flgidで指定したイベントフラグ（対象イベントフラグ）のclrptnで指定したビットをクリアする。

```  
ER ercd = clr_flg(ID flgid, FLGPTN clrptn)
```  

| 引数 | 内容 |
|:----------|:-------------|
| ID flgid | 対象イベントフラグのID番号 |
| FLGPTN clrptn | クリアするビットパターン |
|               | クリアしないビットを1、クリアするビットを0 |

###イベントフラグ待ち
flgidで指定したイベントフラグ（対象イベントフラグ）が、waiptn と wfmode で指定した待ち解除の条件を満たすのを待つ。


```  
ER ercd = wai_flg(ID flgid, FLGPTN waiptn, MODE wfmode, FLGPTN *p_flgptn)
```  

| 引数 | 内容 |
|:----------|:-------------|
| ID flgid | 対象イベントフラグのID番号 |
| FLGPTN waiptn | 待ちビットパターン |
| MODE wfmode | 待ちモード |
| FLGPTN *p_flgptn | 待ち解除時のビットパターンを入れるメモリ領域へのポインタ |

・待ちモード

| マクロ | 値 | 内容 |
|:----------|:-------------:|:-------------|
| TWF_ORW | 0x01U | 待ちビットパターンに含まれるいずれかのビットがセットされるのを待つ |
| TWF_ANDW | 0x02U | 待ちビットパターンに含まれるすべてのビットがセットされるのを待つ |

参考：イベントフラグ待ち（ポーリング）

```  
ER ercd = pol_flg(ID flgid, FLGPTN waiptn, MODE wfmode, FLGPTN *p_flgptn)
```  

| 引数 | 内容 |
|:----------|:-------------|
| ID flgid | 対象イベントフラグのID番号 |
| FLGPTN waiptn | 待ちビットパターン |
| MODE wfmode | 待ちモード |
| FLGPTN *p_flgptn | 待ち解除時のビットパターンを入れるメモリ領域へのポインタ |


参考：イベントフラグ待ち（タイムアウト付き）

```  
ER ercd = twai_flg(ID flgid, FLGPTN waiptn, MODE wfmode, FLGPTN *p_flgptn, TMO tmout)
```  

| 引数 | 内容 |
|:----------|:-------------|
| ID flgid | 対象イベントフラグのID番号 |
| FLGPTN waiptn | 待ちビットパターン |
| MODE wfmode | 待ちモード |
| FLGPTN *p_flgptn | 待ち解除時のビットパターンを入れるメモリ領域へのポインタ |
| TMO tmout | タイムアウト時間 |


###イベントフラグの再初期化

flgidで指定したイベントフラグ（対象イベントフラグ）を再初期化する。
対象イベントフラグのビットパターンは，初期ビットパターンに初期化される。
対象イベントフラグの待ち行列につながれたタスクは，待ち行列の先頭のタスクから順に待ち解除される。
待ち解除されたタスクには，待ち状態となったサービスコールから**E_DLT**エラーが返る。

```  
ER ercd = ini_flg(ID flgid)
```  

| 引数 | 内容 |
|:----------|:-------------|
| ID flgid | 対象イベントフラグのID番号 |


###イベントフラグの削除

flgidで指定したイベントフラグ（対象イベントフラグ）を削除する。

```  
ER ercd = del_flg(ID flgid)
```  

| 引数 | 内容 |
|:----------|:-------------|
| ID flgid | 対象イベントフラグのID番号 |


##サンプルコード
**TOPPERS/ASP**と**sample1.c**を使って、イベントフラグのサンプルプログラムを作ってみました。

##サンプルプログラム
- **main_task()**のほかに**task1()**、**task2()**、**task3()**の計４つのタスクを生成します。
- **main_task()**は他の３つのタスクをスタート後、**tslp_tsk()**でスリープを繰り返します。
- **task1()**は１秒ごとにイベントフラグ FLG1へ0x01、0x02、0x03 (0x01|0x02) を順番にセットし、それを繰り返します。
- **task2()**は**wai_flg()**でイベントパターン**0x01**を待ち、イベントフラグがセットしたらコンソールにメッセージを出力します。
- **task3()**は**wai_flg()**でイベントパターン**0x02**を待ち、イベントフラグがセットしたらコンソールにメッセージを出力します。
- **task1()**でイベントパターン**0x01**がセットされると**task2()**が起床してコンソールにメッセージが出力されます。
- **task1()**でイベントパターン**0x02**がセットされると**task3()**が起床してコンソールにメッセージが出力されます。
- **task1()**でイベントパターン**0x03**がセットされると**task2()**と**task3()**が起床してコンソールにメッセージが出力されます。

以下に主要な設定や関数を記します。


```  sample1.cfg
CRE_TSK(TASK1, { TA_NULL, 1, task1, HIGH_PRIORITY, STACK_SIZE, NULL });
CRE_TSK(TASK2, { TA_NULL, 2, task2, MID_PRIORITY, STACK_SIZE, NULL });
CRE_TSK(TASK3, { TA_NULL, 3, task3, LOW_PRIORITY, STACK_SIZE, NULL });
CRE_TSK(MAIN_TASK, { TA_ACT, 0, main_task, MAIN_PRIORITY, STACK_SIZE, NULL });
DEF_TEX(TASK1, { TA_NULL, tex_routine });
DEF_TEX(TASK2, { TA_NULL, tex_routine });
DEF_TEX(TASK3, { TA_NULL, tex_routine });

CRE_FLG(FLG1, { TA_WMUL, 0x00U });
```  

```  sample1.h
#define MAIN_PRIORITY   5
#define HIGH_PRIORITY   9
#define MID_PRIORITY    10
#define LOW_PRIORITY    11

#define STACK_SIZE      4096

extern void     task1(intptr_t exinf);
extern void     task2(intptr_t exinf);
extern void     task3(intptr_t exinf);
extern void     main_task(intptr_t exinf);
```  

```  sample1.c
void task1(intptr_t exinf)
{
  ER  er;

  syslog(LOG_NOTICE, "task1()");

  er = clr_flg( FLG1, 0x00 );

  while( 1 ) {
    tslp_tsk( 1000 );
    syslog(LOG_NOTICE, "set 0x01");
    er = set_flg( FLG1, 0x01 );

    tslp_tsk( 1000 );
    syslog(LOG_NOTICE, "set 0x02");
    er = set_flg( FLG1, 0x02 );

    tslp_tsk( 1000 );
    syslog(LOG_NOTICE, "set 0x03");
    er = set_flg( FLG1, 0x01 | 0x02 );
  }
}


void task2(intptr_t exinf)
{
  ER  er;
  FLGPTN  ptn;

  syslog(LOG_NOTICE, "task2()");

  while( 1 ) {
    er = wai_flg( FLG1, 0x01, TWF_ORW, &ptn );
    er = clr_flg( FLG1, ~0x01 );
    syslog(LOG_NOTICE, "wakeup task2()");
  }
}


void task3(intptr_t exinf)
{
  ER  er;
  FLGPTN  ptn;

  syslog(LOG_NOTICE, "task3()");

  while( 1 ) {
    er = wai_flg( FLG1, 0x02, TWF_ORW, &ptn );
    er = clr_flg( FLG1, ~0x02 );
    syslog(LOG_NOTICE, "wakeup task3()");
  }
}


void main_task(intptr_t exinf)
{
  syslog(LOG_NOTICE, "main_task()");

  SVC_PERROR(act_tsk(TASK1));
  SVC_PERROR(act_tsk(TASK2));
  SVC_PERROR(act_tsk(TASK3));

  while( 1 ) {
    tslp_tsk( 100 );
  }
}
```  



###結果
プログラムをビルドし、実行すると以下のような表示がコンソールへ出力されます。

```  
TOPPERS/ASP Kernel Release 1.9.3 for RaspberryPi pico (Cortex-M0+) (Dec 20 2021, 01:07:24)
Copyright (C) 2000-2003 by Embedded and Real-Time Systems Laboratory
                            Toyohashi Univ. of Technology, JAPAN
Copyright (C) 2004-2014 by Embedded and Real-Time Systems Laboratory
            Graduate School of Information Science, Nagoya Univ., JAPAN
Copyright (C) 2016-2017 by Education Working Group TOPPERS PROJECT

System logging task is started on port 1.
main_task()
task1()
task2()
task3()
set 0x01
wakeup task2()
set 0x02
wakeup task3()
set 0x03
wakeup task2()
wakeup task3()
set 0x01
wakeup task2()
set 0x02
wakeup task3()
set 0x03
wakeup task2()
wakeup task3()
set 0x01
wakeup task2()
set 0x02
wakeup task3()
```  

##さいごに
使いなれている**API**であれば、忘れたり曖昧な部分を「TOPPERS新世代カーネル統合仕様書」で直ぐに探してプログラムを書けると思いますが、初めて使う場合には少し読みにくい仕様書かなと個人的に思っています。
自分の備忘録や開発メモを兼ねて、他のAPIのサンプルを執筆していきたいと思ってます。

－ 以上 －