RP2040のマルチ(デュアル)コア動作を確認する
秋月電子の「RP2040マイコンボードキット」、購入時に意識していなかったが、マルチ(デュアル)コアであることがわかったので、簡単な動作を確認してみた。
何をするか?
あるコアにて定期的にシリアル出力している状態で、別コアにてシリアル入力された内容をシリアル出力するもの。
準備
Arduino IDE環境:Macシリコンプロセッサ
最初はまともにBuildできなかったが、調べると下記内容が見つかる。
これに従い、下記を実行。
$ softwareupdate --install-rosetta
BOOTSELボタン
最初の1回だけ、BOOTSELボタンを押しながらケーブルを接続することが必要のようだ。下記サイトなど、多数情報が見つかる。
検証
マルチコア未使用
まず、マルチコアを使用せず、逐次処理を行ったケース。
ソースコード
#define INTERVAL 2000
void setup() {
Serial.begin(9600);
Serial.setTimeout(INTERVAL);
}
void loop() {
char buf[16];
memset(buf, 0, sizeof(buf));
while (1) {
if ((Serial.readBytesUntil('\n', buf, sizeof(buf))) > 0) {
Serial.println(buf);
memset(buf, 0, sizeof(buf));
} else {
Serial.println(millis());
}
}
}
- シリアル入力のタイムアウトを2000msに設定
- シリアル入力があればそのまま出力
- シリアル入力がなければ起動からの経過時間(ms)を出力
結果
09:54:42.193 -> 102004
09:54:44.207 -> 104004
09:54:46.185 -> 106004
09:54:50.142 -> =====
09:54:52.155 -> 111951
09:54:54.130 -> 113951
09:54:56.141 -> 115951
09:54:58.151 -> 117951
09:55:00.129 -> 119951
09:55:03.230 -> @@@@
09:55:05.241 -> 125031
09:55:07.220 -> 127031
- "====="や"@@@@"はシリアル入力の折返し出力
- シリアル入力がなければ、2000msごとに表示あり
- シリアル入力があれば、その分の遅れ(時間経過)がわかる
至極まっとうな結果である。次に、これをデュアルコア化したソースコードで検証する。
デュアル(マルチ)コア使用
ソースコード
#define INTERVAL 2000
void setup() {
Serial.begin(9600);
Serial.setTimeout(INTERVAL);
}
void setup1() {
}
void loop() {
Serial.println(millis());
delay(INTERVAL);
}
void loop1() {
char buf[16];
memset(buf, 0, sizeof(buf));
if ((Serial.readBytesUntil('\n', buf, sizeof(buf))) > 0) {
Serial.println(buf);
}
}
RP2040のArduino IDE環境では、デュアルコアソースコードを簡単に記述できる。ネット上に各種情報あり。
- setup()およびloop()が1つ目のコア用処理
- setup1()およびloop1()が2つ目のコア用処理
- loop():定期的にシリアル出力(2000msごと)
- loop1():シリアル入力を折り返し出力
結果
09:56:46.250 -> 36006
09:56:48.231 -> 38006
09:56:50.243 -> 40006
09:56:50.836 -> =====
09:56:52.253 -> 42006
09:56:54.231 -> 44006
09:56:56.239 -> 46007
09:56:58.250 -> 48007
09:56:59.469 -> @@@@
09:57:00.261 -> 50007
09:57:02.239 -> 52007
- "====="や"@@@@"はシリアル入力の折返し出力
- シリアル入力の有無に関わらず、2000msごとに起動からの経過時間(ms)を出力、つまり、デュアルコアが機能していることがわかる。
おまけ:FreeRTOS
上記マルチコア用ソースコードとほぼ同じ内容を、シングルコア上のFreeRTOSで実現してみる。
ソースコード
#include <FreeRTOS.h>
#include <task.h>
#define INTERVAL 2000
TaskHandle_t xKbd, xOnlyPrt;
void TaskKeyBoard(void *arg) {
char buf[16];
for (;;) {
memset(buf, 0, sizeof(buf));
if ((Serial.readBytesUntil('\n', buf, sizeof(buf))) > 0) {
Serial.println(buf);
}
vTaskDelay(1);
}
}
void TaskOnlyPrt(void *arg) {
for (;;) {
Serial.println(millis());
vTaskDelay(INTERVAL); // For task switch
}
}
void setup() {
Serial.begin(9600);
Serial.setTimeout(INTERVAL);
xTaskCreate(TaskKeyBoard, "TaskKeyBoard", 128, NULL, 1, &xKbd);
xTaskCreate(TaskOnlyPrt, "TaskOnlyPrt", 128, NULL, 1, &xOnlyPrt);
//vTaskStartScheduler(); # 存在してはいけない
}
void loop() {
}
- TaskKeyBoard:キーボードから入力された内容を折返し出力するタスク
- TaskOnlyPrt:2000msごとに起動からの経過時間(ms)を出力するタスク
- RP2040では、vTaskStartScheduler()があると動作しないようだ
結果
20:39:23.914 -> 30019
20:39:25.922 -> 32020
20:39:27.703 -> =====
20:39:27.934 -> 34021
20:39:29.909 -> 36022
20:39:31.921 -> 38023
20:39:33.933 -> 40024
20:39:35.909 -> 42025
20:39:36.834 -> @@@@
20:39:37.920 -> 44026
20:39:39.932 -> 46027
20:39:41.943 -> 48028
- "====="や"@@@@"はシリアル入力の折返し出力
- キーボード入力タスクTaskKeyBoardのタスクスイッチ時間である1ms分(vTaskDelay(1))だけ、定期出力タスクTaskOnlyPrtの出力が遅れている(2001ms)
- ただし、1ms以上の遅れが見えたこともある、このあたりはマイコンの精度の話だろう
EOF