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の方が許可が得られる仕組みになっているとの事です。
コメント
コメントを投稿