
前回はタスク・リストを一覧して何故か満足してしまいました。今回は、割り込みとタスクの間の通信を試みてみたいと思います。割り込みサービスルーチン側では、処理の依頼をするだけで、時間のかかる処理はタスクにお任せするという仕組み。とりあえず動くプロトタイプを作ってみました。
※「モダンOSのお砂場」投稿順Indexはこちら
※末尾に実験に使用したコード全文を掲げました。
※実験には、Seeed Studio社製 Seeeduino Xiaoボード(Microchip製 ATSAMD21マイコン<Arm Cortex-M0+>搭載)を使用しています。PC上のVSCode上のPlatformIOを使用させていただき、Arduinoフレームワークでビルドを行っています。
QUEUE
今回、使用するのは Queue という構造です。以前に以下の投稿で取り扱ったことがあります。
モダンOSのお砂場(20) FreeRTOS、キュー構造。でも別件が気になってしまうっ!
根本的な差異はないのですが、上記ではタスク間にQueueを張っていました。今回は、割り込みが絡んでくるのでチトAPIが異なってまいります。使用するAPIのマニュアルページは以下です。
今回のサンプルコードの目論見
今回作成したプログラムの動作は以下のとおりです。
-
- キーLとキーRがある。
- キーLを押すとカウントアップ、キーRを押すとカウントダウンするカウンタがある
- カウンタが増減する度にその値をコンソールに出力する。またカウント処理中LEDを点灯する。
FreeRTOSを使っての処理は以下の図のとおりです。

2つのキーにそれぞれ対応する割り込みハンドラがあり、その中でキューにカウントUP/DOWNすべき数を書き込んでいく。基本周期100msで動作している定期タスクRed Taskが、Queueに書き込まれている数を読み取って実際にカウントUP/DOWNを行い、コンソール出力、LED点滅を行う。Queueに数が書き込まれていた場合、10ms後にもQueueに値が溜まっていないか確認する。溜まっている場合は処理する。
難しいことはなんもありゃしませんが、しかし、今回手を抜いている部分おおありです。
-
- キーは人間が押す前提なので、Queue(8段確保)があふれるような頻度での動作は想定していない。80ms中に9回のキー入力など無いだろ~という手抜き。
- 割り込みハンドラからキューイングするときに、タスクが待機状態であった場合、「プライオリティを上げてたたき起こす」機能があるが使用していない。
実験に使用した回路
以下に、実験に使用した回路図を示します。以前から使用していた赤、青のLEDが左に、今回とりつけたプッシュスイッチ2個が右という簡単な構成です。
スタック・オーバーフロー発生
今回のコードも前回使用のコードを改造して作ったのですが、RAMに制約(32KB)のあるATSAMD21G18では、ちょっとコードを増やすとエラーが起こります。こんな感じ。
メモリ量に制限がでやすい「組み込みらしい」挙動なので、スタックの割り当て量などを調整する「事案」かとも思ったのです。でもタイマサービスは今回使わなくても済むので削除して対応してしまいました(本当はメンドイから。)
ただし、一度このようなエラーに落ち込むと更新したオブジェクトコードをUSB経由で書き込めなくなります。その時は、Seeeduino XiaoボートのUSBソケットの脇にある小さなRSTパッドを素早く2回短絡して(勿論導電性のピンなどで)ハングしているアプリコードから制御を奪う必要があります。結構、何度もやっているのでツツクのにも慣れました。
実行結果
スタック・オーバーフロー件を対処後は問題なく動作しました。そのときの標準出力に現れた結果が以下に。Lボタンを押すとカウントが上がり、Rボタンを押すとカウントが下がります。一応、意図どおりの動作だね。
ATSAMD21 FreeRTOS INTR+QUEUE test. 001 Start FreeRTOS Scheduler. UpDownCounter: 1 UpDownCounter: 2 UpDownCounter: 1 UpDownCounter: 2 UpDownCounter: 3 UpDownCounter: 4 UpDownCounter: 5 UpDownCounter: 6 UpDownCounter: 5 UpDownCounter: 4 UpDownCounter: 3 UpDownCounter: 2 UpDownCounter: 1 UpDownCounter: 0 UpDownCounter: -1
一応、割り込みからのキューイングが動作しているので叩き台くらいにはなるか。次は?
モダンOSのお砂場(31) FreeRTOS、タスク・リストを一覧する へ戻る
モダンOSのお砂場(33) Mbed OS6、bare metal profile、Lチカ へ進む
実験に使用したソース全文
#include <Seeed_Arduino_FreeRTOS.h>
// D2/A2=PA10, D3/A3=PA11, D10/A10=PA6
// D7/A7=PB9(EINT9), D8/A8=PA7(EINT7)
#define LED_RED (2)
#define LED_BLUE (3)
#define KEY_L (8)
#define KEY_R (7)
#define LED_ACTIVE (0x0)
#define LED_INACTIVE (0x1)
#define STACK_DEPTH (256)
#define ENABLE_NOP_LOOP (0)
#define QUEUE_DEPTH (8)
volatile int upDownCounter;
int counter;
int blueFlag;
TaskHandle_t Handle_RedTask;
QueueHandle_t isrQueue;
void waitLoop(int ms) {
#if ENABLE_NOP_LOOP > 0
vNopDelayMS(ms);
#else
vTaskDelay(ms / portTICK_RATE_MS);
#endif
}
static void RedTask(void * pvParameters) {
int item;
while (1) {
item = 0;
if (xQueueReceive(isrQueue, (void*)&item, 0) == pdTRUE) {
upDownCounter += item;
Serial.printf("UpDownCounter: %d\r\n", upDownCounter);
digitalWrite(LED_RED, LED_ACTIVE);
waitLoop(10);
} else {}
digitalWrite(LED_RED, LED_INACTIVE);
waitLoop(100);
}
}
void keyLhandler() {
int sendData = 1;
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
xQueueSendToBackFromISR(isrQueue, &sendData, &xHigherPriorityTaskWoken);
}
void keyRhandler() {
int sendData = -1;
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
xQueueSendToBackFromISR(isrQueue, &sendData, &xHigherPriorityTaskWoken);
}
void setupLEDpins() {
pinMode(LED_RED, OUTPUT);
pinMode(LED_BLUE, OUTPUT);
digitalWrite(LED_RED, LED_INACTIVE);
digitalWrite(LED_BLUE, LED_INACTIVE);
}
void setupINTR() {
pinMode(KEY_L, INPUT);
pinMode(KEY_R, INPUT);
attachInterrupt(digitalPinToInterrupt(KEY_L), keyLhandler, FALLING);
attachInterrupt(digitalPinToInterrupt(KEY_R), keyRhandler, FALLING);
}
void setup() {
Serial.begin(9600);
vNopDelayMS(1000);
while(!Serial);
Serial.printf("ATSAMD21 FreeRTOS INTR+QUEUE test. 001\r\n");
setupLEDpins();
setupINTR();
isrQueue = xQueueCreate(QUEUE_DEPTH, sizeof(int));
counter = 0;
upDownCounter = 0;
blueFlag = 0;
xTaskCreate(RedTask, "RED", STACK_DEPTH, NULL, tskIDLE_PRIORITY + 3, &Handle_RedTask);
Serial.printf("Start FreeRTOS Scheduler.\r\n");
vTaskStartScheduler();
}
// NEVER CALLED, if scheduler OK.
void loop() {
while(1) {
Serial.printf("NEVER loop: %d\r\n",counter++);
delay(1000);
}
}


