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

##前回のあらすじ
[前回の記事][(3)]では、**Cortex-M4**側のデータを**IPC Channel**の仕組みを使って**Cortex-M0+**側に送信しました。
今回は、**Pipe**という仕組みを使ってメッセージを送信します。

##ハードウェア
本プロジェクトでは、[前回の記事][(3)]と同様にタイマとしてMCWDTを使用します。

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

##PIPE
[前回の記事][(3)]では、メッセージを送信するために**IPC Channel**を一つ消費していました。**IPC Channel**の数は限りがあるため、アプリケーションが大きくなると使い切ってしまう恐れがあります。この**PCoC 6**の場合には、16個の**IPC Channel**のうちユーザが使えるのは8個だけです。

この状況を解決するために用意されたのが**PIPE**という仕組みです。

![GS004419.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/224737/e834cbfd-d03f-2584-4c7c-8defbaab259d.png)

ふたつのCPUの間をふたつの**IPC Channel**がつないでいて、互いにメッセージを送りあう仕組みがあらかじめ用意されています。そこにメッセージを流すのですが、メッセージの先頭に**CLIENT**と呼ばれる宛先番号を付加することにより適切な宛先にメッセージを送り届けることができます。

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

メッセージ交換の仕組みを**IPC Channel**から**PIPE**に変更したので、今回は**Cortex-M0+**側のプログラムも変更されます。

```main_cm0p.c
#include <string.h>
#include "project.h"

#define PIPE_CLIENT_UARTTX      (0) /* UART送信用の CLIENT 番号 */

/* 送信バッファの定義 */
char_t txBuffer[128] = ""; /* バッファ本体 */
uint32_t txColumn = 0; /* 次に送るべき文字へのインデックス */
uint32_t txCharsLeft = 0;  /* 送信すべき残り文字数 */
```

**PIPE**では、あらかじめ決められた**CLIENT**番号で宛先を識別しています。マクロ定義部分で、この**CLIENT**番号が宣言されます。他に使う予定もないので、0番を割り当てました。
この**PIPE**という仕組みは、システム向けの**CYPIPE**と呼ばれるものとユーザ向けの**USRPIPE**呼ばれるものの二種類が存在しているようです。しかしながら、**USRPIPE**の方はデフォルトの状態では実装が不完全なため、使うにはかなり手間がかかります。ここでは、**CYPIPE**の方を利用します。
送信バッファの宣言部分は、これまで同様です。

```main_cm0p.c
void pipeUartTxCallback(uint32_t *message) {
    CY_ASSERT(
        txCharsLeft == 0
    );
    
    /* 受信成功:急いでデータをバッファにコピー */
    strcpy(txBuffer, (char_t *)(&message[1]));
    txCharsLeft = strlen(txBuffer);
    txColumn = 0;
    
    /* 処理が終わるまで受け付け停止 */
    if (txCharsLeft > 0) {
        Cy_IPC_Pipe_EndpointPause(CY_IPC_EP_CYPIPE_CM0_ADDR);
    }
}
```

**PIPE**の仕組みを使うと、受信されたメッセージを引き渡すために**Callback**関数が呼ばれます。ここには、これまでのプログラムで受信に成功した場合の処理が入っています。
**PIPE**の仕組みでは、送られてきたメッセージは必ず受信されて、**Callback**が呼び出されます。受信側の都合にはお構いなしです。したがって、このプログラム例のように一文字ずつ**UART**に出力するような処理を行っていると、処理しきれないほど**Callback**が発生してしまいす。
このような事態を防ぐために、このプログラムでは、文字列を受信したら`Cy_IPC_Pipe_EndpointPause()`関数を呼び出して、受信した文字列の処理が終わるまで次のメッセージを受け付けないようにします。ただし、この関数は**PIPE**上のすべての**CLIENT**に対する受信も停止してしまうため、他の**Callback**も停止してしまい、悪影響が出ます。修正は、次回考えます。

