FreeRTOSを仕事で使う機会がありましたので、使用方法・OSの振る舞いの概要をここにまとめておこうかと思います。
コンフィグや各API、タスクの状態監視の説明は下記の別記事にて説明いたします。
FreeRTOS( https://www.freertos.org/ )とは
複数のマイクロコントローラに対応した、オープンソースのRTOS。各マイクロコントローラごとにサンプルが用意されているため、導入は難しくない。
ライセンス
バージョン9までは例外条項付きGPLの下で配布されていた。 例外条項は、カーネルそのものがオープンソースである場合にはユーザのコードをクローズドソースにしておくことができるという内容である。Amazon.comに買収された後のバージョン10からはMITライセンスを採用している。
(wikipediaより引用)
用語
用語 | 意味 |
---|---|
アイドルタスク | アイドルタスクとは優先度が最小値で設定されている。 常時実行可能な状態のタスクのことを示す。 実行するタスクが無い場合、このタスクが動く。アイドルタスクはOSの処理で定義されている(コルーチンを使う場合は一部ユーザで手を入れる)。 |
タスクトレース | 作成されたタスク情報を出力する機能。OSの設定方法によっては使用可能 |
コルーチン | コルーチンはタスクと違い、スタックを共有する小さなタスクのことを示す。主に低スペックなCPUでのマルチタスク動作がしたい場合に用いる |
タスクステート
タスクの状態に関して説明する。タスクは生成された時点ではreadyステートになり、4つのステートどれかに設定されている。以下のリンクを参照のこと
引用元:https://www.freertos.org/RTOS-task-states.html
Running
実行状態であることを示す。現在実行されているタスクがRunningステートとなる
Ready
実行可能状態であることを示す。現在実行されているタスクより優先度が低く、Blockedステート・SuspendedステートではないタスクがReadyステートとなる
Blocked
別タスク・もしくは割込みからのOSのシステムコール(イベントフラグ・メッセージボックス・データキュー等)を待っている際になるステート
Suspended
vTaskSuspend()を呼び出した際になるステート。Blockedステートとは厳密には違う状態として扱う
(ただし、ソースコードを見る限りではBlockedステートと同じ扱いをしている)
スケジューリング
FreeRTOSのスケジューラは「FreeRTOSConfig.h」の「configUSE_PREEMPTION」の値を1と定義するとプリエンプティブになる。「configUSE_PREEMPTION」の値を0とした際は協調的スケジューリングとなる
プリエンプティブスケジューリング
基本的なスケジューリングは、Itron系OS等と同等なスケジューリングを行う。ただし、同じ優先度のタスクにおいては「FreeRTOSConfig.h」の「configUSE_TIME_SLICING」の値を1にしていると、同じ優先度のタスク同士でタイムスライスを行い、交互にそれぞれのタスクが動く。また、タスクの切り替えのタイミングは以下の3つである
-
OS用のタイマ割込み
- タイマ割込みの頻度はFreeRTOSConfig.h内のconfigTICK_RATE_HZの値で調節可能。1秒間の間にconfigTICK_RATE_HZで指定した値の回数分だけが呼ばれる。ただし、内ではOSで使用するタイマのカウントも行うため、タスクを1ms刻みでwait,delayする際にはconfigTICK_RATE_HZの値を1000にしておく必要がある。(configTICK_RATE_HZの値は1000で固定を推奨)
-
RunningステートのタスクがBlockedステートorSuspendedステートに切り替わった時
-
OSのシステムコールにより、BlockedステートorSuspendedステートのタスクがreadyステートに切り替わった時
協調的スケジューリング
「configUSE_PREEMPTION」の値を0に設定し、「configUSE_TIME_SLICING」の値を1にすると、
全てのタスクが均等に実行される設定となる。
「configUSE_PREEMPTION」の値を0に設定し、「configUSE_TIME_SLICING」の値を0にすると、
優先度低のタスクであろうと、実行中であれば、他のタスクへスイッチしなくなる。Delayなどで自身のタスクを待ち状態にしないといけなくなるので注意。
コルーチン
-
タスクとは異なる、複数の関数(コルーチン)を平行して実行させる仕組み。メモリの制約上、タスクを大量に作ることが困難な場合など、小規模なシステムで使用されることを想定している
-
タスクとコルーチンではタスクの方が優先され、アイドルタスクが実行されるタイミングでコルーチンは動作する
-
コルーチンも、ステートと優先度が存在し、優先度が高く実行可能な状態(Ready状態)のコルーチンから実行される(Running状態となる)
引用元:https://www.freertos.org/media/2018/crstate.gif
タスクとの共存方法
アイドルタスク(vApplicationIdleHook())内でコルーチンを作動させるための関数「vCoRoutineSchedule()」を呼び出すことでタスクと共存させて、動作させる。
FreeRTOSのコード例
一つ以上のタスクを生成し、「vTaskStartScheduler()」を呼び出すことによりOSのスケジューリングが開始される。
以下にFreeRTOSを使用した際のmain.cのコード例を示す。以下の例では優先度2、スタックサイズ1024のタスク(mainTask)を作成し、動作させている
コード例:
/* FreeRTOS用モジュール */
#include "FreeRTOS.h"
#include "task.h"
#define THREAD_NAME "THREAD1"
#define THREAD_PRIORITY (2)
#define SAMPLE_STACKSIZE (1024)
TaskHandle_t task1;
static int task_delay = 1000;
static void mainTask( void *pvParameters );
int main()
{
if (pdTRUE != xTaskCreate(mainTask, THREAD_NAME, SAMPLE_STACKSIZE, &task_delay, THREAD_PRIORITY, &task1))
{
return;
}
/* RTOSの動作開始。中で無限ループしている。エラーがかかった場合のみリターンされる */
vTaskStartScheduler();
while(1);
}
static void mainTask( void *pvParameters )
{
const TickType_t xTicksToDelay = 1000 / portTICK_PERIOD_MS;
while(1) {
vTaskDelay( xTicksToDelay );
}
}