マリオカートハッキングが流行っていて、久しぶりにSTM32のことを思い出しました。
STM32で大規模プログラムを書くために必要なFreeRTOSを、まずはArduinoで勉強してみます。
FreeRTOSとは
オープンソースの組み込み用リアルタイムOS。
非常に軽量(6K~12KB)であることと、複数のマイコン(Arm、AVR(Arduino)、PICなど)に対応していることが特徴的。
2017年にAmazonに買収されているため、AWSとの密な連携が可能となる。
FreeRTOSの公式ドキュメント (https://docs.aws.amazon.com/ja_jp/freertos/latest/userguide/what-is-freertos.html) には、アーキテクチャとして以下の図が掲載されている。MQTTやBLEなどの通信ライブラリやAWS関連の機能があるのが心踊る。
(※ただしこれはFreeRTOSの全体図であり、Arduinoで使えるのはおそらくFreeRTOS Kernelの部分だけ(?)だと思われる。要調査。)
ArduinoにFreeRTOSをインストールして遊ぶ
インストールする方法
こちらの記事 (https://qiita.com/infinite1oop/items/aac04a70c1ed71cb83cc) を参考に、ライブラリマネージャからFreeRTOSをインストール。簡単。FreeRTOSライブラリのインストールが完了すると、Arduinoのファイル>スケッチ例からFreeRTOSのサンプルプログラムが選択できるようになる。
マルチタスクをやってみる
2つのタスクを約1秒おきに実行するプログラム
シリアル出力で「A」と表示する<タスクA>と「B」と表示する<タスクB>だけのプログラムを作った。
#include <Arduino_FreeRTOS.h>
void TaskA( void *pvParameters );
void TaskB( void *pvParameters );
// the setup function runs once when you press reset or power the board
void setup() {
// initialize serial communication at 9600 bits per second:
Serial.begin(9600);
while (!Serial) {
; // wait for serial port to connect. Needed for native USB, on LEONARDO, MICRO, YUN, and other 32u4 based boards.
}
// Now set up two tasks to run independently.
xTaskCreate(
TaskA
, "TaskA" // A name just for humans
, 128 // This stack size can be checked & adjusted by reading the Stack Highwater
, NULL
, 2 // Priority, with 3 (configMAX_PRIORITIES - 1) being the highest, and 0 being the lowest.
, NULL );
xTaskCreate(
TaskB
, "TaskB"
, 128 // Stack size
, NULL
, 1 // Priority
, NULL );
}
void loop()
{
// Empty. Things are done in Tasks.
}
void TaskA(void *pvParameters) // This is a task.
{
(void) pvParameters;
for (;;) // A Task shall never return or exit.
{
Serial.print('A');
vTaskDelay( 1000 / portTICK_PERIOD_MS ); // wait for one second
}
}
void TaskB(void *pvParameters) // This is a task.
{
(void) pvParameters;
for (;;)
{
Serial.println('B');
vTaskDelay( 1000 / portTICK_PERIOD_MS ); // wait for one second
}
}
サンプルプログラムの「Blink_AnalogRead.ino」を修正して作った。
setup()の中でxTaskCreate関数を呼んでタスクを生成し、loop()には何も書かずに各タスクに処理を書くのが基本構成。
優先度が高いタスクは、vTaskDelay()を使用して、待ち状態を意図的に作る必要がある。
上のプログラムをシリアルモニタで見てみる。
だいたい1秒おきにAとBが出力されている。Aの方が優先度が高いので、ちゃんとAが先に実行されている。
上記のプログラムでは、タスクAが実行して1秒待つ→その間にタスクBが実行される→タスクAが実行される、という構成のため、タスクAの処理時間だけ1秒間隔がずれてしまっている。50ms~100msくらいずれているので、組み込みの世界では致命的になりかねない。次は「できる限り正確に」1秒周期で実行するタスクを実行するする方法について書きたい。