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


前回のあらすじ

前回の記事では、Cortex-M4側のデータをPIPEの仕組みを使ってCortex-M0+側に送信しました。ところが、頻繁にメッセージを受け取ってしまうと受信側の処理が間に合わないため、PIPE全体を止めるという方法で解決をしました。これでは、他の用途にPIPEを使うことができません。
今回は、メッセージの送信をセマフォという仕組みを使って管理します。

ハードウェア

本プロジェクトでは、前回の記事と同様にタイマとしてMCWDTを使用します。

セマフォの利用方法

セマフォは、PSoC 6 のデュアルコアでLチカ (2)でも使用しました。複数のプロセッサがひとつのリソースを共有する仕組みです。
ここでは、「メッセージを送信する権利」というリソースを複数のタスクが取り合うという構図を実現しました。

まず、Cortex-M4側のあるタスクが、「メッセージを送信する権利」セマフォを獲得します。すると、他のタスクはメッセージを送信できなくなります。

セマフォを獲得したタスクがメッセージを送信したあと、通常であればセマフォを開放して「メッセージを送信する権利」を放棄します。しかしながら、今回のモデルでは獲得したセマフォを放置します。

放置されたセマフォは、誰のものでもなくなりますが、メッセージを送信したことで「メッセージを受信したCortex-M0+側に譲渡された」と解釈します。つまり、「メッセージを送信する権利」を受信側が獲得したとみなします。

メッセージを受信したCortex-M0+側は、もちろん「メッセージを送信する権利」を行使するわけではありません。行使しないことで、後続メッセージが到着するのを防ぎます。

Cortex-M0+側でメッセージの処理が終わったら、「メッセージを送信する権利」を開放します。これで、他のタスクがメッセージを送信できるようになります。

Cortex-M0+側のプログラム

まず、セマフォ番号を決めます。ここでは、8番を使っています。

main_cm0p.c
#define SEMAPHORE_SENDER        (8) /* メッセージを送信する権利 */

Cortex-M0+側が「メッセージを送信する権利」を譲渡されるとき、セマフォを放置することにより譲渡を実現しているので、何かをする必要があるわけではありません。

main_cm0p.c
void pipeUartTxCallback(uint32_t *message) {
    CY_ASSERT(
        txCharsLeft == 0
    );

    /* 受信成功:急いでデータをバッファにコピー */
    strcpy(txBuffer, (char_t *)(&message[1]));
    txCharsLeft = strlen(txBuffer);
    txColumn = 0;
}

むしろ、PIPEを停止する処理がなくなったのですっきりしました。

一方、メッセージの処理が終わった時にセマフォを開放する必要があります。

main_cm0p.c
    for (;;) {
        /* UART制御ブロック */
        if (txCharsLeft > 0) {
            /* バッファのデータを一文字ずつちんたら送り出す。 */
            UART_Put(txBuffer[txColumn]);
            txColumn++;
            txCharsLeft--;

            if (txCharsLeft <= 0) {
                /* すべて送り出したらセマフォを開放する */
                while (
                    Cy_IPC_Sema_Clear(SEMAPHORE_SENDER, false) != CY_IPC_SEMA_SUCCESS
                ) ;
            }
        }
        /* 一文字当たり10msの低速処理 */
        CyDelay(10);
    }

メインループの中で、メッセージの受付を再開した部分でセマフォを開放しています。

Cortex-M4側のプログラム

Cortex-M4側でも同じセマフォ番号を宣言します。

main_cm4.c
#define SEMAPHORE_SENDER        (8) /* メッセージを送信する権利 */

メッセージの送信前に「メッセージを送信する権利」を獲得する処理が追加されました。

main_cm4.c
        case ST_SEND:
            /* フラグが立っている時のみ実行 */
            if (! *(context->flag)) break;
            /* セマフォを獲得する */
            if (Cy_IPC_Sema_Set(SEMAPHORE_SENDER, false) == CY_IPC_SEMA_SUCCESS) {
                /* メッセージを送る */
                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;
                } else {
                   /* メッセージの送信に失敗したので、セマフォを開放する */
                    while (
                        Cy_IPC_Sema_Clear(SEMAPHORE_SENDER, false) != CY_IPC_SEMA_SUCCESS
                    ) ;
                }
            }
            break;

セマフォが獲得出来たらメッセージを送ります。獲得したセマフォ
は、メッセージと共にCortex-M4側に譲渡されたと解釈しますので、他の処理は必要ありません。

万が一メッセージの送信に失敗した場合には、セマフォを開放します。

関連文献

AN215656 - PSoC 6 MCU Dual-Core CPU System Design
AN217666 - PSoC 6 MCU Interrupts
CE216795 - PSoC(R) 6 MCU Dual-Core Basics
CE219339 - PSoC 6 MCU - MCWDT and RTC Interrupts (Dual Core)
PSoC 6 MCU: PSoC 63 with BLE Architecture Technical Reference Manual

関連記事

PSoC 6 のデュアルコアでLチカ (1)
PSoC 6 のデュアルコアでLチカ (2)
PSoC 6 のデュアルコアでLチカ (3)
PSoC 6 のデュアルコアでLチカ (4)
PSoC 6 のデュアルコアでLチカ (5)
PSoC 6 のデュアルコアでLチカ (6)
PSoC 6 のデュアルコアでLチカ (7)
PSoC 6 のデュアルコアでHello World (1)
PSoC 6 のデュアルコアでHello World (2)
PSoC 6 のデュアルコアでHello World (3)
PSoC 6 のデュアルコアでHello World (4)

リポジトリ

github Repository