LoginSignup
0
0

FreeRTOS概要

Last updated at Posted at 2024-05-16

FreeRTOSを仕事で使う機会がありましたので、使用方法・OSの振る舞いの概要をここにまとめておこうかと思います。

FreeRTOS( https://www.freertos.org/ )とは

複数のマイクロコントローラに対応した、オープンソースのRTOS。各マイクロコントローラごとにサンプルが用意されているため、導入は難しくない。

ライセンス

バージョン9までは例外条項付きGPLの下で配布されていた。 例外条項は、カーネルそのものがオープンソースである場合にはユーザのコードをクローズドソースにしておくことができるという内容である。Amazon.comに買収された後のバージョン10からはMITライセンスを採用している。
(wikipediaより引用)

FreeRTOSの振る舞いとAPI

用語

用語 意味
アイドルタスク アイドルタスクとは優先度が最小値で設定されている。
常時実行可能な状態のタスクのことを示す。
実行するタスクが無い場合、このタスクが動く。アイドルタスクはOSの処理で定義される
タスクトレース 作成されたタスク情報を出力する機能。OSの設定方法によっては使用可能
コルーチン コルーチンはタスクと違い、スタックを共有する小さなタスクのことを示す。コルーチンは協調的スケジューリングを行う。主に低スペックなCPUでのマルチタスク動作がしたい場合に用いる

OS設定

FreeRTOS設定方法

「FreeRTOSConfig.h」というファイルにて定義されているマクロの値を変更、もしくは決められたマクロを定義することによりOSの設定を変更できる。各マクロは以下を参照のこと

マクロ 意味
configUSE_PREEMPTION スケジューラの設定。
値が1のときはプリエンプティブ、0のときは協調型
configUSE_TIME_SLICING 同じ優先度のタスクに対しタイムスライスを行うかどうか。値が1の時にタイムスライスを行う
configUSE_IDLE_HOOK 値が1のとき、
「void vApplicationIdleHook( void )」
を定義することで、アイドルタスク内で定義したvApplicationIdleHook
が呼ばれる
configMAX_PRIORITIES ( 設定した値-1 )がタスクの最大の優先度になる
configUSE_TICK_HOOK 値が1のとき、
「void vApplicationTickHook( void )」
を定義することでタイマ割込み内で「vApplicationTickHook」が呼ばれる。
割込み頻度は「configTICK_RATE_HZ」の値で設定できる
configCPU_CLOCK_HZ CPUの周波数を設定する
configTICK_RATE_HZ 1秒間に何度タイマ割込みがかかるかを示す
configMINIMAL_STACK_SIZE アイドルタスクのスタックサイズ(タスクのスタックの最小値)を設定
configTOTAL_HEAP_SIZE 動的に確保できるメモリの総量。タスクのスタックもこの領域から確保するため、タスクのスタックが必要ならばこの値を大きくするとよい
configMAX_TASK_NAME_LEN タスク名(タスクを生成する際に指定するデバッグ用のタスクを示す文字
列)の最大の長さ
configUSE_TRACE_FACILITY タスクトレースを使用するかどうか。デバッグ用。1にした際はタスクトレースが使用可能になる
configUSE_STATS_FORMATTING_FUNCTIONS タスクトレースを使用するかどうか。デバッグ用。
configUSE_TRACE_FACILITYとこの値を1にした際はタスクトレースが使用可能になる
configUSE_16_BIT_TICKS タイマ割り込みによるtickカウンタのビット数を示す。1なら16bit、0なら32bit
configIDLE_SHOULD_YIELD configUSE_PREEMPTION == 1のとき、1を定義することでアイドルタスクの
処理中にタスクの切り替えを行う
configUSE_CO_ROUTINES コルーチンを使用するかどうか。
1を定義することでコルーチンが使用可能になる。
configUSE_MUTEXES ミューテックスを使用するかどうか。
1を定義することでミューテックスを使用可能
configMAX_CO_ROUTINE_PRIORITIES コルーチンの優先度の最大値を設定。
configMAX_CO_ROUTINE_PRIORITIES-1が優先度の最大値となる
configUSE_COUNTING_SEMAPHORES 資源獲得回数を数える機能を持ったセマフォを使うかどうか。
1にすると使用可能
configCHECK_FOR_STACK_OVERFLOW stack overflow時の処理の設定。0にするとstack overflow時に何もしない。1にした場合はstack overflow時にタスクを停止する。2にした場合はstackが残り16byteを切った時に、ユーザが定義した
void vApplicationStackOverflowHook(void)
を呼び出す
configUSE_RECURSIVE_MUTEXES 再帰を使用した排他制御を使用するかどうか。1にすると使用可能
configQUEUE_REGISTRY_SIZE デバッグ用。キューの名前を割り当てる際、いくつまで割り当てが可能かを設定する
configGENERATE_RUN_TIME_STATS タスクの実行状況を確認する機能を使うかどうか。1にするとタスクの実行状況の計測
を使用可能にする(cortex M3で使用できるかは不明。)
INCLUDE_~ 値が1のとき「~」に入るモジュールを使用可能にする。例えば「INCLUDE_vTaskPrioritySet」の値が1なら「vTaskPrioritySet()」関数が使用可能になる
configKERNEL_INTERRUPT_PRIORITY arm CortexM-3, PIC24, dsPIC, PIC32, SuperH and RX600.で使用。割込みの優先度の最大値(cortexM-3では0が最大値のため)を設定する。ここで設定された値が、そのままPendSVの優先度とSysTick の優先度に設定される
configMAX_SYSCALL_INTERRUPT_PRIORITY この値が割込みマスクレジスタ(BASEPRI(basepri))に反映される
configSUPPORT_STATIC_ALLOCATION OSで確保する以外のメモリを使用してタスク、システムコールを使用する場合に1にする。定義されていない場合は、OSのコード上で
configSUPPORT_STATIC_ALLOCATION=0
と定義される
configSUPPORT_DYNAMIC_ALLOCATION OSで確保するメモリを使用してタスク、システムコールを使用する場合に1にする。
定義されていない場合は、OSのコード上で
configSUPPORT_DYNAMIC_ALLOCATION=1
と定義される
configTIMER_TASK_STACK_DEPTH OS内で作成されるタイマータスクのスタックサイズ
configTIMER_QUEUE_LENGTH OS内で作成されるタイマータスクのキューの長さ
configTIMER_TASK_PRIORITY OS内で作成されるタイマータスクの優先度。低い優先度に設定しても特に影響はなし
configUSE_TIMERS OS内で作成されるタイマータスクを使用するかどうか。使わない場合は割込みからのシステムコールが使用できないため、値を1と定義しておく必要がある
INCLUDE_xTimerPendFunctionCall OS内で作成されるタイマータスクにおいて、割込みからのシステムコールの対応に必要な関数を使用するかどうか。基本的には値を1と定義しておく必要がある

メモリ

  • OSにて使用しているメモリサイズは
    376byte+タスクのスタックサイズ+queueサイズ(システムコール用に使っているもの)

  • 「FreeRTOSConfig.h」の「configTOTAL_HEAP_SIZE」の値で使用できるメモリの総サイズを調節できる

  • ヒープメモリの確保・解放のために使用するファイルが5つ用意されており、どれか一つを選んで使用する形
    (私が使用した際は主にheap_4.cを使用。 heap_1.c、 heap_2.cはメモリが足リなくなりやすいので非推奨)

使用ファイル メモリの処理方法
heap_1.c メモリの確保を一度行うと解放できない
heap_2.c メモリの再確保が可能。
ただし、フラグメンテーションを避けるための処理はなし
heap_3.c C言語のmalloc,freeのラッパー
heap_4.c メモリの再確保が可能。
フラグメンテーションを避けるため、解放されているメモリで隣接したものがあれば、一つにまとめる動作を行う。逆に小さいサイズのメモリが隣接していても一つにまとめてくれるわけではないので注意
heap_5.c heap_4.cに加え、確保したメモリエリアを非隣接的に割り当てる機能あり。
vPortDefineHeapRegions()で使用するメモリを定義しなければ使用不可。定義したメモリのみを使用してタスクやシステムコールで使用するメモリを確保する。

参考: https://www.freertos.org/FAQMem.html

その他設定

FREERTOS_TASKS_C_ADDITIONS_INIT(型はvoid func(void))を定義することにより、vTaskStartScheduler呼び出し時に追加で初期化を行うことが可能

タスクステート

タスクの状態に関して説明する。タスクは生成された時点では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ステートに切り替わった時

協調的スケジューリング

割愛(時間のある時に更新予定)

システムコール及びAPI

概ね使用頻度が高いものを重点的に説明する。「~ISR」という名前の付いた関数(xEventGroupSetBitsFromISR, xMessageBufferSendFromISR等 )は割込みのルーチンからシステムコール(セマフォ・イベントフラグ・メッセージボックス等の関数)を呼ぶ際に用いる関数である。

※ただし、この割込み用のシステムコールではシステムコールのアドレスを別の場所にストックし、割込み終了後にタイマー割り込みによって呼び出していると思われる。

スケジューラ関連

void vTaskStartScheduler( void )

項目1 項目2 説明
機能 スケジューラをスタートする。(RTOSの動作開始)
引数 なし
返り値 なし

void vTaskEndScheduler( void )

項目1 項目2 説明
機能 スケジューラを停止する。(RTOSの動作終了)
引数 なし
返り値 なし

TickType_t xTaskGetTickCount( void )

項目1 項目2 説明
機能 OSのスケジューラで使用するタイマの値を取得する
引数 なし
返り値 OSのタイマの値

task関連

BaseType_t xTaskCreate( TaskFunction_t pvTaskCode,
const char * const pcName,
configSTACK_DEPTH_TYPE usStackDepth,
void *pvParameters,
UBaseType_t uxPriority,
TaskHandle_t *pvCreatedTask )

項目1 項目2 説明
機能 タスクを生成する。
引数 pvTaskCode タスクとなる関数
pcName タスク名(デバッグ用。動作に影響なし)
usStackDepth スタックサイズ
pvParameters タスク起動時にタスクに渡される引数。
基本的にはNULLで設定
uxPriority 優先度
pvCreatedTask タスクのID
返り値 タスク生成が成功ならばpdTRUEを返す

TaskHandle_t xTaskGetCurrentTaskHandle( void )

項目1 項目2 説明
機能 呼び出し先のタスクのIDを取得する
引数 なし
返り値 呼び出し先のタスクID

void vTaskDelete( TaskHandle_t xTaskToDelete )

項目1 項目2 説明
機能 タスクを削除する(タスクがsuspendedステート、blockedステートの際に動作可能)
引数 xTaskToDelete 削除するタスクのID
返り値 なし

wake(vTaskResume),sleep(vTaskSuspend)

wake,sleepという概念はなく、sleepはvTaskSuspend() 、wakeはvTaskResume() で代用することになる。ただし、vTaskResume()が呼ばれた後にvTaskSuspend()が呼ばれた際は、初めに呼ばれたvTaskResume()は無効になる。使用する際は必ずvTaskSuspend()を呼んだ後にvTaskResume()を呼ぶ必要がある(つまり、itronと違い、wakeリクエストはキューイングされない)

void vTaskSuspend( TaskHandle_t xTaskToSuspend )

項目1 項目2 説明
機能 タスクのステートをSuspendedステートへ変更する
引数 xTaskToSuspend Suspendedステートへステートを変更する対象のタスクID
返り値 なし

void vTaskResume( TaskHandle_t xTaskToResume )

void xTaskResumeFromISR( TaskHandle_t xTaskToResume )

項目1 項目2 説明
機能 Suspendedステートのタスク
のステートをReadyステート
へ変更する。このとき、指定
したIDのタスクがSuspended
ステートでない場合は何もしない
引数 xTaskToSuspend Readyステートへステートを
変更する対象のタスクID
返り値 なし

wait(vTaskDelay, vTaskDelayUntil)

「FreeRTOSConfig.h」の「configTICK_RATE_HZ」の値によって精度が変わるので注意。ms単位でwait,delayをしたい場合は、「configTICK_RATE_HZ」の値を1000にすること

void vTaskDelay( const TickType_t xTicksToDelay )

項目1 項目2 説明
機能 呼び出し先のタスクを引数で
指定した時間waitする
引数 xTicksToDelay waitする時間
返り値 なし

void vTaskDelayUntil( TickType_t * const pxPreviousWakeTime,
const TickType_t xTimeIncrement )

項目1 項目2 説明
機能 呼び出し先のタスクを指定した時間
(pxPreviousWakeTime + xTimeIncrement )
までwaitする。周期タスクにて使用するの
がよい。vTaskDelayは、関数が呼ばれた時
点から指定した時間waitするが、この関数
はpxPreviousWakeTimeで入力された時間
から、xTimeIncrement
で指定した時間waitする
引数 pxPreviousWakeTime waitする時間の基準となる値。
初回の呼び出し時はxTaskGetTickCount()
で取得した時間を入れる。
関数内でwaitが終了した時点で
pxPreviousWakeTime
= pxPreviousWakeTime
+ xTimeIncrement
に更新される
xTimeIncrement pxPreviousWakeTimeからwaitする時間
返り値 なし

semaphor

使用できるセマフォは以下の2種類ある

項目 説明
バイナリセマフォ 単発の待ち合わせ用のセマフォ。
Itron系と同じように使用できる。
「vSemaphoreCreateBinary()」
で生成
カウンタセマフォ 複数回の資源獲得・解放を行う場
合に、資源獲得回数を記憶してお
くことができるセマフォ。
「xSemaphoreCreateCounting()」
で生成

void vSemaphoreCreateBinary( SemaphoreHandle_t xSemaphore )

項目1 項目2 説明
機能 単発の待ち合わせ用のセマフォ
を作成する
引数 xSemaphore セマフォID。
(SemaphoreHandle_tはポイン
タとして定義されているので、
セマフォIDを格納したいものを
引数にする)
返り値 なし

SemaphoreHandle_t xSemaphoreCreateCounting( UBaseType_t uxMaxCount,
UBaseType_t uxInitialCount )

項目1 項目2 説明
機能 資源獲得回数を数えるセマフォ
を作成する
引数 uxMaxCount 獲得回数の最大値
引数 uxInitialCount 獲得回数の初期値。この数値から
カウントをスタートする
返り値 セマフォID。
(SemaphoreHandle_tはポイン
タとして定義されているので、
セマフォIDを格納したいものを引
数にする)

BaseType_t xSemaphoreTake( SemaphoreHandle_t xSemaphore,
TickType_t xBlockTime )

項目1 項目2 説明
機能 セマフォを獲得する
(vSemaphoreCreateBinary
,xSemaphoreCreateCounting
で作成されたセマフォ用)
引数 xSemaphore 獲得するセマフォID
xBlockTime タイムアウト時間
返り値 正常終了したならばpdTRUEを返す

BaseType_t xSemaphoreGive( xSemaphore )

項目1 項目2 説明
機能 セマフォを解放する
(vSemaphoreCreateBinary,
xSemaphoreCreateCounting
で作成されたセマフォ用)
引数 xSemaphore 解放するセマフォID
返り値 正常終了したならばpdTRUEを返す

void vSemaphoreDelete( SemaphoreHandle_t xSemaphore )

項目1 項目2 説明
機能 セマフォを削除する
(vSemaphoreCreateBinary,
xSemaphoreCreateCounting
で作成されたセマフォ用)
引数 xSemaphore 削除するセマフォID
返り値 なし

eventGroup(eventFlag)

名前はeventGroupだが、仕様はItronやToppers等のイベントフラグと似たような形。''イベントフラグの使用可能な数はFreeRTOSConfig.hで定義するconfigUSE_16_BIT_TICKSの値によって決まる。configUSE_16_BIT_TICKSが0なら8、configUSE_16_BIT_TICKSが1なら24まで使用することができる''

EventGroupHandle_t xEventGroupCreate( void )

項目1 項目2 説明
機能 イベントフラグを作成する
引数 なし
返り値 作成に成功したイベントフラグのID。失敗時はNULLが入る

EventBits_t xEventGroupWaitBits( EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToWaitFor,
const BaseType_t xClearOnExit,
const BaseType_t xWaitForAllBits,
const TickType_t xTicksToWait );

項目1 項目2 説明
機能 イベントフラグを待つ
引数 xEventGroup イベントフラグID
uxBitsToWaitFor 待ちbitパターン
xClearOnExit イベントフラグが立った際に、
フラグをクリアするかどうか
xWaitForAllBits pdTRUEならば、待っている
すべてのbitが立つまで待つ。
pdFALSEなら、待っている
bitのうちどれかが立つのを待つ
xTicksToWait タイムアウト時間
返り値 待ち解除時のbitパターン

EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToSet )

