RP2040で簡単マルチコアコーディング(C言語)
RP2040はCPU(Cortex-M0+)を2つ内蔵しているデュアルコアのマイコンで、2つの異なるコードを並列に実行することができます。
使い方も簡単で以下のように専用関数の呼び出しで使用できます。
#include "pico/multicore.h"
void core1_entry() {
// Core1で動作させる処理
...
}
// main関数はCore0で動作する
int main() {
// Core1でcore1_entry関数を動作させる
multicore_launch_core1(core1_entry);
// 以下Core0で動作させる処理
...
}
"pico/multicore.h" を使用するために、『CMakeLists.txt』の『target_link_libraries』に『pico_multicore』を追記します。
・・・ # pico_multicore を追記 (『YourProjName』はプロジェクト名に変更してください) target_link_libraries(YourProjName pico_stdlib pico_multicore) ・・・
マルチコアプログラミングでは、複数のコアから同時に同じメモリや同じIO等にアクセスしないように排他処理が必要です。
ここでは semaphore_t を使ったセマフォによる排他処理を使用します。
#include "pico/multicore.h"
// 排他処理用のセマフォ
static semaphore_t sem;
void core1_entry() {
// Core1で動作させる処理
// セマフォから許可を要求。許可が得られるまで待機
sem_acquire_blocking(&sem);
// ここでCore0と共用するメモリ・IO等ハードウェアにアクセスする処理を記述
...
// 完了後、セマフォ解放。Core0を止めるのでなるべく早く解除する。
sem_release(&sem);
}
// main関数はCore0で動作する
int main() {
// セマフォを初期化
sem_init(&sem, 1, 1);
// Core1でcore1_entry関数を動作させる
multicore_launch_core1(core1_entry);
// 以下Core0で動作させる処理
...
// セマフォから許可を要求。許可が得られるまで待機する。
sem_acquire_blocking(&sem);
// ここでCore1と共用するメモリ・IO等ハードウェアにアクセスする処理を記述
...
// セマフォの許可を解除
sem_release(&sem);
}
次のコードでは片方のCPU(Core0)でLEDを点滅させ、もう片方のCPU(Core1)でその点滅速度を変更しています。
#include <stdio.h>
#include "pico/stdlib.h"
#include "pico/multicore.h"
// 排他処理用のセマフォ
static semaphore_t sem;
// Core0 と Core1 で共有する変数
static uint32_t share_sleep_time_ms = 0;
// Core1で動作させる処理
void core1_entry()
{
uint32_t core1_sleep_time_ms = 0;
while (true)
{
// セマフォから許可を要求。許可が得られるまで待機
sem_acquire_blocking(&sem);
// Core0 と Core1 で共有する変数に値を入れる
share_sleep_time_ms = core1_sleep_time_ms;
// セマフォの許可を解除
sem_release(&sem);
// LED点滅速度用の値を調整
core1_sleep_time_ms += 10;
if (core1_sleep_time_ms >= 5000) core1_sleep_time_ms = 0;
sleep_ms(core1_sleep_time_ms * 2);
}
}
// main関数はCore0で動作する
int main()
{
// ボード上のLEDを点灯させるためにGPIO25を初期化
uint32_t core0_sleep_time_ms;
const uint GPIO_LED = PICO_DEFAULT_LED_PIN;
gpio_init(GPIO_LED);
gpio_set_dir(GPIO_LED, GPIO_OUT);
// セマフォを初期化
sem_init(&sem, 1, 1);
// Core1でcore1_entry関数を動作させる
multicore_launch_core1(core1_entry);
while(true)
{
// セマフォから許可を要求。許可が得られるまで待機
sem_acquire_blocking(&sem);
// Core0 と Core1 で共有する変数から値を取得
core0_sleep_time_ms = share_sleep_time_ms;
// セマフォの許可を解除
sem_release(&sem);
// ボード上のLEDを点灯
gpio_put(GPIO_LED, true);
// Core1 で変更された時間分待機
sleep_ms(core0_sleep_time_ms);
// ボード上のLEDを消灯
gpio_put(GPIO_LED, false);
// Core1 で変更された時間分待機
sleep_ms(core0_sleep_time_ms);
}
}
上記コードを実行すると、ボード上のLEDの点滅速度が徐々に遅くなっていくのが確認できると思います。
マルチコアの実装を行う際には、2つのコア間で同一のメモリやデバイスに、同時にアクセスしてしまう事が無いように細心の注意が必要です。
コア間の通信には『multicore_fifo_pop_blocking()』や『multicore_fifo_pop_blocking()』といった専用関数が用意されていますが、ここではセマフォ(semaphore_t)を使用した排他処理を実施しています。
セマフォ(semaphore_t)を使用した排他処理は、『sem_acquire_blocking(&sem);』にて許可が得られたら『sem_release(&sem);』で解放するまで、他のコアでは『sem_acquire_blocking(&sem);』による許可が出ず、待機させられる仕組みで実現します。
この方法は少量のデータしか扱えない『multicore_fifo_pop_blocking()』と違い、コア間で大容量のデータを共有することが可能ですが、処理を止めてしまうデメリットがあるので可能な限り速く『sem_release(&sem);』で解放する必要があります。
セマフォを使用した排他処理の内部ではハードウェアスピンロックという機構が用いられており、Core0とCore1が同時に許可を求めた場合、Core0の方が許可が得られる仕組みになっているとの事です。
コメント
コメントを投稿