
タスクの一時停止(suspend)と再開(resume)を実験しようとしたのですが、勝手な割り込み端子への思い込みに足をすくわれました。Arduino UNO R4は32ビット機になったわけだし、その辺の端子はみな割り込みに使えるんじゃね?大間違い!Arduino UNOの伝統にのっとり、2番、3番だけなのね。律儀。
※「モダンOSのお砂場」投稿順Indexはこちら
※Arduino IDE上で「スケッチ」形式のソースからFreeRTOSを使って実験してみてます。ターゲット機はArduino UNO R4 Minima。泣く子も黙る?Armコアのルネサスマイコン搭載機です。
FreeRTOSのタスクステート
FreeRTOSのタスクステートは忘却力の年寄にも覚えやすいシンプルな4ステートです。実行中のRunning、実行可能だけれどまだCPUの割り当てがないReady、ブロッキングされるAPIを使って「何かを待っている」Blocked、そして一時停止中のSuspendedです。詳しいことは御本家の以下ドキュメントなどご参照くだされ。
さて今回練習してみるのは、Taskの一時停止と再開です。使用するAPIは2つだけ。どちらも引数はタスクハンドルのみのシンプルさです。
-
- vTaskSuspend()、Running, Ready, Blockedのどの状態からでもSuspendedへ遷移させる
- vTaskTaskResume()、Suspended状態からReady状態に遷移させる
これを使えばTaskの一時停止と再開など思うがままだと。知らんけど。
UNO R4 attachinterrupt
例によって3つのTaskを走らせておいて、4つめのTaskから前の3つのうちの一つを一時停止させたり、再開させたりしようということにいたしました。実際にAPIを発行するのはタスクからなのですが、停止、再開の切っ掛けは割り込みハンドラで作ることにいたしました。要はボタンを1回押したら最初のタスクが止まり、2回目で最初が再開、2つ目が停止、3回目で2つ目再開、3つ目停止、4回目で全部動作で最初に戻るってな塩梅です。
FreeRTOSといってもArduino環境です。外部端子の割り込みハンドラは以下の関数でOK。
attachInterrupt()
その筈が勝手な思い込みでちと手間取りました。「UNO R4は32ビット機だし、その辺のデジタルピンは皆割り込みに使えるんじゃね」嘘です。UNO R4でもattachInterrupt()できるのは以下の2端子に限られてました。
D2, D3のみ
なんだ8ビットのUNO R3と一緒なのね。勝手な思い込みを正すべく、以下のチートシートを読み直しましたです。
Arduino UNO R4 Minima Cheat Sheet
そして、UNO R4の端子機能を反映した図面を起こしました。今回の実験回路が以下に。
今回実験のソース
タスクは4つですが、3つが suspend/resumeの対象です。それぞれ「働いている」ところを見せるため、赤、青、緑のLEDのうち1色を担当して、それぞれ別々の周期で点滅(ほとんどの時間点灯していて、時々消灯する)するようにしてあります。
4つのタスクとは別に割り込みハンドラがD3端子のフォーリング・エッジに反応するようになっており、この中で0から3の数字をsuspendedという大域変数に格納してます。割り込みハンドラの仕事はこれだけです。
4個目のタスクはクルクル回りつつ、上記のsuspended変数に変化が現れたら、それに応じたTASKを停止させ、suspendedから外れたTASKを再開させます。0なら0番タスク停止という塩梅。3の場合は全部稼働状態。
#include <Arduino_FreeRTOS.h>
#define NTASKS (3)
#define LED_RED (7)
#define LED_BLUE (6)
#define LED_GREEN (5)
#define BUTTON (3)
#define LOOP_W (100)
#define LED_W (200)
#define PRIOBASE (1)
TaskHandle_t loop_task;
TaskHandle_t tasks[NTASKS] = {NULL, NULL, NULL};
const char taskname[NTASKS][6] = {"Task1", "Task2", "Task3"};
pin_size_t taskParam[NTASKS] = {LED_RED, LED_BLUE, LED_GREEN};
volatile int suspended;
void initPins() {
pinMode(LED_RED, OUTPUT);
digitalWrite(LED_RED, 0);
pinMode(LED_BLUE, OUTPUT);
digitalWrite(LED_BLUE, 0);
pinMode(LED_GREEN, OUTPUT);
digitalWrite(LED_GREEN, 0);
}
void loop_thread_func(void *pvParameters)
{
int oldsuspended = suspended;
while (1) {
vTaskDelay(LOOP_W);
if (oldsuspended != suspended) {
Serial.print("OLD: ");
Serial.print(oldsuspended);
Serial.print(" NEW: ");
Serial.println(suspended);
if (oldsuspended < 3) {
vTaskResume(tasks[oldsuspended]);
}
if (suspended < 3) {
vTaskSuspend(tasks[suspended]);
}
oldsuspended = suspended;
}
}
}
void task_func(void *pvParameters)
{
TickType_t xLastWakeTime;
pin_size_t pinNumber = *(pin_size_t *)pvParameters;
const TickType_t xTimeInc = pinNumber * LED_W;
while (1) {
vTaskDelay(xTimeInc);
digitalWrite(pinNumber, 0);
vTaskDelay(LED_W);
digitalWrite(pinNumber, 1);
}
}
void selTask() {
suspended = suspended < NTASKS ? suspended + 1 : 0;
}
void setup()
{
Serial.begin(115200);
while (!Serial) { }
initPins();
pinMode(BUTTON, INPUT_PULLUP);
suspended = NTASKS;
attachInterrupt(digitalPinToInterrupt(BUTTON), selTask, FALLING);
auto const rc_loop = xTaskCreate (
loop_thread_func, static_cast<const char*>("Loop Thread"), 512 / 4, nullptr, PRIOBASE, &loop_task
);
if (rc_loop != pdPASS) {
Serial.println("Failed to create 'loop' thread");
return;
}
TaskHandle_t xHandle = NULL;
BaseType_t xReturned;
for (int i=0; i<NTASKS; i++) {
xReturned = xTaskCreate ( task_func, taskname[i], 512 / 4, (void *)&taskParam[i], PRIOBASE, &xHandle );
if (xReturned != pdPASS) {
Serial.println("Failed to create task.");
return;
} else {
tasks[i] = xHandle;
}
}
Serial.println("Starting scheduler ...");
vTaskStartScheduler();
for( ;; ); /* Never! */
}
/* NEVER CALLED! */
void loop() {
}
実機動作確認
Arduino IDEでビルドした上記の「スケッチ」をArduino UNO R4 Minima機に書き込んで走らせたときの、シリアルモニタの状況が以下に。
ボタンを押す度に、OLDとNEWでresumeするタスクとsuspendされるタスクが報告されます。OLDは suspended状態だったタスク番号(3は停止中はなし)、NEWは新たにsuspendされるタスク番号。0は赤色LEDをつかさどるタスク、1は青で、2が緑っと。
箱の奥底から発掘した実験用の治具をUNO R4に接続して実験しております。タスクがsuspendされていないときは、それぞれの周期で時折LEDが消灯しますが、suspendされるとLEDは点灯状態または消灯状態のままフリーズします。
停止も再開も思うがまま。ホントか?

