PSoC 6 のデュアルコアでHello World (3)

これは、[PSoC Advent Calendar 2017]の17日目に突っ込まれた記事です。

##前回のあらすじ
[前回の記事][(2)]では、メッセージを送信する**Cortex-M4**側のプログラムをマルチタスク風に変更して、複数の要求がバラバラに到着する様子を見ました。
今回は、これらのタスクが発するメッセージをハードウェアタイマのタイミングで送信するように変更していきます。

##ハードウェア
本プロジェクトでは、タイマとしてMCWDTを追加しています。

![GS003394.png](https://qiita-image-store.s3.amazonaws.com/0/224737/9eee4742-3461-c273-fe0a-ee7967f173e4.png)

もうひとつ忘れてはいけないのが、割り込みを受信するCPUの指定です。

![GS003396.png](https://qiita-image-store.s3.amazonaws.com/0/224737/386d0760-c022-2b02-7c00-2ff0a2bb7a2d.png)

CYDWRファイルのInterruptsタブで、**Int_WDT**割り込みが**Cortex-M4**に受信されるように設定します。

##MCWDT
Multi-Counter WatchDog Timer (MCWDT)には、三つのカウンタが入っており、それぞれを三つの独立したタイマとして使ったり、複数のカウンタを組み合わせて周期の長いタイマを構成したりできます。
また、このブロックは**Cortex-M0+**と**Cortex-M4**のどちらにも一つずつ搭載されていますので、初期化に使用したCPUに搭載されているブロックが使われます。このプロジェクトでは、メッセージを送信する**Cortex-M4**側のタイミングを作るので、当然**Cortex-M4**側のプログラムで初期化をします。

![GS003393.png](https://qiita-image-store.s3.amazonaws.com/0/224737/5827cc03-811d-fdb1-1f81-ac50d6fed560.png)

本プロジェクトでは、上のような設定をしています。

1. 三つのカウンタを独立した三つの周期タイマとして使うので、カウンタ同士を連結するCascade機能は使いません。
2. カウンタ０(C0)は、32kHzクロックを12288回数えて割り込みを発生します。周期は384m秒です。
3. カウンタ１(C1)は、32kHzクロックを16384回数えて割り込みを発生します。周期は512m秒です。
4. カウンタ２(C2)は、32kHzクロックをバイナリカウンタで数えてカウンタの15ビット目を割り込み要因として使っています。割り込み周期は32768クロックで、周期は1.02秒です。

これら三つの要因をひとつの割り込みとして取り扱います。

##**Cortex-M0+**側のプログラム

**Cortex-M0+**側のプログラムは、今回も変更箇所はゼロです。

##**Cortex-M4**側のプログラム

**Cortex-M4**側のプログラムでは、まず、MCWDTを利用するための割り込みサービスルーチン(ISR)と初期化ルーチンを定義しています。

```c:main_cm4.c
/* WDT を使った周期割り込み */
bool Int_WDT_flag0 = false;
bool Int_WDT_flag1 = false;
bool Int_WDT_flag2 = false;

void Int_WDT_isr(void) {
    uint32_t status = Cy_MCWDT_GetInterruptStatus(MCWDT_HW);
    if (status & CY_MCWDT_CTR0) {
        Cy_MCWDT_ClearInterrupt(MCWDT_HW, CY_MCWDT_CTR0);
        Int_WDT_flag0 = true;
    }  
    if (status & CY_MCWDT_CTR1) {
        Cy_MCWDT_ClearInterrupt(MCWDT_HW, CY_MCWDT_CTR1);
        Int_WDT_flag1 = true;
    }  
    if (status & CY_MCWDT_CTR2) {
        Cy_MCWDT_ClearInterrupt(MCWDT_HW, CY_MCWDT_CTR2);
        Int_WDT_flag2 = true;
    }  
}

void Int_WDT_Start(void) {
    /* 割り込みコンポーネントを初期化しイネーブル */
    Cy_SysInt_Init(&Int_WDT_cfg, Int_WDT_isr);
    NVIC_ClearPendingIRQ((IRQn_Type) Int_WDT_cfg.intrSrc);
    NVIC_EnableIRQ((IRQn_Type) Int_WDT_cfg.intrSrc);
   
    /* MCWDT コンポーネントを起動する */
    Cy_MCWDT_Init(MCWDT_HW, &MCWDT_config);
    Cy_MCWDT_Enable(MCWDT_HW, MCWDT_ENABLED_CTRS_MASK, 0u);
  
    /* MCWDT コンポーネントの割り込みマスクを設定する */
    Cy_MCWDT_SetInterruptMask(MCWDT_HW,
        CY_MCWDT_CTR0 | CY_MCWDT_CTR1 | CY_MCWDT_CTR2
    );
}
```

ISRの中では、MCWDTブロックの三つの要因に対してそれぞれフラグを立てています。メインループの中に記述されたメッセージ送信プログラムが、これらのフラグを利用してタイミングを制御しています。

MCWDTの設定方法については、[CE219339 – PSoC 6 MCU – MCWDT and RTC Interrupts (Dual Core)][CE219339]を参照してください。

```c:main_cm4.c
struct Task1Context {
    enum Task1State state;  /* ステートマシンの状態 */
    bool *flag;  /* 割り込みフラグ */
    uint32_t count;  /* メッセージ番号 */
    uint32_t side;  /* 使用中バッファ面 */
    const char_t *name;  /* タスクの名前 */
    char_t buffer[2][128]; /* ダブルバッファ */
};

void task1_init(struct Task1Context *context, const char_t *name, bool *flag) {
    /* タスクの初期化 */
    context->name = name;
    context->count = 0;
    context->side = 0;
    context->flag = flag;
    context->state = ST_CREATE;
}

void task1_dispatch(struct Task1Context *context) {
    switch (context->state) {
        case ST_CREATE:
            /* 送信するメッセージを作成する */
            (void)sprintf(
                context->buffer[context->side],
                "%s: HELLO WORLD %lu\r\n",
                context->name, context->count++
            );
            context->state = ST_SEND;
            break;
        case ST_SEND:
            /* フラグが立っている時のみ実行 */
            if (*(context->flag)) {
                /* メッセージを送る */
                if (
                    Cy_IPC_Drv_SendMsgPtr(
                        ipcHandle,
                        CY_IPC_NO_NOTIFICATION,
                        context->buffer[context->side]
                    ) == CY_IPC_DRV_SUCCESS
                ) {
                    /* バッファを切り替える */
                    context->side = (context->side)?(0):(1);
                    context->state = ST_CREATE;
                    /* 割り込みフラグを折る */
                    *(context->flag) = false;
                }
            }
            break;
        default:
            CY_ASSERT(false);
    }
}
```

Task1に多少の変更を加えています。

1. Task1Context構造体にフラグへのポインタを追加しました。
2. ディスパッチャでフラグが立っていることという条件をメッセージを送る条件に加えました。
3. メッセージが送信できた場合に限りフラグをおろします。

##実行してみたら

実行を始めると、三つのタスクが処理されていく様子がわかります。

![GS003395.png](https://qiita-image-store.s3.amazonaws.com/0/224737/cdbf517f-ab8a-8444-263d-b53a6a2485c5.png)

それぞれのタスクの実行頻度は、MCWDTで指定した周期に依存します。タスクの実行周期が管理されているので、あるタスクだけが実行されるような状況はなくなりました。

![GS003397.png](https://qiita-image-store.s3.amazonaws.com/0/224737/ae3103ff-566d-2e5f-5bcc-dd67526eae76.png)


##関連文献
[AN215656 – PSoC 6 MCU Dual-Core CPU System Design][AN215656]
[AN217666 - PSoC 6 MCU Interrupts][AN217666]
[CE216795 - PSoC(R) 6 MCU Dual-Core Basics][CE216795]
[CE219339 – PSoC 6 MCU – MCWDT and RTC Interrupts (Dual Core)][CE219339]
[PSoC 6 MCU: PSoC 63 with BLE Architecture Technical Reference Manual][PSoC63BLE ATRM]

##関連記事
[PSoC 6 のデュアルコアでＬチカ (1)][LC(1)]
[PSoC 6 のデュアルコアでＬチカ (2)][LC(2)]
[PSoC 6 のデュアルコアでＬチカ (3)][LC(3)]
[PSoC 6 のデュアルコアでＬチカ (4)][LC(4)]
[PSoC 6 のデュアルコアでＬチカ (5)][LC(5)]
[PSoC 6 のデュアルコアでＬチカ (6)][LC(6)]
[PSoC 6 のデュアルコアでＬチカ (7)][LC(7)]
[PSoC 6 のデュアルコアでHello World (1)][(1)]
[PSoC 6 のデュアルコアでHello World (2)][(2)]

[PSoC Advent Calendar 2017]:https://qiita.com/advent-calendar/2017/psoc
[PSoC 6]:http://www.cypress.com/psoc6
[CY8CKIT-062-BLE]:http://www.cypress.com/cy8ckit-062-ble
[AN215656]:https://www.cypress.com/an215656
[CE216795]:https://www.cypress.com/ce216795
[CE219339]:https://www.cypress.com/ce219339
[AN217666]:https://www.cypress.com/an217666
[PSoC63BLE ATRM]:http://www.cypress.com/file/385621/download
[LC(1)]:https://qiita.com/noritan_org/items/d08f4578a91d63b93c06
[LC(2)]:https://qiita.com/noritan_org/items/8240f48f81b32b81a179
[LC(3)]:https://qiita.com/noritan_org/items/ea0ea4c933539409f2dc
[LC(4)]:https://qiita.com/noritan_org/items/03a360f00fd3f6988baf
[LC(5)]:https://qiita.com/noritan_org/items/96fb2d7cfb28620eb3a6
[LC(6)]:https://qiita.com/noritan_org/items/c20005207be98ac33200
[LC(7)]:https://qiita.com/noritan_org/items/23abc166c3b4e6719574
[(1)]:https://qiita.com/noritan_org/items/35651a747f0dec5b2cc8
[(2)]:https://qiita.com/noritan_org/items/c3878bc05a53247b641d