モダンOSのお砂場(31) FreeRTOS、タスク・リストを一覧する

Joseph Halfmoon

ここ数回、Seeeduino Xiaoボード(Microchip製のArmコア内蔵SAMD21マイコン搭載)上でFreeRTOSのタスクを作ってその動作を観察しています。タスク数は少ないのだけれどもタスクの一覧みたいなものが見たくなってきました。そのためのAPIがありました。組み込み用のOSなので、デバッグするとき向けの機能ですが。

(末尾に実験に使用したソース全文を掲げました。Windows上のVSCode+PlatformIOを使用し、Arduinoプラットフォームでビルドしています。)

今回使用のAPIのマニュアル

幾つか似た機能のAPIが存在するのですが、今回使用した一番「楽」そうなAPIのFreeRTOSのマニュアルページは以下に。

Kernel > API REference > Task Utilities > vTaskList

何が楽かといえば、いろいろ調べるAPIを呼び出して情報を得た後、人間可読なフォーマットの文字列を返してくれるのです。出力先の文字列領域を引数に与えて vTaskList() 関数を呼び出せばOK。情報を使ってなにか操作したいときはよりローレベルのAPIを呼んでね、という感じですかい。

なお、この関数はFreeRTOSの各種コンフィギュレーションを決めている FreeRTOSConfig.h ヘッダ内の以下のマクロの値が「1」になっていないと使えませぬ。

  • configUSE_TRACE_FACILITY
  • configUSE_STATS_FORMATTING_FUNCTIONS

framework-arduino-samd-seeed 内のFreeRTOSのフォルダを確認してみると、デフォルトで 1 になってました。ということは使えるということね。

また注意点として、こいつが走っている間は割り込み禁止になるみたいです。実機でホントに何か制御しているときには使うなよ、デバッグ用だぜ、という感じ。

前回からのソースの修正

前回のソースに加えた修正点は以下です(全文は末尾に。)

  1. マクロ定数 MONITOR_ITVL を加えた。何msに1回タスクリストを表示するか制御する定数
  2. 上記のインターバルでタスクリストを出力するタスク ”MON”、実体関数はMonitorTask()を加えた。

結果は、Serial.printf()で、シリアル端末に出力しています。大した修正ではないので珍しく順調にビルド完了して動作もOK。

実行結果

以下に実行結果、最初の3回分、を示します。フィールドは5つあり左から、

  1. タスクのお名前、タスク生成時に与える文字列(アイドルとタイマはシステム側で定義)
  2. タスクのステータス文字
  3. タスクの現在プライオリティ
  4. タスクのスタックの「ウオーターマーク」
  5. タスクのID番号

です。

vTaskListResult
第2欄のタスクのステータスですが、上記のAPIマニュアル見ると、B, R, D, Sの4種類しか書いてなくて、MONタスクのXについて記載がないです。ソースを調べると以下のようでした。

  • X:RUNNING
  • R:READY
  • B:BLOCKED
  • S:SUSPENDED
  • D:DELETED

MONタスクの中で vTaskList()関数が走っているのでXなわけです。

またプライオリティは、タスクをCreateするときにIDLEタスクのプライオリティにプラスする形で決めているとおりになってます。ただし、タイマサービスの2は自分で設定した記憶が無いデス。どこで決めてる?

四番目のフィールドの「ウオーターマーク」については、ソース(task.h)のコメントから1か所引用させていただきます。

The closer this value is to zero the closer the task has come to overflowing its stack.

 

ということで、0に向かって少なくなっていき、0になったらオーバーフローということみたいです。危ない、危ない。なお「ウオーターマーク(透かしの意味ではなく、本来の量水標の意味だと思う)」ということでタスクが生成されてから今までで、一番高かった水位(数字的には小さい)を記録しているみたいです。現在値ではないようです。

単にタスクの一覧表を出力しただけなのだけれど、妙に納得してしまう自分。それでよいのか。その程度の頭だよ。

モダンOSのお砂場(30) FreeRTOS、タイマAPI対vTaskDelay へ戻る

モダンOSのお砂場(32) FreeRTOS、割り込みハンドラからタスクにキュー へ進む

実験に使用したソース全文
#include <Seeed_Arduino_FreeRTOS.h>

// D2/A2=PA10, D3/A3=PA11, D10/A10=PA6
#define LED_RED   (2)
#define LED_BLUE  (3)
#define LED_ACTIVE    (0x0)
#define LED_INACTIVE  (0x1)
#define STACK_DEPTH (256)
#define ENABLE_NOP_LOOP (0)
#define TEST_PIN  (10)
#define BLUE_TIMER_TICK  (10)
#define SOFTWAIT  (0)
#define MONITOR_ITVL  (10000)

int counter;
int blueFlag;
TaskHandle_t  Handle_MonitorTask;
TaskHandle_t  Handle_RedTask;
TimerHandle_t Handle_BlueTask;
char pBUF[400];

void waitLoop(int ms) {
  #if ENABLE_NOP_LOOP > 0
    vNopDelayMS(ms);
  #else
    vTaskDelay(ms / portTICK_RATE_MS);
  #endif
}

static void MonitorTask(void * pvParameters) {
  while(1) {
    vTaskList(pBUF);
    Serial.printf("%s\r\n",pBUF);
    vTaskDelay(MONITOR_ITVL / portTICK_RATE_MS);
  }
}

static void RedTask(void * pvParameters) {
    while (1) {
    digitalWrite(LED_RED, LED_ACTIVE);
    waitLoop(10);
    digitalWrite(LED_RED, LED_INACTIVE);
    waitLoop(10);
    #if SOFTWAIT == 1
      vNopDelayMS(1);
    #endif
  }
}

static void BlueTask(TimerHandle_t xTimer) {
  if (blueFlag == 0) {
    digitalWrite(LED_BLUE, LED_ACTIVE);
    blueFlag = 1;
  } else {
    digitalWrite(LED_BLUE, LED_INACTIVE);
    blueFlag = 0;
  }
  #if SOFTWAIT == 1
    vNopDelayMS(1);
  #endif
}

void setup() {
  Serial.begin(9600);
  vNopDelayMS(1000);
  while(!Serial);
  Serial.printf("ATSAMD21 FreeRTOS task list test. 001\r\n");
  pinMode(LED_RED, OUTPUT);
  pinMode(LED_BLUE, OUTPUT);
  digitalWrite(LED_RED, LED_INACTIVE);
  digitalWrite(LED_BLUE, LED_INACTIVE);
  counter = 0;
  xTaskCreate(MonitorTask, "MON", STACK_DEPTH, NULL, tskIDLE_PRIORITY + 3, &Handle_MonitorTask);
  xTaskCreate(RedTask, "RED", STACK_DEPTH, NULL, tskIDLE_PRIORITY + 2, &Handle_RedTask);
  blueFlag = 0;
  Handle_BlueTask = xTimerCreate("BlueTimer", BLUE_TIMER_TICK, pdTRUE, (void *)0, BlueTask);
  xTimerStart(Handle_BlueTask, 0);
  Serial.printf("Start FreeRTOS Scheduler.\r\n");
  vTaskStartScheduler();
}

void loop() {
    while(1) {
        printf("Counter: %d\n",counter);
        digitalWrite(TEST_PIN, 1);
        delay(100);
        digitalWrite(TEST_PIN, 0);
        delay(1000);
    }
}