モダンOSのお砂場(62) UNO R4、ルネサスRA4M1でFreeRTOS、タスク生成

Joseph Halfmoon

Arduino UNO R4登場。ルネサスのArmマイコン搭載。32ビット機なのでArduino IDE環境を使いつつ、FreeRTOSベースで使ってみようかと思います。別シリーズでFreeRTOSでLチカのサンプルコードを動かしているので、今回はその構造のおさらいから。まずはタスク2つ作ってダブルLチカ。ありがち。

※「モダンOSのお砂場」投稿順Indexはこちら

Arduino UNO R4

Arduino UNO R3まではMicroChip社のAVRマイコンでしたが、R4からはマイコンがルネサスR4AM1になりました。コアはCortex-M4コアのArmです。「お求めやすい方」のArduino UNO R4 MINIMAという機種(WiFiが載ってない)を購入してまずはLチカしてみたのが以下の別シリーズ記事であります。

鳥なき里のマイコン屋(172) ルネサスMCU、Arduino UNO R4で吉例Lチカ

Arduino IDE上のFreeRTOS

1年以上前に、Arduino IDE上でもFreeRTOSを使って練習してます。そのときはMicroChip社のArmコアマイコン、SAMD21シリーズを使ってでした。

モダンOSのお砂場(28) FreeRTOS、スケジューラの起動、SAMD21

既に一度使っているので分かっている筈、と思いきや、忘却力の年寄です。既にサッパリと忘れてます。まずはFreeRTOSのタスク生成APIのマニュアルが以下に。

xTaskCreate

幸い別件にてFreeRTOSのサンプルプログラムを動かしているので、それを参考にArduino環境でのFreeRTOSの利用をどうしたら良いかポイントだけおさらいしていきたいと思います。段取り的にはこんな感じでないかと。

    1. Arduino_FreeRTOS.hをインクルードしておく
    2. TaskHandle_t型でタスクハンドルを生成するタスクの数だけ宣言しておく
    3. Arduino伝統の setup() 関数内で  xTaskCreate関数を使い「並行動作させる」タスクをFreeRTOSのスケジューラに登録する
    4. setup()関数の末尾でvTaskStartScheduler関数を使ってFreeRTOSのスケジューラを起動する(起動すると正常に実行できればそこから帰ってくることはない)
    5. Arduino伝統のloop()関数はそのままでは決して呼ばれないので、もし必要であれば、loop()関数を呼び出すためのタスクを作って、その中でloop()関数を呼び出すようにする。

上記の手順を図にしたものが以下に。FreeRTOS_Arduino

ダブルLチカのコード

上記の図解?にそって処理を並べたコードが以下に。タスクの割り当ては以下のようです。

    1. loop_task、「伝統の」loop()関数を呼び出すだけのTask。loop()関数は1000ミリ秒に1回起動され、ミリ秒タイマの値をUSBシリアルへ出力する。
    2. task1、端子0番を使ってLチカする。500ミリ秒で点滅反転。
    3. task2、端子1番を使ってLチカする。200ミリ秒で点滅反転。

なお、configTICK_RATE_HZの値は1000です。

#include <Arduino_FreeRTOS.h>
TaskHandle_t loop_task, task1, task2;

void setup()
{
  Serial.begin(115200);
  while (!Serial) { }
  auto const rc_loop = xTaskCreate (
      loop_thread_func, static_cast<const char*>("Loop Thread"), 512 / 4, nullptr, 1, &loop_task
    );
  if (rc_loop != pdPASS) {
    Serial.println("Failed to create 'loop' thread");
    return;
  }
  auto const rc_task1 = xTaskCreate (
      task1_func, static_cast<const char*>("Task1"), 512 / 4, nullptr, 1, &task1
    );
  if (rc_task1 != pdPASS) {
    Serial.println("Failed to create 'task1' thread");
    return;
  }
  auto const rc_task2 = xTaskCreate (
      task2_func, static_cast<const char*>("Task2"), 512 / 4, nullptr, 1, &task2
    );
  if (rc_task2 != pdPASS) {
    Serial.println("Failed to create 'task2' thread");
    return;
  }
  Serial.println("Starting scheduler ...");
  vTaskStartScheduler();
  for( ;; ); /* Never! */
}

void loop()
{
  Serial.println(millis());
  vTaskDelay(configTICK_RATE_HZ);
}

void loop_thread_func(void *pvParameters)
{
  for(;;)
  {
    loop();
    taskYIELD();
  }
}

void task1_func(void *pvParameters)
{
  pinMode(0, OUTPUT);
  digitalWrite(0, LOW);

  for(;;)
  {
    digitalWrite(0, !digitalRead(0));
    vTaskDelay(configTICK_RATE_HZ/2);
  }
}

void task2_func(void *pvParameters)
{
  pinMode(1, OUTPUT);
  digitalWrite(1, LOW);

  for(;;)
  {
    digitalWrite(1, !digitalRead(1));
    vTaskDelay(configTICK_RATE_HZ/5);
  }
}
実機動作結果

上記をビルド後、Arduino UNO R4に書き込みました。まずはシリアルモニタに出力されるミリ秒タイマの値から。ぴたり1000ミリ秒間隔で loop()関数が動作していることが分かります。loopTask

0番端子に接続した赤色LED、1番端子に接続した緑色LEDもそれぞれの周期で点滅しとります。UNO_R4_DUAL_THREADS

FreeRTOSでのタスクの生成、予定通りのようだね。

モダンOSのお砂場(61) Keil Studio Cloudへの自動変換、赤でもビルドでき に戻る

モダンOSのお砂場(64) UNO R4、ルネサスRA4M1でFreeRTOS、OSタイマ へ進む