yukuro’s blog

ぽえむ日記

ESP32を触ってわかったこと

  • 今回、Esp32に触れる機会があったので色々調べた&試した結果を備忘録&ロステク防止のために書いておく

デュアルコア

  • Esp32はデュアルコアを搭載しており、FreeRTOSの一部のAPIが使える
  • Esp32でデュアルコアのタスクを生成させるにはxTaskCreatePinnedToCoreを宣言する必要がある
BaseType_t xTaskCreatePinnedToCore(
        TaskFunction_t pxTaskCode,          
        const char * const pcName,           
        const uint16_t usStackDepth,          
        void * const pvParameters,            
        UBaseType_t uxPriority,             
        TaskHandle_t * const pxCreatedTask,    
        const BaseType_t xCoreID               
    );
  • 各引数に関して
xTaskCreatePinnedToCore(task0, "Task0", 4096, NULL, 1, NULL, 0);
  1. pxTaskCode

    • タスクのポインタ
  2. pcName

    • タスク名。デバッグの時に使う
    • 最大長はconfigMAX_TASK_NAME_LENに定義されているが、デフォルトでは16。
  3. usStackDepth

    • スタックメモリサイズ。(byte数で指定)
    • 8192byteが推奨
  4. pvParameters

    • タスク関数に渡す引数
    • 多くのサイトではNULLとなっている
  5. uxPriority

    • タスクの優先度(1~25で指定)(1は最も高く、25が最も低い)
    • setup()とloop()は1になっている
  6. pxCreatedTask

    • タスクハンドルポインタ(作成したタスクを参照するためのハンドルを返す)
    • TaskHandle_t型の引数を定義して、そのアドレスを指定
  7. xCoreID

    • CPUのコア番号
    • Esp32-DevkitCの場合はデュアルコアなので0か1を指定する
    • CPU0ではシステムタスク(システムの基幹に関わるタスク)を行っている
  8. xTaskCreatePinnedToCoreを宣言した時点でそのタスクは始まるので、loop()を2つ置きたいとかの用途ならsetup()の最後に置くのがよさそう

vTaskDelay

  • vTaskDelayは指定チック数分だけそのタスクを遅延させ、他のタスクをその間に実行する
  • xTaskCreatePinnedToCoreuxPriorityで同じ優先度を指定するとそれらのタスクは交互に実行される
  • ある処理にかかるティック数は次の通りで確認できる
TickType_t starttick = xTaskGetTickCount(); 
/*
hogehoge
*/
TickType_t endtick = xTaskGetTickCount(); 
TickType_t executiontick = endtick - starttick;
  • 今回試してみたところ、ティック数は常に一定でもなかったので、↑のexecutiontick×2ぐらいの気持ちで指定するとよさそう
  • 尚、これをタスクに入れないとウォッチドッグタイマ(WDT)が介入できずcore~'s panicになってEsp32が再起動するので注意
  • なので遅延の必要が特にない場合はvTaskDelay(1)をそれぞれのタスクに入れておくとよさそう

GPIO

  • パっと見るとEsp32のGPIOは豊富にあるように見えるが、実際のところ色々な制約があり、思ったほど自由ではないというのが個人的な印象。
  • Esp32のGPIOのピンアサインについては以下のスイッチサイエンスさんの記事が詳しいかと思います
  • 個人的にハマったところをまとめていく
  • GPIO6 ~ 11は入力ピンとして使いずらい
    • GPIO6~11は内蔵Flashと接続されているらしく、Esp32の起動時にHighだと以下のようなエラーを吐いて起動しない cpp flash read err, 1000 ets_main.c 371 ...
  • GPIO0,2はブートモードの設定に使われるので使わない

UART

  • Esp32にはUART0~2の3つのUARTがある
  • UART0はPCとのシリアル通信に使われる
  • UART1は(RX,TX = 9,10)、UART2は(RX,TX = 16,17)がデフォルトで使用可能だが、WROOM32ではUART1のデフォルトが使用不可なため、以下のようにする必要がある。
HardwareSerial HOGEHOGE(1);
HOGEHOGE.begin(115200, SERIAL_8N1, 18, 19);
  • 注意
    • UART1を使用する場合、HardwareSerial HOGEHOGE(1)か、Serial1のように宣言する必要がある。
    • HardwareSerial Serial1と宣言すると cpp error: conflicting declaration 'HardwareSerial Serial1' とのエラーを吐かれる

Tips

センサ値をコア間で共有する

  • センサ値をコア間で共有して何かの処理を行いたいとき、2つの方策が考えられる
    1. センサ値をグローバル変数にする
      • センサ値が連続で続く場合、一つの値しか共有できないのであまり使いたくない
    2. センサ値をキューに格納して各コアでキューからセンサ値を出す
      • この方法なら連続的なデータでも取得できる
/* setup内 */
QueueHandle_t queue_magnitude;
queue_magnitude = xQueueCreate(512, sizeof(double));

/* CPU0(センサ値取得側) */
double magnitude;
BaseType_t data_magnitude = xQueueSend(queue_magnitude, &magnitude, 0);

/* CPU1(センサ値を使って何かする側) */
double magnitude;
BaseType_t data_magnitude = xQueueReceive(queue_magnitude, &magnitude, 0);

参考文献