項目1 項目2 説明
機能 イベントフラグを立てる
引数 xEventGroup イベントフラグID
uxBitsToSet セットするbitパターン
返り値 uxBitsToSet をセットした後のbitパターン

EventBits_t xEventGroupSetBitsISR( EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToSet,
BaseType_t *pxHigherPriorityTaskWoken )

項目1 項目2 説明
機能 イベントフラグを立てる
引数 xEventGroup イベントフラグID
uxBitsToSet セットするbitパターン
pxHigherPriorityTaskWoken イベントフラグを立てた際にready
ステートになるタスクが、その時
点でrunningステートのタスクよりも優先度が高い場合にpdTRUEが入る
返り値 uxBitsToSet をセットした後のbit
パターン

EventBits_t xEventGroupClearBits( EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToClear );

項目1 項目2 説明
機能 イベントフラグをクリアする
引数 xEventGroup イベントフラグID
uxBitsToClear クリアするbitパターン。
0になっているbitをクリアする
返り値 uxBitsToClear
でクリアする前のbitパターン

void vEventGroupDelete( EventGroupHandle_t xEventGroup )

項目1 項目2 説明
機能 イベントフラグを削除する
引数 xEventGroup 削除するイベントフラグのID
返り値 なし

スケジューラの具体的な動きとしては、タスク情報の構造体のメンバであるイベントリスト(xEventListItem)に待っているビットの値を格納し、ブロックステートに入り、タスクの切り替えを行う。仕様書ではフラグ待ち時はブロックステートとなっているが、タスクをサスペンドさせた時と同じようにpxDelayedTaskListに追加される

