これは、PSoC Advent Calendar 2017の14日目に突っ込まれた記事です。
##Lチカに飽きました
「PSoC 6 のデュアルコアでLチカ」シリーズでは、Lチカという題材でデュアルコアのPSoC 6を使ってきましたが、さすがに飽きました。
今回は、「Hello World」を題材にPSoC 6を使います。
##「Hello World」とは
組み込み用途のMCUには、ほとんどの場合UARTシリアルインターフェイスが搭載されており、このインターフェイスを介してPCと接続します。「Hello World」は、UARTを使って、"Hello World"の文字を表示させる初歩的なプログラムを指しています。
##本プロジェクトで目指すゴール
PSoC 6では、ふたつのCPUのどちらからもUARTコンポーネントの操作を行うことができます。しかし、単一のUARTコンポーネントに双方のAPIからアクセスを行うと競合が起きてしまいます。どちらか一方のCPUに権利を与えた方が安全です。
このような事情からUARTで取り扱うデータをふたつのCPU間でやり取りする必要が生じてきます。本プロジェクトでは、**Cortex-M0+にUARTコンポーネントの制御を任せ、Cortext-M4からの要求に応じてCortex-M0+**がUARTの制御を行うプロセッサ間通信を行います。
##ハードウェアは、こうなっている
今回のプロジェクトで必要なのは、UARTコンポーネントだけです。
"UART"と名前を付けられたUARTコンポーネントを一つ置きました。このコンポーネントのシリアル出力から出てくるシリアル信号をPCに受信させるために最近ではUSB-UART変換器が使われます。
PSoC 6の評価キットCY8CKIT-062-BLEでは、USB-UARTの役割をKitProg2が担ってくれます。UARTの信号は、P5[0]とP5[1]に接続されているので、.CYDWR
ファイルで端子の割り当てをお忘れなく。
##**Cortex-M0+**側のプログラム
**Cortex-M0+**側のプログラムでは、UARTの制御を行っています。また、Cortex-M4から届く文字列は、メッセージ機能を使って受け取ります。
#include <string.h>
#include "project.h"
#define IPC_CHANNEL_UARTTX (8) /* メッセージ送受信用の IPC チャネル番号 */
/* 送信バッファの定義 */
char_t txBuffer[128] = ""; /* バッファ本体 */
uint32_t txColumn = 0; /* 次に送るべき文字へのインデックス */
uint32_t txCharsLeft = 0; /* 送信すべき残り文字数 */
int main(void) {
/* IPC チャネルのハンドル */
IPC_STRUCT_Type *ipcHandle;
__enable_irq(); /* 全体の割り込みを許可する */
/* メッセージ交換のためのハンドルを獲得する */
ipcHandle = Cy_IPC_Drv_GetIpcBaseAddress(IPC_CHANNEL_UARTTX);
/* Cortex-M4 を叩き起こして CY_CORTEX_M4_APPL_ADDR から実行させる。 */
Cy_SysEnableCM4(CY_CORTEX_M4_APPL_ADDR);
/* UART を初期化する */
UART_Start();
for (;;) {
/* UART制御ブロック */
if (txCharsLeft <= 0) {
char_t *message; /* 受信したメッセージへのポインタ */
/* バッファが空の時はメッセージが到着するのを待つ */
if (
Cy_IPC_Drv_ReadMsgPtr(ipcHandle, (void *)&message) == CY_IPC_DRV_SUCCESS
) {
/* 受信成功:急いでデータをバッファにコピー */
strcpy(txBuffer, message);
txCharsLeft = strlen(txBuffer);
txColumn = 0;
/* IPC チャネルの解放 */
CY_ASSERT(
Cy_IPC_Drv_LockRelease(ipcHandle, CY_IPC_NO_NOTIFICATION) == CY_IPC_DRV_SUCCESS
);
} else {
/* 受信できず:次回に期待 */
}
} else {
/* バッファのデータを一文字ずつちんたら送り出す。 */
UART_Put(txBuffer[txColumn]);
txColumn++;
txCharsLeft--;
}
/* 一文字当たり10msの低速処理 */
CyDelay(10);
}
}
メッセージ機能は、PSoC 6 のデュアルコアでLチカ (4)でも使いましたが、送受信されるデータはダミーでした。今回は、UARTから送信すべき文字列へのポインタをCy_IPC_Drv_ReadMsgPtr()
関数で受け取ります。
受け取ったポインタの先にある文字列の記憶域は、Cortex-M4側が管理している領域です。そのため、破損しないうちにCortex-M0+側が管理する領域に実体をコピーします。
このプログラムでは、低速な出力装置を模して10m秒ごとに一文字のペースで文字を出力します。そのための残り文字数カウンタと次に送信する文字へのインデックスを同時に初期化します。
データを取り出したら、メッセージはLockReleaseによって破棄されます。
##Cortex-M4側のプログラム
Cortex-M4側のプログラムでは、ひっきりなしにメッセージを送り続けます。
#include <stdio.h>
#include "project.h"
#define IPC_CHANNEL_UARTTX (8) /* メッセージ送受信用の IPC チャネル番号 */
/* IPC チャネルのハンドル */
IPC_STRUCT_Type *ipcHandle;
int main(void) {
uint32_t count = 0; /* メッセージ番号 */
uint32_t side = 0; /* 使用中バッファ面 */
char_t buffer[2][128]; /* ダブルバッファ */
__enable_irq(); /* 全体の割り込みを許可する */
/* メッセージ交換のためのハンドルを獲得する */
ipcHandle = Cy_IPC_Drv_GetIpcBaseAddress(IPC_CHANNEL_UARTTX);
for (;;) {
/* 送信するメッセージを作成する */
(void)sprintf(buffer[side], "M4: HELLO WORLD %lu\r\n", count++);
/* メッセージを送る */
while (
Cy_IPC_Drv_SendMsgPtr(ipcHandle, CY_IPC_NO_NOTIFICATION, buffer[side]) != CY_IPC_DRV_SUCCESS
) ;
/* バッファを切り替える */
side = (side)?(0):(1);
}
}
ここでは、Cy_IPC_Drv_SendMsgPtr()
関数によって文字列へのポインタが送信されます。**Cortex-M0+側のプログラムで解説したように、メッセージ機能で送信されるのは文字列へのポインタだけです。また、送ったメッセージがCortex-M0+**で処理されたかどうかに関わらず次の処理を行いたい場合もあります。このような状況であるため、文字列を格納するバッファをふたつ持たせて交互に使用しています。
PSoC 6 のデュアルコアでLチカ (4)と比較するとわかるように、このプログラムでは送信した後の受信確認を行っていません。メッセージが送られたら、処理を待つまでもなく次の処理を続けて効率を上げています。
このように、すべてのメッセージが抜けることなく伝達されたことがわかります。
##関連文献
AN215656 – PSoC 6 MCU Dual-Core CPU System Design
AN217666 - PSoC 6 MCU Interrupts
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)
PSoC 6 のデュアルコアでLチカ (4)
PSoC 6 のデュアルコアでLチカ (5)
PSoC 6 のデュアルコアでLチカ (6)
PSoC 6 のデュアルコアでLチカ (7)