PSoC 6 のデュアルコアでLチカ (4)


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

前回のあらすじ

前回の記事では、ふたつのCPUがお互いのセマフォの獲得を監視することで、Lチカを実現しました。しかしながら、セマフォの獲得を確実に検出できる保証がないので、このアプリケーションに限り動作するプログラムになってしまっています。
今回は、Cortex-M0+からCortex-M4にメッセージを送信することにより、より確実なLチカを行います。

メッセージ機能のコンセプト

メッセージ機能については、PSoC 6 MCU: PSoC 63 with BLE Architecture Technical Reference Manualの"Inter-Processor Communication"に記述があります。簡単に図示すると以下のようになっています。

この図では、SenderReceiverDataを送っています。

  1. SenderIPC Channelを獲得して通信の権利を得ます。
  2. Senderは、Dataに送信すべきデータを載せます。
  3. Senderは、IPC ChannelNotify機能によってデータを載せた事をReceiverに通知します。
  4. Receiverは、Dataからデータを取り出します。
  5. Receiverは、Release機能によってデータを取り出した事をSenderに通知します。

以上のステップにより、Senderから送られたデータがReceiverに届けられた事をSender側も確認できるようになります。

メッセージを二つ使って、自身の処理が終了した事を相手方のCPUに知らせれば、セマフォを使った時と同じようにLチカが実現できます。
以下のプログラムでは、ステップ3の通知をCortex-M0+からCortex-M4への通知に、ステップ5の通知をCortex-M4からCortex-M0+への通知に利用することでメッセージ一つで通知の送受信を行っています。

Cortex-M0+側のプログラム

このプロジェクトでは、Cortex-M0+がメッセージの送信役になります。

main_cm0p.c
#include "project.h"

#define IPC_CHANNEL_LED  (8)  /* メッセージ送受信用の IPC チャネル番号 */

int main(void)
{
    IPC_STRUCT_Type *ipcHandle;

    __enable_irq(); /* 全体の割り込みを許可する */

    /* メッセージ交換のためのハンドルを獲得する */
    ipcHandle = Cy_IPC_Drv_GetIpcBaseAddress(IPC_CHANNEL_LED);

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

    for(;;)
    {
        /* GPIO 出力をクリア (Low) する。 */
        Cy_GPIO_Clr(Pin_LEDR_PORT, Pin_LEDR_NUM);
        /* 500m秒待つ */
        CyDelay(500);
        /* GPIO 出力をセット (High) する。 */
        Cy_GPIO_Set(Pin_LEDR_PORT, Pin_LEDR_NUM);
        /* Cortex-M4にメッセージを送る */
        while (
            Cy_IPC_Drv_SendMsgWord(ipcHandle, CY_IPC_NO_NOTIFICATION, 0) != CY_IPC_DRV_SUCCESS
        );
        /* Cortex-M4の受信確認を待つ */
        while (
            Cy_IPC_Drv_IsLockAcquired(ipcHandle) == true
        );
    }
}

メッセージの送受信に使われるハンドラがCy_IPC_Drv_GetIpcBaseAddress()関数で定義されています。この関数に与えられる引数には、IPCチャネルの番号が入ります。 IPC チャネルのうち、 0番から7番までは、システムで予約されているので、ここでは、IPC_CHANNEL_LED=8を使用しています。使用されているチャネルは、Shared Filesの中のcy_ipc_config.hで定義されていますので参照してください。

メッセージを送信するのは、Cy_IPC_Drv_SendMsgWord()関数です。メッセージは32ビットのデータです。このアプリケーションでは、送るメッセージそのものに意味は無いので、便宜的に0を送っています。

Cortex-M4がメッセージの受信を確認したら、IPC Channelが解放されて、Cy_IPC_Drv_IsLockAcquired()関数が偽を返します。これで、一連の処理は終わりです。

Cortex-M4側のプログラム

一方、Cortex-M4側のプログラムは、以下のようになっています。

main_cm4.c
#include "project.h"

#define IPC_CHANNEL_LED  (8)  /* メッセージ送受信用の IPC チャネル番号 */

int main(void)
{
    IPC_STRUCT_Type *ipcHandle;
    uint32_t message;

    __enable_irq(); /* 全体の割り込みを許可する */

    /* メッセージ交換のためのハンドルを獲得する */
    ipcHandle = Cy_IPC_Drv_GetIpcBaseAddress(IPC_CHANNEL_LED);

    for(;;)
    {
        /* Cortex-M0+からメッセージが到着するのを待つ */
        while (
            Cy_IPC_Drv_ReadMsgWord(ipcHandle, &message) != CY_IPC_DRV_SUCCESS
        );
        /* GPIO 出力をクリア (Low) する。 */
        Cy_GPIO_Clr(Pin_LEDG_PORT, Pin_LEDG_NUM);
        /* 500m秒待つ */
        CyDelay(500);
        /* GPIO 出力をセット (High) する。 */
        Cy_GPIO_Set(Pin_LEDG_PORT, Pin_LEDG_NUM);
        /* Cortex-M0+にメッセージの受信確認を送る */
        CY_ASSERT(
            Cy_IPC_Drv_LockRelease(ipcHandle, CY_IPC_NO_NOTIFICATION) == CY_IPC_DRV_SUCCESS
        );
    }
}

ハンドルの獲得は、Cortex-M0+と同じです。

メッセージの到着を待つのが、Cy_IPC_Drv_ReadMsgWord()関数です。受信が成功したら変数messageに値が入るはずですが、ここでは使わないので無視しています。

500m秒の処理が終わったら、Cy_IPC_Drv_LockRelease関数で受信確認を通知します。これで、Cortex-M0+側が次の処理を進められるようになります。

割り込みは、使ってない

「メッセージ機能のコンセプト」の説明図にあった割り込みは、今回は使わずにポーリングによって処理をしています。割り込みを使えば、さらにCPUの自由時間が増えるでしょう。

関連文献

AN215656 – PSoC 6 MCU Dual-Core CPU System Design
CE216795 - PSoC(R) 6 MCU Dual-Core Basics
PSoC 6 MCU: PSoC 63 with BLE Architecture Technical Reference Manual

関連記事

PSoC 6 のデュアルコアでLチカ (1)
PSoC 6 のデュアルコアでLチカ (2)
PSoC 6 のデュアルコアでLチカ (3)