イベントフラグのセット時は、タスク情報からイベントリスト(xEventListItem)内にあるビットの値を参照し、待っていたビットならばステートをreadyステートに変更し、タスクの切り替えを行う。

messageBuffer(messageBox)

名前はmessageBufferだが、仕様はItronやToppers等のメッセージボックス、メールボックスと似たような形。ただし、メッセージボックスで受信するのに使うバッファはユーザ側で定義する必要がある(OS側では受信バッファを用意していない)。
また、uItronにおけるメッセージバッファ(先頭アドレスのみを渡すもの)は機能として存在しない

MessageBufferHandle_t xMessageBufferCreate( size_t xBufferSizeBytes )

項目1 項目2 説明
機能 メッセージボックスを作成する
引数 xBufferSizeBytes 一度に送受信可能なメッセージサイズ。小さすぎると動作が遅くなることがあるので注意
(単位はbyte)
返り値 作成に成功したメッセージボックスの
ID。失敗時はNULLが入る

size_t xMessageBufferSend(
MessageBufferHandle_t xMessageBuffer,
const void *pvTxData,
size_t xDataLengthBytes,
TickType_t xTicksToWait )

size_t xMessageBufferSendISR(
MessageBufferHandle_t xMessageBuffer,
const void *pvTxData,
size_t xDataLengthBytes,
TickType_t xTicksToWait )

