これは、PSoC Advent Calendar 2017の2日目に突っ込まれた記事です。
##前回のあらすじ
前回の記事では、ふたつのCPUでかわりばんこにLow/Highを出力させることで、Lチカを実現しました。しかし、これらのCPUは互いに同期しているわけではないので、タイミングがずれるときれいなLチカになりません。
今回は、セマフォという仕組みを使ってふたつのCPUを同期させます。
##セマフォって、何じゃろうか
セマフォについて、ここでは詳しく解説しません。セマフォという「もの」が一つだけ存在し、一方のCPUが獲得したら他方は獲得できなくなるという仕組みで働きます。
##ここで実現したいこと
このプロジェクトでは、「500m秒のあいだセマフォを獲得し、その間だけLEDを点灯する。」というプログラムをふたつのCPUに実行させます。すると、
- CPU1がセマフォを獲得しLED1を点灯させる。
- CPU1が500m秒の間、セマフォを保持し続ける。
- CPU1がLED1を消灯しセマフォを解放する。
- CPU2が解放されたセマフォを獲得しLED2を点灯させる。
- CPU2が500m秒の間、セマフォを保持し続ける。
- CPU2がLED2を消灯しセマフォを解放する。
- 1に戻る
以上のように、ふたつのCPUがかわりばんこにセマフォを獲得し、LED1/LED2が点滅をするという計画です。
##**Cortex-M0+**のプログラム
#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制御を行っています。
#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が交互に光るのを期待したのに、点灯時間が異なっているようです。オシロスコープでみた所、このようになっていました。
上(CH1)がCortex-M0+側の波形で、下(CH2)がCortex-M4側の波形です。どうやらCortex-M4側がセマフォを獲ったまま解放していないように見えます。そこで、セマフォが解放された瞬間を詳しく見てみました。
先ほどの波形を見る限りでは、セマフォが解放されていないように見えました。しかし、解放の様子を詳しく見ると、Cortex-M4により解放されたセマフォが、ふたたびCortex-M4によって獲得されているのがわかります。再獲得するまでの時間は、約3μ秒です。
一方、**Cortex-M0+**がセマフォを獲得できた場合を見ると、約4μ秒かかっています。これだけの時間がかかっていたら、Cortex-M4に先を越されてしまいます。
つまり、**Cortex-M0+**がセマフォを獲得するスキが無かったのが原因のようです。
##**Cortex-M0+**にゆずってあげる
色々な解決方法が考えられますが、手っ取り早いのが「**Cortex-M0+**がセマフォを獲得するスキを作る」ことです。main_cm4.c
の無限ループの終わりの方にこのようにスキを作りました。
/* セマフォを解放する */
while (
Cy_IPC_Sema_Clear(SEMAPHORE_LED, false) != CY_IPC_SEMA_SUCCESS
) ;
/* Cortex-M0+ にチャンスを与える */
CyDelayUs(3);
}
}
これで、毎回**Cortex-M0+**がセマフォを獲得することができるようになります。変更後は、50%デューティーの波形が観測されました。
Cortex-M4がセマフォを開放した後の様子を見ると、Cortex-M0+がセマフォを獲得するまでに約5.6μ秒を要しています。この時間の長さがCortex-M4にセマフォを横取りされてしまった原因と考えられます。
##こんな目分量でいいの?
とはいえ、この記事で「スキ」を作るために待ち合わせた3μ秒が適切かどうかはわかりません。もっと確実に動作させる方法は、無いのでしょうか?
##関連文献
AN215656 – PSoC 6 MCU Dual-Core CPU System Design
CE216795 - PSoC(R) 6 MCU Dual-Core Basics
##関連記事
PSoC 6 のデュアルコアでLチカ (1)