これは、PSoC Advent Calendar 2017の17日目に突っ込まれた記事です。
##前回のあらすじ
前回の記事では、メッセージを送信するCortex-M4側のプログラムをマルチタスク風に変更して、複数の要求がバラバラに到着する様子を見ました。
今回は、これらのタスクが発するメッセージをハードウェアタイマのタイミングで送信するように変更していきます。
##ハードウェア
本プロジェクトでは、タイマとしてMCWDTを追加しています。
もうひとつ忘れてはいけないのが、割り込みを受信するCPUの指定です。
CYDWRファイルのInterruptsタブで、Int_WDT割り込みがCortex-M4に受信されるように設定します。
##MCWDT
Multi-Counter WatchDog Timer (MCWDT)には、三つのカウンタが入っており、それぞれを三つの独立したタイマとして使ったり、複数のカウンタを組み合わせて周期の長いタイマを構成したりできます。
また、このブロックはCortex-M0+とCortex-M4のどちらにも一つずつ搭載されていますので、初期化に使用したCPUに搭載されているブロックが使われます。このプロジェクトでは、メッセージを送信するCortex-M4側のタイミングを作るので、当然Cortex-M4側のプログラムで初期化をします。
本プロジェクトでは、上のような設定をしています。
- 三つのカウンタを独立した三つの周期タイマとして使うので、カウンタ同士を連結するCascade機能は使いません。
- カウンタ0(C0)は、32kHzクロックを12288回数えて割り込みを発生します。周期は384m秒です。
- カウンタ1(C1)は、32kHzクロックを16384回数えて割り込みを発生します。周期は512m秒です。
- カウンタ2(C2)は、32kHzクロックをバイナリカウンタで数えてカウンタの15ビット目を割り込み要因として使っています。割り込み周期は32768クロックで、周期は1.02秒です。
これら三つの要因をひとつの割り込みとして取り扱います。
##**Cortex-M0+**側のプログラム
**Cortex-M0+**側のプログラムは、今回も変更箇所はゼロです。
##Cortex-M4側のプログラム
Cortex-M4側のプログラムでは、まず、MCWDTを利用するための割り込みサービスルーチン(ISR)と初期化ルーチンを定義しています。
/* WDT を使った周期割り込み */
bool Int_WDT_flag0 = false;
bool Int_WDT_flag1 = false;
bool Int_WDT_flag2 = false;
void Int_WDT_isr(void) {
uint32_t status = Cy_MCWDT_GetInterruptStatus(MCWDT_HW);
if (status & CY_MCWDT_CTR0) {
Cy_MCWDT_ClearInterrupt(MCWDT_HW, CY_MCWDT_CTR0);
Int_WDT_flag0 = true;
}
if (status & CY_MCWDT_CTR1) {
Cy_MCWDT_ClearInterrupt(MCWDT_HW, CY_MCWDT_CTR1);
Int_WDT_flag1 = true;
}
if (status & CY_MCWDT_CTR2) {
Cy_MCWDT_ClearInterrupt(MCWDT_HW, CY_MCWDT_CTR2);
Int_WDT_flag2 = true;
}
}
void Int_WDT_Start(void) {
/* 割り込みコンポーネントを初期化しイネーブル */
Cy_SysInt_Init(&Int_WDT_cfg, Int_WDT_isr);
NVIC_ClearPendingIRQ((IRQn_Type) Int_WDT_cfg.intrSrc);
NVIC_EnableIRQ((IRQn_Type) Int_WDT_cfg.intrSrc);
/* MCWDT コンポーネントを起動する */
Cy_MCWDT_Init(MCWDT_HW, &MCWDT_config);
Cy_MCWDT_Enable(MCWDT_HW, MCWDT_ENABLED_CTRS_MASK, 0u);
/* MCWDT コンポーネントの割り込みマスクを設定する */
Cy_MCWDT_SetInterruptMask(MCWDT_HW,
CY_MCWDT_CTR0 | CY_MCWDT_CTR1 | CY_MCWDT_CTR2
);
}
ISRの中では、MCWDTブロックの三つの要因に対してそれぞれフラグを立てています。メインループの中に記述されたメッセージ送信プログラムが、これらのフラグを利用してタイミングを制御しています。
MCWDTの設定方法については、CE219339 – PSoC 6 MCU – MCWDT and RTC Interrupts (Dual Core)を参照してください。
struct Task1Context {
enum Task1State state; /* ステートマシンの状態 */
bool *flag; /* 割り込みフラグ */
uint32_t count; /* メッセージ番号 */
uint32_t side; /* 使用中バッファ面 */
const char_t *name; /* タスクの名前 */
char_t buffer[2][128]; /* ダブルバッファ */
};
void task1_init(struct Task1Context *context, const char_t *name, bool *flag) {
/* タスクの初期化 */
context->name = name;
context->count = 0;
context->side = 0;
context->flag = flag;
context->state = ST_CREATE;
}
void task1_dispatch(struct Task1Context *context) {
switch (context->state) {
case ST_CREATE:
/* 送信するメッセージを作成する */
(void)sprintf(
context->buffer[context->side],
"%s: HELLO WORLD %lu\r\n",
context->name, context->count++
);
context->state = ST_SEND;
break;
case ST_SEND:
/* フラグが立っている時のみ実行 */
if (*(context->flag)) {
/* メッセージを送る */
if (
Cy_IPC_Drv_SendMsgPtr(
ipcHandle,
CY_IPC_NO_NOTIFICATION,
context->buffer[context->side]
) == CY_IPC_DRV_SUCCESS
) {
/* バッファを切り替える */
context->side = (context->side)?(0):(1);
context->state = ST_CREATE;
/* 割り込みフラグを折る */
*(context->flag) = false;
}
}
break;
default:
CY_ASSERT(false);
}
}
Task1に多少の変更を加えています。
- Task1Context構造体にフラグへのポインタを追加しました。
- ディスパッチャでフラグが立っていることという条件をメッセージを送る条件に加えました。
- メッセージが送信できた場合に限りフラグをおろします。
##実行してみたら
実行を始めると、三つのタスクが処理されていく様子がわかります。
それぞれのタスクの実行頻度は、MCWDTで指定した周期に依存します。タスクの実行周期が管理されているので、あるタスクだけが実行されるような状況はなくなりました。
##関連文献
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)