項目1 項目2 説明
機能 メッセージボックスへメッセージ
を送る
引数 xMessageBuffer 送信先のメッセージボックスのID
pvTxData 送信するメッセージの先頭ポインタ
(メッセージそのものはメッセージ
ボックス内でコピーされ使われる)
xDataLengthBytes 送信するメッセージの長さ(byte値)
xTicksToWait タイムアウト時間
返り値 送信が完了したデータサイズ(byte値)

size_t xMessageBufferReceive(
MessageBufferHandle_t xMessageBuffer,
void *pvRxData,
size_t xBufferLengthBytes,
TickType_t xTicksToWait )

項目1 項目2 説明
機能 メッセージボックスからメッセージを受信する
引数 xMessageBuffer 受信先のメッセージボックスのID
pvRxData 受信するメッセージの先頭ポインタ
(メッセージそのものはpvRxDataに
コピーされる)
xBufferLengthBytes 受信するメッセージの長さ(byte値)
xTicksToWait タイムアウト時間
返り値 受信が完了したデータサイズ(byte値)

void vMessageBufferDelete( MessageBufferHandle_t xMessageBuffer )

項目1 項目2 説明
機能 メッセージボックスを削除する
引数 xMessageBuffer 削除するメッセージボックスのID
返り値 なし

