ラズピコのコードを書いててちょっと困ったことに
ラズピコでシリアル・バスを監視するコード書いてみてる.信号を入力したピンをポーリングで監視するようにしてるんだけど,まぁだいたい動いてる.しかし..
CORE0だけで動かした場合
処理ループの中に下のようなコードを仕込んでバイタル・サインをピンに出力.これでCPUの動きを見てみる
pinMode(VITAL_OUTPUT_PIN, OUTPUT);
int toggle = 0;
int count = 0;
while (1) {
// この場所で本来やらせる処理(入力ピンの監視とイベントの記録)をしてるのだけど省略
if (!(count++ & 0xF))
gpio_put(VITAL_OUTPUT_PIN, (toggle = !toggle));
}
}
そうするとこんな感じなんだな.1msに十数マイクロ秒の隙間が空く.これのおかげで,この間のポーリングが止まってしまい取りこぼしがでる.
下の図のD2チャンネル(CORE0_vital_sign)で上記ピンの出力をみてるのだけど,トリガ後の0.05ms,さらに1.05ms付近に停止期間が見れる.
この隙間ができるのは,デフォルトで設定されてしまってる1ms毎のタイマー割り込みのせいみたい.試しに全部の割り込みを停止させてみた.割り込み停止はsave_and_disable_interrupts()
をコールする.(ここを参考にPico SDKのAPIコールを使ったのだけど,あとで試してみたらCMSISの__disable_irq()
も使えた)
しかし問題が..
割り込みを禁止すると,開発環境から認識できなくなって,Arduino IDEからの実行コードの書き込みができず,シリアル出力も得られない ┐(´~`)┌
問題回避をCORE1で
ラズピコってCPUコアが2個入ってる.しかしこれまで1個しか使ってこなかった.だってその必要がなかったから.
でもここにきて「ひょっとしてもう一個のコアも使えばいいんぢゃね?」って思って調べてみたら,なんか超簡単に使えるみたい.
マネして書いてみたらあっさりいごいた(´(ェ)`)
これまでの処理をcore1_main()
って関数にまとめ直して,下のようなコンパイルオプションを用意した.まず#if 1
でこれまで通り動くか試してから,#if 0
でCORE1での実行に変えてみる.
#if 0
core1_main();
#else
multicore_launch_core1(core1_main);
#endif
CORE0側のループにもバイタル・サイン出力を入れておいて,それぞれの状態を見たのがこれ↓↓
CORE0の出力はD3(オレンジ:CORE0_vital_sign)に,CORE1出力はD2(赤:CORE1_vital_sign)に.
割り込みはCORE0には居るけどCORE1には影響してないのがわかる.
CORE0はカウンタを回すだけのループなんで周波数が高い.
CORE1は入力の監視をしてるのでその分だけ周波数は低く,信号状態の変化があるとそれを記録するための処理を行うので,さらに周波数が下がる.
まとめ
これまでは必要もないし,ややこしそうだから手を出してなかったマルチコア使用.
あっけないほど簡単だった.
この例では処理を単純に別コアに移しただけなので,何もややこしいことは起こらなかった.
ここまでできたら,メモリ共有とかをしながら結構遊べそうな気がするけど,先に「お作法」的なのを調べておかないといけないな.