ESP32 FreeRTOS CriticalSectionについて

ESP32 ならArduinoを使っている方が多いと思う。FreeRTOSをつかっていると幾度となく、ここでタスクのディスパッチが発生して欲しくないなあ、と思うことがあるはず。

で、簡単に思いつくのが割り込み禁止!

Disable Interrupt 的なCであれC++であれアセンブリであれ、ええい止めちゃえ、って方法ですね。これをFreeRTOSで実現するには・・・

portENTER_CRITICAL(mux)
portEXIT_CRITICAL(mux)

が出てきます。
調べてみると、マクロで

#define taskENTER_CRITICAL(mux)  portENTER_CRITICAL(mux)
#define taskEXIT_CRITICAL(mux)  portEXIT_CRITICAL(mux)
#define taskENTER_CRITICAL_ISR(mux)  portENTER_CRITICAL_ISR(mux)
#define taskEXIT_CRITICAL_ISR(mux)  portEXIT_CRITICAL_ISR(mux)

となっていて、FreeRTOS的な方言のようですね。

ネットで多く説明で使われているのは、 Serial.print() を クリティカルセクションで挟み込む例題。例えば以下のようなもの。

portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED;
taskENTER_CRITICAL( &mux ) ;
Serial.println( "Task1 ⁻ Hel" );
Serial.println( "lo world." );
taskEXIT_CRITICAL( &mux );

で "Hello world." が途中で途切れないことを確認するようなもの。
でもそもそもprint文はprintf 程ではないにしても大きな関数なので実行時間も長い。
なので、長い文字列や込み入った時間のかかる処理をtaskENTER_CRITICAL( &mux ) ;で挟むと Gru panic を起こしてESP32がリセットしてしまうことがある。
つまりディスパッチ禁止というより、FreeRTOS外でCPUそのものの割り込みが禁止されてしまっているように見える。
いづれにしても、サンプルとしては分かりやすくて良いのだが、実際のコードではprint文で使うのは賢明ではないように思える。
デバッグにしても、他の機器との通信であれ、print outしたいときは、一つのタスクに一つの出力先をアサインして、デバイスレベルで排他処理しなくても、変数への書き込みだけ排他処理されていれば、空き時間に外部デバイスへ随時 print していくような構成にした方が精神衛生上もよさそうだ。


 volatile  uint64_t  val;

 taskENTER_CRITICAL( &mux );
 val |= 0xff00;
 taskEXIT_CRITICAL( &mux );

volatile の変数を触るときは逆に絶対に守らないと行けない場合が多いと思うし、こここそ使う場所かと思う。