ミューテックス

未調査。後々更新予定

メモリ関連

void *pvPortMalloc( size_t xSize )

項目1 項目2 説明
機能 メモリの確保
引数 xSize 確保するメモリサイズ
返り値 確保したメモリの先頭ポインタ

void vPortFree( void *pv )

項目1 項目2 説明
機能 メモリの解放
引数 pv 解放するメモリの先頭ポインタ
返り値 なし

FreeRTOSのコード例

一つ以上のタスクを生成し、「vTaskStartScheduler()」を呼び出すことによりOSのスケジューリングが開始される。

以下にFreeRTOSを使用した際のmain.cのコード例を示す。以下の例では優先度2、スタックサイズ1024のタスク(mainTask)を作成し、動作させている

コード例:


/* FreeRTOS用モジュール */
#include "FreeRTOS.h"
#include "task.h"

static void mainTask( void *pvParameters );

int main()
{
  UENV_OS_TSKID testID1;
  
  uenv_os_CreTask( mainTask, &testID1, 2, 1024 );

  /* RTOSの動作開始。中で無限ループしている。エラーがかかった場合のみリターンされる */
  vTaskStartScheduler();

  while(1);
}

static void mainTask( void *pvParameters )
{
  const TickType_t xTicksToDelay = 1000 / portTICK_PERIOD_MS;

  while(1) {
      vTaskDelay( xTicksToDelay );
  }
}

※また、使用するすべてのタスクを定義した後に「vTaskStartScheduler()」を呼び出すことが公式では推奨されているが、特に動作に影響はないので気にしなくてもよいと思われる(動作確認済)

その他

  • cortex M-3にてfreeRTOSを用いる際はcortex M-3用のMemory protection Unitはサポート
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0