LoginSignup
2
0

More than 3 years have passed since last update.

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

Last updated at Posted at 2017-12-19

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

前回のあらすじ

前回の記事では、ふたつのCPUでかわりばんこにLow/Highを出力させることで、Lチカを実現しました。しかし、これらのCPUは互いに同期しているわけではないので、タイミングがずれるときれいなLチカになりません。
今回は、セマフォという仕組みを使ってふたつのCPUを同期させます。

セマフォって、何じゃろうか

セマフォについて、ここでは詳しく解説しません。セマフォという「もの」が一つだけ存在し、一方のCPUが獲得したら他方は獲得できなくなるという仕組みで働きます。

ここで実現したいこと

このプロジェクトでは、「500m秒のあいだセマフォを獲得し、その間だけLEDを点灯する。」というプログラムをふたつのCPUに実行させます。すると、

  1. CPU1がセマフォを獲得しLED1を点灯させる。
  2. CPU1が500m秒の間、セマフォを保持し続ける。
  3. CPU1がLED1を消灯しセマフォを解放する。
  4. CPU2が解放されたセマフォを獲得しLED2を点灯させる。
  5. CPU2が500m秒の間、セマフォを保持し続ける。
  6. CPU2がLED2を消灯しセマフォを解放する。
  7. 1に戻る

以上のように、ふたつのCPUがかわりばんこにセマフォを獲得し、LED1/LED2が点滅をするという計画です。

Cortex-M0+のプログラム

main_cm0p.c
#include "project.h"

#define SEMAPHORE_LED  (8)  /* セマフォの識別番号 */

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

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

    for(;;)
    {
        /* セマフォを獲得する */
        while (
            Cy_IPC_Sema_Set(SEMAPHORE_LED, false) != CY_IPC_SEMA_SUCCESS
        ) ;
        /* 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);
        /* セマフォを開放する */
        while (
            Cy_IPC_Sema_Clear(SEMAPHORE_LED, false) != CY_IPC_SEMA_SUCCESS
        ) ;
    }
}

セマフォは、main() 関数に制御が渡る前に SystemInit()関数によって初期化されて、全部で128個のセマフォが作成されます。このうち、0番から7番まではシステムで予約されています。このプログラムでは、8番だけを使用します。

無限ループでは、先のステップ通りにセマフォの獲得(Cy_IPC_Sema_Set)および解放(Cy_IPC_Sema_Clear)とLEDのON/OFF制御を行っています。

main_m4.c
#include "project.h"

#define SEMAPHORE_LED  (8)  /* セマフォの識別番号 */

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

    for(;;)
    {
        /* セマフォを獲得する */
        while (
            Cy_IPC_Sema_Set(SEMAPHORE_LED, false) != CY_IPC_SEMA_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);
        /* セマフォを開放する */
        while (
            Cy_IPC_Sema_Clear(SEMAPHORE_LED, false) != CY_IPC_SEMA_SUCCESS
        ) ;
    }
}

Cortex-M4側でも main() に制御が渡る前に初期化されます。ふたつのCPUが同じセマフォにアクセスするために識別番号 SEMAPHORE_LED が定義されています。

無限ループでの振る舞いは、操作するLEDが異なるのを除いて、Cortex-M0+と同じです。

うまくいかなかった

プロジェクトを作成してLチカさせたところ、ふたつのLEDが交互に光るのを期待したのに、点灯時間が異なっているようです。オシロスコープでみた所、このようになっていました。

NewFile86.png

上(CH1)がCortex-M0+側の波形で、下(CH2)がCortex-M4側の波形です。どうやらCortex-M4側がセマフォを獲ったまま解放していないように見えます。そこで、セマフォが解放された瞬間を詳しく見てみました。

NewFile90.png

先ほどの波形を見る限りでは、セマフォが解放されていないように見えました。しかし、解放の様子を詳しく見ると、Cortex-M4により解放されたセマフォが、ふたたびCortex-M4によって獲得されているのがわかります。再獲得するまでの時間は、約3μ秒です。

NewFile89.png

一方、Cortex-M0+がセマフォを獲得できた場合を見ると、約4μ秒かかっています。これだけの時間がかかっていたら、Cortex-M4に先を越されてしまいます。
つまり、Cortex-M0+がセマフォを獲得するスキが無かったのが原因のようです。

Cortex-M0+にゆずってあげる

色々な解決方法が考えられますが、手っ取り早いのが「Cortex-M0+がセマフォを獲得するスキを作る」ことです。main_cm4.cの無限ループの終わりの方にこのようにスキを作りました。

main_cm4.c
        /* セマフォを解放する */
        while (
            Cy_IPC_Sema_Clear(SEMAPHORE_LED, false) != CY_IPC_SEMA_SUCCESS
        ) ;
        /* Cortex-M0+ にチャンスを与える */
        CyDelayUs(3);
    }
}

これで、毎回Cortex-M0+がセマフォを獲得することができるようになります。変更後は、50%デューティーの波形が観測されました。

NewFile92.png

Cortex-M4がセマフォを開放した後の様子を見ると、Cortex-M0+がセマフォを獲得するまでに約5.6μ秒を要しています。この時間の長さがCortex-M4にセマフォを横取りされてしまった原因と考えられます。

NewFile93.png

こんな目分量でいいの?

とはいえ、この記事で「スキ」を作るために待ち合わせた3μ秒が適切かどうかはわかりません。もっと確実に動作させる方法は、無いのでしょうか?

関連文献

AN215656 – PSoC 6 MCU Dual-Core CPU System Design
CE216795 - PSoC(R) 6 MCU Dual-Core Basics

関連記事

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

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0