Windowsシミュレータで学ぶuITRON (1) - タスク操作

# はじめに

**uITRON（FreeRTOSベース）Windowsシミュレータ - Qiita**
https://qiita.com/imagou/items/42c687402e6e58fccddb

にて、せっかくuITRONを体験できる環境を作りましたんで、体験してみましょう。

今回のお題は、**RTOSの肝である「タスク」**です。

# 本編

## 1. 環境

[前回記事](https://qiita.com/imagou/items/42c687402e6e58fccddb#1-1-%E7%92%B0%E5%A2%83)を参照ください。

## 2. 動かしてみる

ビルド方法は[こちら](https://qiita.com/imagou/items/42c687402e6e58fccddb#3-1-%E3%83%93%E3%83%AB%E3%83%89%E6%96%B9%E6%B3%95)。

後はF5押下などしてやれば、コンソールウインドウでデモプログラムが動きます。

### 2-1. タイマタスク

放置しておくと、コンソールで以下が出力されるはずです。

```Console
[TimerTask]: DELAY 3000ms
[TimerTask]: DELAY 3000ms
[TimerTask]: DELAY 3000ms
[TimerTask]: DELAY 3000ms
```

`TimerTask`とやらが、3000msecおきに動作しているらしい、ということがわかるかと思います。
（当方の環境ですと3000msecより**相当長い**ですが、その解析は後回し）

### 2-2. 送信タスク・受信タスク

まずはコンソールで「10」と入力してみましょう（入力後にEnterを押してください）。

```Console
[TimerTask]: DELAY 3000ms
[TimerTask]: DELAY 3000ms
10
[SendTask]: RECV EVENT (00000400)
[RecvTask]: RECV MESSAGE (10,1)
```

どうやら「10」が`SendTask`から`RecvTask`に通知されているようです。

そこで次は、「stop」を入力後、再度「10」を入力してみます。

```Console
[TimerTask]: DELAY 3000ms
stop
[SendTask]: RECV EVENT (00010000)
10
[SendTask]: RECV EVENT (00000400)
[TimerTask]: DELAY 3000ms
```

おや？今回は`RecvTask`が動いていませんね。

次に「start」を入力します。

```Console
[TimerTask]: DELAY 3000ms
start
[SendTask]: RECV EVENT (00020000)
[RecvTask]: RECV MESSAGE (10,2)
```

お察しの通り、`RecvTask`が動き出し、先ほどの「10」を受信しています。

### 2-3. もっと動かしたくなったら

[使い方](https://qiita.com/imagou/items/42c687402e6e58fccddb#3-2-%E4%BD%BF%E3%81%84%E6%96%B9)を読んでください（雑）。

## 3. コードによる解説

では、プログラムコード - `main.c`を引用して説明します。

なお、実際に**裏で動いているのはFreeRTOS**ですので、その制限を受けています。

### 3-1. タスク生成

`cre_tsk()`を使います。

```C
#define CREATE_TASK(name, act, func) {   \
    T_CTSK ctsk_;   \
    ATR tskatr_ = TA_HLNG;  \
    if (act) tskatr_ |= TA_ACT; \
    ctsk_.tskatr = tskatr_;                 /* タスク属性 */ \
    ctsk_.exinf = NULL;                     /* タスク起動時パラメータ */ \
    ctsk_.task = func;                      /* タスク関数 */ \
    ctsk_.itskpri = TASK_PRI(name);         /* タスクの優先度 */   \
    ctsk_.stksz = sizeof(TASK_STACK(name)); /* タスクスタックサイズ */   \
    ctsk_.stk = TASK_STACK(name);           /* タスクスタック（静的に確保しておく必要あり） */    \
    cre_tsk(TASK_ID(name), &ctsk_); \
}
```

マクロ定義しちゃってますが、要は以下を指定して、タスクを生成しています。

- タスク属性（TA_ACT指定で即「実行可能状態」となる）
- タスク関数
- 優先度
- スタックとそのサイズ

### 3-2. タスク起動・中断・再開

#### 3-2-1. タスク起動

```C
    CREATE_TASK(TIMER, FALSE, TimerTask);
```

とあるように、`TimerTask` - タイマタスクは遅延起動しているので、`sta_tsk()`を明示的に呼ぶ必要があります。

```C
    /* タイマタスクは遅延起動 */
    /* sta_tsk()を使いたいがためだけにこうしている */
    sta_tsk(TASK_ID(TIMER), NULL);
```

#### 3-2-2. タスク中断・再開

それぞれ`sus_tsk()`と`rsm_tsk()`を使います。

```C
        /* Suspend Task */
        if (flgptn & EVENT_STOP) {
            sus_tsk(TASK_ID(RECV));
            continue;
        }
        /* Resume Task */
        if (flgptn & EVENT_RESTART) {
            rsm_tsk(TASK_ID(RECV));
            continue;
        }
```

#### 3-2-3. 補足

uITRONでは「待ち状態 - WAITING」「強制待ち状態 - SUSPENDED」の区別があります。
関数との関係はこちら：

- 「待ち状態」から「実行可能状態 - READY」とするのが`sta_tsk()`
- 「実行状態」などから「強制待ち状態」とするのが`sus_tsk()`
- 「強制待ち状態」から「実行可能状態」とするのが`rsm_tsk()`

しかしながら、FreeRTOSには「SUSPENDED」状態しかないらしく、よって`sta_tsk()`と`rsm_tsk()`の本シュミレータ上の実装は同じです。

### 3-3. タスク遅延

`dly_tsk()`を使います。

```C
    while (1) {
        RELTIM tim = 3000;
        DEBUG_PRINT("[%s]: DELAY %dms", __func__, tim);
        dly_tsk(tim);
    }
```

### 3-4. TBD

TBD（今後追加していく予定）

# おわりに

簡単（**フワフワっと**した）なタスク操作の説明でした。

読むだけでなく、実際に動かしてみると、より理解が深まるかと思います！

# 参考

**FreeRTOS API categories**
https://www.freertos.org/a00106.html

**Micro-ITRON4.0 Specification (in Japanese)**
http://www.ertl.jp/ITRON/SPEC/mitron4-j.html