```main_cm0p.c
int main(void) {
    __enable_irq(); /* 全体の割り込みを許可する */
    
    /* メッセージ受信後に呼び出されるCallbackを登録する */
    CY_ASSERT(
        Cy_IPC_Pipe_RegisterCallback(
            CY_IPC_EP_CYPIPE_CM0_ADDR,
            pipeUartTxCallback,
            PIPE_CLIENT_UARTTX
        ) == CY_IPC_PIPE_SUCCESS
    );

    /* Cortex-M4 を叩き起こして CY_CORTEX_M4_APPL_ADDR から実行させる。 */
    Cy_SysEnableCM4(CY_CORTEX_M4_APPL_ADDR); 

    /* UART を初期化する */
    UART_Start();

    for (;;) {
        /* UART制御ブロック */
        if (txCharsLeft > 0) {
            /* バッファのデータを一文字ずつちんたら送り出す。 */
            UART_Put(txBuffer[txColumn]);
            txColumn++;
            txCharsLeft--;
            
            if (txCharsLeft <= 0) {
                /* すべて送り出したら受付再開 */
                Cy_IPC_Pipe_EndpointResume(CY_IPC_EP_CYPIPE_CM0_ADDR);
            }
        }
        /* 一文字当たり10msの低速処理 */
        CyDelay(10);
    }
}
```

`main()`関数では、**IPC Channel**を生成していた部分が**Callback**の登録に変更されました。登録する際には、受信側の**ENDPOINT**番号と**CLIENT**番号が必要です。
メッセージの受信は**Callback**で行われるため、メインループでは、バッファ内のデータを**UART**に送信する処理だけが残されています。
バッファのデータをすべて送り出したら、`Cy_IPC_Pipe_EndpointResume()`関数で次のメッセージの受け付けを再開します。

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

メッセージ交換の方法が変更されたので、**Cortex-M4**側のプログラムも変更されます。

```main_cm4.c
#include <stdio.h>
#include "project.h"

#define PIPE_CLIENT_UARTTX      (0) /* UART送信用の CLIENT 番号 */

/* 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
    );
}
```

メッセージ交換に使用される**CLIENT**番号がマクロで宣言されています。それ以外の割り込み処理とフラグの取り扱いは、これまで通りです。

```main_cm4.c
/* Task1の定義 */
enum Task1State {
    ST_CREATE,
    ST_SEND
};

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

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;
}
```

`Task1Context`のメッセージバッファ部分が変更されています。**PIPE**を使うときには、メッセージの最初の32ビットに**CLIENT**番号などの宛先情報が格納されます。このため、文字列本体の前に`header`が追加されています。

```main_cm4.c
void task1_dispatch(struct Task1Context *context) {
    switch (context->state) {
        case ST_CREATE:
            /* 送信するメッセージを作成する */
            context->message[context->side].header =
                _VAL2FLD(CY_IPC_PIPE_MSG_CLIENT,  PIPE_CLIENT_UARTTX) |
                _VAL2FLD(CY_IPC_PIPE_MSG_USR,     0u) |
                _VAL2FLD(CY_IPC_PIPE_MSG_RELEASE, CY_IPC_CYPIPE_INTR_MASK );
            (void)sprintf(
                context->message[context->side].message,
                "%s: HELLO WORLD %lu\r\n",
                context->name, context->count++
            );
            context->state = ST_SEND;
            break;
        case ST_SEND:
            /* フラグが立っている時のみ実行 */
            if (*(context->flag)) {
                /* メッセージを送る */
                if (
                    Cy_IPC_Pipe_SendMessage(
                        CY_IPC_EP_CYPIPE_CM0_ADDR,
                        CY_IPC_EP_CYPIPE_CM4_ADDR,
                        (uint32_t *)(&context->message[context->side]),
                        0  
                    ) == CY_IPC_PIPE_SUCCESS
                ) {
                    /* バッファを切り替える */
                    context->side = (context->side)?(0):(1);
                    context->state = ST_CREATE;
                    /* 割り込みフラグを折る */
                    *(context->flag) = false;
                }
            }
            break;
        default:
            CY_ASSERT(false);
    }
}
```

送信メッセージを作成する`ST_CREATE`では、`header`部分の操作が追加されています。
`ST_SEND`では、`Cy_IPC_Pipe_SendMessage()`関数で**Cortex-M0+**から**Cortex-M4**に向けてメッセージが発信されます。

`main()`関数は、これまでと同じです。

##実行してみたら

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

![GS004420.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/224737/24e8d72b-5a54-ce42-08d0-39bf6236a861.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 6 のデュアルコアでHello World (3)][(3)]

[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
[(3)]:https://qiita.com/noritan_org/items/e03f958baeafeb2996e0