FreeRTOSでRTOS解説|ポイントAPI抜粋、サンプルソフト提供

MCU RTOS習得(2020年版)

FreeRTOSを例に、RTOS基本機能を解説し、習得のための開発環境と、RTOSサンプルソフトを示します。
IoT MCUのソフト開発は、RTOS: Real Time Operation Systemが必要になると思います。その理由が以下です。

  • 一般的なMCU開発でも、通信UART制御は処理が複雑で重く、鬼門!
  • IoT MCUの通信プロトコルが何に決まるかは不透明だが、UARTに比べて複雑な通信処理になることは明らか

これらの対策、切り札として、FreeRTOSやmbed OSなどのMCU RTOSが期待されています。弊社ブログでも紹介してきました。これらRTOS関連資料は、多くあります。しかし、1から10まで書いている教科書的な内容で、参考書としては優れていますが、実務的にはもっと効率的に習得したいのが本音です。

FreeRTOS習得の開発環境

そこで、普通のベアメタルMCUソフト開発に慣れた開発者が、最低限のRTOS知識と評価ボードを使って、手っ取り早くMCU RTOSを習得するための開発環境が、上図です。

  • 使用MCU RTOS:FreeRTOS(NXP MCUXpresso IDE無料版に付属)
  • 評価ボード:NXP LPCXpresso54114 (Cortex-M4/M0+、256KB Flash、192KB RAM)
  • RTOSサンプルソフト:MCUXpresso SDK付属LPCXpresso54114 FreeRTOSサンプルコード

選定理由は、(1)LPCXpresso54114で動作するFreeRTOSがポーティング済み、(2)入手性が良く低価格な評価ボード、(3)サンプルソフトを使ってRTOSを効率的に学べる、ことです。

提案環境でRTOSを理解習得すれば、IoT MCUでRTOSを使う場合でも慌てずに対処することができます。FreeRTOSを使いますが、他のRTOSへも応用可能です。

第1部から第3部は、FreeRTOSサンプルコード理解のための基礎知識、第4部は、MCUXpresso SDK付属LPCXpresso54114 FreeRTOSサンプルコードを使って、FreeRTOS習得方法を具体的に説明します。

※MCU RTOS習得環境:MCUXpresso IDE/SDKインストール方法や、評価ボードLPCXpresso54114購入先などは、弊社ブログ:NXPマイコン開発環境更新と、FreeRTOSサンプルコード(1)を参照してください。

掲載内容

第1部:RTOSの利点
第2部:RTOSのタスク管理
第3部:RTOSタスク間のデータ通信、同期、排他制御
第4部:FreeRTOSサンプルソフトウェア
※2020年版は、主に第4部:FreeRTOSサンプルソフトウェアを、弊社ブログ:FreeRTOSサンプルコード調査を基に改版しました。

第1部:RTOSの利点

RTOSのメリットを、2個LEDを点滅させるLチカサンプルソフトを例に解説します。

第1部:RTOS利点のまとめ

  • RTOSが、複数ユーザタスク優先順位に応じてMCU実行時間を振り分けるので、個々のユーザタスクは、シンプルで可搬性に優れた記述ができる。
  • IoT MCUの通信処理は、UARTに比べ複雑。この複雑さは、再送データ数や外来ノイズなどの通信環境により様々に変化。1つの無限ループ内で各種処理を実行するベアメタルMCUソフト開発手法では、これらに対応するには複雑すぎる。
  • RTOSを使うと、通信処理のような複雑な処理を、他の処理の影響を受けずシンプルな記述で開発できる。

RTOSの必要性

2個のLEDを点滅させるいわゆる「Lチカサンプルソフト」を、FreeRTOS利用時ソース記述(左側)と、通常のベアメタルソフト記述(右側)で示します。最大の違いは、無限ループの数です。

Lチカサンプルソフトの差

FreeRTOS記述の場合、1個のタスク(≒ユーザ処理単位)で1個の無限ループを持ちます。一方、通常ソフト記述の場合は、全体で1個の無限ループのみです。この1個の無限ループ内で様々なユーザ処理を行うため、ループ内の1処理時間の長さ、短さ、待ちがその他の処理へ影響を与えます。

RTOSを使う最大の利点は、1つのタスク実行時間の影響が、他のタスクへ及ばないことです。つまり、赤LED点滅タスクのvTaskDealy()の処理停止時間を変えても、緑LED点滅タスクに影響は全くありません。

このおかげで、あたかも1つのMCUを占有するかのようにユーザタスク記述ができます。従って、1個のタスクが、1個の無限ループを持つのです。複数のタスクへ優先順位に応じて実行時間を振り分けるのは、RTOSの役目です。

ユーザタスクは、他のタスクのことを気にせずに記述できるため、シンプルな処理になりタスク単位の可搬性も向上します。

RTOSでのユーザタスク記述は、通常ソフト記述と何ら変わることはありません。1無限ループ内にシンプルな処理を記述すれば良いのです。ただし、RTOS利用のオーバーヘッドとして、タスクの登録や優先順位の設定は別途必要となります。


要はRTOS APIを追加するだけ!

RTOSのLチカサンプルソースは、FreeRTOS APIとLPCXpresso API、残りがC言語の3種類からなる構成です。

Lチカサンプルソース

LPCXpresso APIとC言語は、通常ソフト記述時に使うものと同じです。FreeRTOS APIは、APIの頭に必ずx…、v…、ux…などが付いています。
これらの接頭語は、FreeRTOS以外の他のRTOSでも同様です。

つまり、RTOSのAPIは、他のAPIと明示的に分けられるのです。RTOSユーザタスクの記述は、通常ソフトの記述に、これらRTOS APIが加わったのみです。

従ってFreeRTOS APIの使い方を理解すれば、FreeRTOSに限らず他のRTOSへも応用可能です。使用頻度が高いFreeRTOS APIの使い方さえ習得すれば、基本的なRTOSユーザタスク開発ができます。

この使用頻度の高いRTOS APIをマスターする方法で、IoT MCUにRTOSが適用する時でも、慌てずに備えることができます。


RTOS習得を手軽にする方法 (提案RTOS習得環境の説明とお勧めの参考書)

RTOS習得には、初期段階で手間と時間が掛かります。例えば、RTOSを使用する為のみに別途面倒なMCUシステムコンフィグレーションが必要になるなどです。

そこで、実務的で低価格、手軽な「FreeRTOS+評価ボード+RTOSサンプルソフト」を使ったRTOS習得方法を示しました。

この環境では、通常のベアメタルソフトにRTOS API追加のみで、FreeRTOSが簡単に使えます。使用頻度が高い20個弱のRTOS APIを理解、習得すれば、RTOSソフト開発ができます。

以降もFreeRTOSのポイントをできるだけ簡潔に説明していきます。詳しく知りたい方は、

などを参照してください。


第2部:RTOSのタスク管理

RTOSが複数ユーザタスクの無限ループを回し、タスクの優先順位に応じてMCU実行時間を振り分けること、その利点を第1部で示しました。

RTOSは、タスクを処理単位として扱います。RTOSがユーザタスクをどのように扱うかを解説します。タスク自身は既に出来上がったものと仮定します。

第2部:RTOSのタスク管理のまとめ

  • スケジューラーの優先順位判定により、複数タスクのRunningが切り替わりマルチタスクを実現
  • Blockedにより、RTOSでのタスク記述に、通常ソフト記述と同様の無限ループを使える

FreeRTOSのユーザタスクの扱い方

ユーザタスクをFreeRTOSで処理してもらうには、最初にタスク登録が必要です。登録済みの複数タスクは、RTOSにより以下のように4つの状態で管理されます。

FreeRTOSの4状態遷移

FreeRTOSへのタスク登録APIが、vTaskCreate()、登録済みタスクの削除APIが、vTaskDelete()です。FreeRTOSは、優先順位の高いタスクをRunningにします。従って、登録後、タスクを即実行するのではなく、他タスクとの優先順位判定をReadyで行い、その結果で実行状態にします(図示の太線部分)。

優先順位は、登録APIのvTaskCreate()パラメタで指定できますが、デフォルトは全て同順位です。同順位タスクは、TICK_RATE(タスク切換え時間)単位に実行状態を切り替えるラウンドロビン方式です。実行後は、再びReadyに戻されます。

例えば2タスクのみが登録された場合、2タスクを1ms毎に交互に切り替えながら実行します。ユーザ側からは、2つのタスクが並列動作したように見えます。これが最も簡単なRTOSマルチタスク処理の説明です。

デフォルト優先順位の変更に使うAPIが、vTaskPrioritySet()、vTaskPriorityGet()です。

更にReadyやRunningのタスクに対して、図示のAPIでSuspendedやBlockedへも遷移可能です。

これら制御を行うのがスケジューラー、スケジューラーが行う制御をタスク管理と呼びます。スケジューラーをRTOSカーネルと呼ぶこともあります。

FreeRTOSスケジューラーは、4個の状態でタスクを管理しますが、数がもっと多いRTOSの例もあります。
※例えば、RL78用のRTOS:RI78V4は、6個の状態遷移を持ちます。


Blockedの役目

さてここで、第1部で示したLED点滅タスクの無限ループ内にあるvTaskDelay()を解説します。

vTaskDealy()は、タスクをBlockedへ遷移させます。そして設定時間の停止後、Readyへ戻します。つまりBlockedの間は、MCUを使わないため他タスクがRunningになりうるのです。これが、RTOSを使っても、各タスクに通常ソフトと同じように無限ループを記述できる非常に重要な仕組みです。

RTOSを使わない通常ソフト記述の場合、無限ループは、文字通りそのループ内に留まりMCU能力を使い続けます。しかしRTOSは、vTaskDelay()によりソース上は無限ループでも、MCUを使いません。これによりマルチタスク処理ができるのです。

RTOSにより再びRunningに戻ったタスクは、vTaskDealy()の後の処理から実行されます。タスク側からは、指定時間の停止後に継続して実行しているように見えます。

スケジューラーの状態遷移図は、ユーザタスク側からみた状態です。Running以外はMCUを使わないNot Running (super state)ですが、スケジューラー自身のために(ほんの少し!)MCUを使います。このスケジューラーを起動するAPIが、RTOSのmain()最後にあるvTaskStartScheduler()です。

スケジューラー自身は、実は最高プライオリティを持つタスクです。従ってユーザタスクよりも優先的に処理されますが、実態はユーザタスクと変わりません。


第2部までに登場したRTOS API

RTOSのタスク管理を説明しました。第1部~第2部で登場したFreeRTOSのAPIが下記です。FreeRTOS APIレファレンスマニュアルで詳細が解ります。

vTaskCreate()、vTaskDelete()、uxTaskPriorityGet()、vTaskPrioritySet()、vTaskDelay()、vTaskStartScheduler()、
vTaskSuspend()、vTaskResume()。
vTaskSuspend()、vTaskResume()の2つは、第3部で解説します。


第3部:RTOSタスク間のデータ通信、同期、排他制御

各タスクが独立=バラバラで動作する場合には、第2部に示したスケジューラーのRunning切り替えのみでもRTOSを使ったマルチタスクとしては十分機能します。

実際、LPCXpresso付属のタスク数1個のサンプルソフトを理解するには、第2部までの説明で十分です。

しかし、あるタスクの結果を待って別タスクが動作するような場合には、結果の待ちや通知、タスク間の同期が必要です。

第3部は、RTOSがどのようにこれらタスク間のデータ通知、同期を行っているかを解説します。

これらの技術を習得すれば、殆ど(7割以上)のソフト開発をFreeRTOSでカバーできるようになります。

第3部:タスク間データ通信、同期、排他制御のまとめ

  • タスクの待ちの制御は、スケジューラーのタスク管理Suspendedが重要な役割を果たす
  • 同期と排他制御は、Semaphore:セマフォ作成後、このセマフォへ、タスクから動作許可Give/Takeにより実現
  • データ通信は、Queue:キュー作成後、このキューへ、タスクからデータSend/Receiveという通信で実現

Suspendedの役目

第2部で示した状態遷移図のSuspendedが、タスクの待ちを実現します。

タスクAとタスクB間の通知や同期には、タスク実行中に別タスクの結果を待つことが必要となります。

タスクAに待ちが発生した時は、vTaskSuspened()のAPIを使ってSuspendedへ移行し、タスクBの結果を受け取ると、RTOSがvTaskResume()のAPIを使ってタスクAをReadyへ戻します。Suspendedも第2部で示したBlocked同様、MCU能力を消費しませんので、待ち期間中に他タスクRunningが可能です。

以上がSuspendedによるタスクの待ちや同期を行う仕組みの簡単な説明です。Blockedと似ていることが解ると思います。違いは、BlockedがRunningからのみ遷移するのに対し、どの状態からでもSuspendedへ遷移できる点です。

次にFreeRTOSでの具体的な方法を示します。


Semaphore:セマフォによるタスク同期

FreeRTOSのSemaphore:セマフォは、バイナリセマフォです。割込みによる同期を図示します。

FreeRTOSのタスク間同期(Semaphore)

割込み処理は、割込みハンドラーと割込みサービスルーティン:ISRの2つで構成します。

割込み発生時、優先順位に応じてMCUハードウエアが自動的にCallするのが割込みハンドラー、実際の割込み処理を記述する部分がISRです。
※図では、Interrupt!がハンドラー、TaskがISRです。

FreeRTOSの割込み同期は、ISRで割込み発生をxSemaphoreTake()で待ちます。割込み発生時、ハンドラーで割込みフラグクリアなどの処理後、xSemaphoreGiveFromISR()で動作許可(図赤丸)を与えます。

この動作許可によりISRのxSemaphoreTake()以降の割り込み処理が実行されます。これが割込み同期の実現方法です。

ISR処理後、動作許可は消えます。再びハンドラーが動作許可を生成するまでISRはSuspendedになります。


Mutex:ミューティックスによる排他制御

FreeRTOSのMutex:ミューティックス排他制御を図示します。

FreeRTOSの排他制御(Mutex)

ミューティックスの場合は、セマフォと異なり初めから動作許可(図赤丸)があります。

この動作許可を初めにTakeしたタスクAのみが共有リソースへアクセスできます。タスクAのアクセス中は、動作許可がないタスクBはxSemaphoreTake()でSuspendedになります。

タスクAのアクセス終了後、動作許可をxSemaphoreGive()で放棄するので、今度はタスクBが共有リソースへアクセスできます。これが排他制御の実現方法です。

つまり、動作の許可を示すバイナリセマフォを同期で使う時はセマフォ、排他制御で使う時はミューティックスと呼ぶだけで、使用するAPIは、どちらもxSemaphoreGive()とxSemaphoreTake()です。

違いは、セマフォ同期のvSemaphoreCreateBinary()では、初期値:動作許可が無いこと、ミューティックス排他制御のxSemaphoreCreateMutex()では、初期値:動作許可が有ることです。


Queues:キューによるタスク間データ通信

FreeRTOSは、Operating SystemですのでMCU資源のユーザによる直接アクセスを嫌います。メモリなどの直接表現ではなく、論理的にメモリを繋げたQueues:キューという手段で、通信という方法によりタスク間データ送受信を行います。

FreeRTOSのタスク間通信Queues:キューは、FIFO:First In First Outとして使います。

タスクAからタスクBへキュー経由でデータ通信する例です。

FreeRTOSのタスク間データ通信(Queues)

受信タスクBは、xQueueReceive()でキューからのデータを受信します。このキューにデータが無い時のみSuspendedへ移行します。Suspended中は、キューデータ有無をRTOSが監視し、データが生じた時はタスクBのxQueueReceive()以降の処理が実行されます。

つまりタスクBは、xQueseReceive()の記述のみでデータ受信処理が実現できます。データ有無による待ち制御は全てRTOS側で行いますので、タスクBは受信処理のみの簡単記述ができます。

キューにより送受信タスクの処理は完全に分離されますが、処理結果のデータは、FIFOなので順序が保たれて通信されます。


第3部に登場したRTOS API

RTOSのタスク間の同期、排他制御、データ通信方法を示しました。第3部で登場したFreeRTOSのAPIが下記です。

セマフォ同期:xSemaphoreCreateBinary()、xSemaphoreGiveFromISR()、xSemaphoreTake()
ミューティックス排他制御:xSemaphoreCreateMutex()、xSemaphoreGive()、xSemaphoreTake()
キューデータ通信:xQueueCreate()、xQueueSend()、xQueueReceive()


第4部:FreeRTOSサンプルソフトウェア

使用頻度が高いFreeRTOS APIを中心に、RTOS要点を第1部~第3部で、なるべく簡潔に解説しました。簡潔にし過ぎて不正確な記述もあります。

しかし、正確さに拘って記述すると文章の量が増え、参考書の和訳になりかねません。
ポイントとなる点をざっと掴んで、開発環境で試し、参考書やマニュアルなどを使って開発者自ら考える、これにより新しい技術を、本当に身に付けることができます。

私は、これを食物の消化に例えます。

これには、出だしでつまずかず、多少間違えてもスムースに学習を進めること(=先ずは食べること)が大切です。食べたものの消化には、時間が掛かります。後で振り返ると、内容や詳細が解るということはよくあります。この時点で参考書を見れば、より効率的にRTOSが習得できます。

学習とは食べること

つまるところ、ソースコードと評価ボードによる開発環境に勝るソフトウェア解説書は無いと思います。

ソースコードを読み理解するのに最低限必要な知識と、実際のマイコンで使えるFreeRTOSサンプルソフトを示し、これを活用してRTOSを習得するのが本ページの目的です。


LPCXpresso54114SDK付属FreeRTOSサンプルコード/プロジェクト

第4部は、MCU RTOS習得環境を使って、第1部から第3部の内容を、より具体的に習得します。
※本内容は、2020年1月~2月の弊社ブログ:FreeRTOSサンプルコード(1)~(5)を再編集しました。サンプルコード毎のより詳しい解説は、リンク先に示したブログ投稿を参照してください。

LPCXpresso54114 SDK付属FreeRTOSサンプルコード/プロジェクト一覧表(タスク数が少ない順)
Project Tasks FreeRTOS APIs Comments Brog
freertos_swtimer 1 xTaskCreate
vTaskStartScheduler
xTimerStart
IDE Console出力
ユーザ作成Software Timerデモ
2020-01-31
freertos_hello 1 xTaskCreate
vTaskStartScheduler
vTaskSuspend
IDE Console出力
freertos_usart 1 xTaskCreate
vTaskStartScheduler
vTaskSuspend
Usart 115200bps 8-Non-1送受信4B受信後エコーバック
freertos_tickless 2 xTaskDealy
vTaskGetTickCount
xSemaphoreCreateBinary
xSemaphoreGiveFromISR
xSemaphoreTake
FreeRTOS低電力動作とSW_taskの2タスク並列動作説明。
Tickless_taskはvTaskDelay、hello_taskはvTaskSuspendの差。
SW_taskはTickless_taskに何ら影響を与えない。
2020-02-07
freertos_i2c 2 xSemaphoreCreateBinary
xSemaphoreGiveFromISR
xSemaphoreTake
master_taskとslave_taskの2タスク構成。正常動作結果は、Console窓出力。
freertos_spi 2 xSemaphoreCreateBinary
xSemaphoreGiveFromISR
xSemaphoreTake
同上
freertos_mutex 2 xSemaphoreCreateMutex
xSemaphoreGive
並列動作の共有リソース同期/競合制御。
taskYIELDは要注意!
Mutexのセマフォ作成は、xSemaphoreCreateMutex。
2020-02-14
freertos_sem 1+3 xSemaphoreGive Semaphoreのセマフォ作成は、xSemaphoreCreateBinary。
freertos_event 3 xEventGroupCreate
xEventGroupSetBits
xEventGroupWaitBits
タスクや割込みなどのイベントをグループ化し、他タスク制御。
セマフォと似ているがイベント間論理演算可能。
2020-02-21
freertos_queue 3 xQueueCreate
xQueueSend
xQueueReceive
xQueueAddToRegistory
タスク間メッセージ通信デモ。
キューは順序維持FIFO構造。
freertos_generic 3 追加APIなし キュー、ソフトウェアタイマ、セマフォ組合せデモ。
FreeRTOS.orgサンプルコードを基にNXP作成。

※FreeRTOS APIの接頭語x/vは、API戻り値型を示し、v:void、x:結果コードまたはハンドル。

LPCXpresso54114のSDK付属FreeRTOSサンプルコード11個を、タスク数が少ないプロジェクト順に並び換えた表です。FreeRTOSソフトウェアは、タスク数が少ない方が理解しやすく、タスクプライオリティ設定なども不要だからです。
つまり、表の一番上が最も簡単なプロジェクト、下方につれて複雑になります。

第4部:サンプルコード/プロジェクトのFreeRTOS API利用法まとめ

  • FreeRTOS初期設定(タスク登録とスケジューラー起動)が、通常のベアメタル初期設定後に追加
  • タスク数1のプロジェクトは、PRINTF活用のFreeRTOSタスク単体デバッグの手本
  • タスク正常終了後は、vTaskSuspend処理
  • UART0利用VCOM送受信タスク(uart_task)は、移植性が高く、流用・応用が容易
  • 低電力動作(Sleep)は、vTaskDelay(msec)で低電力動作開始と復帰
  • タスク数2のプロジェクトは、タスク並列動作が解り易く、プライオリティ設定とその意味も理解容易
  • I2C/SPI割込みISRとのタスク同期に、バイナリセマフォ利用
  • 割込みcallback関数でセマフォをgive → 割込み処理タスクでセマフォをtake → セマフォ消滅
  • IoT MCUは、セキュティデバイスとのI2C接続可能性大
  • タスク並列動作副作用の共有リソースアクセス競合回避手段に、ミューテックスがある
  • LPCXpresso54114 のFreeRTOS共有リソース利用途中には、taskYIELDが必要
  • 初期値(pdTRUE)の有無が、ミューテックス作成とセマフォ作成で異なる
  • バイナリセマフォの排他制御利用例に、ランデブーモデル同期がある
  • 複数セマフォを1つにまとめたイベントグループタスク制御は、イベント間の論理演算が可能
  • キュー利用のタスク間メッセージ通信は、深さ設定にプロトタイプ開発が有効
  • freertos_genericは、SDK付属サンプルコード10個の習得度評価に使える
  • 全プロジェクトでメモリ使用法は、heap_4を利用

SDK付属の11個サンプルコード/プロジェクトから得られる内容は、多岐にわたり、一覧表の順に列挙すると上記になります。

実はこれら以外にも、例えば、タスク優先順位やメモリ使用法を変えるとタスク動作はどう変化するか、FreeRTOSのMCUリソース使用量はどの程度かなど、RTOSソフトウェア開発で当然知っておくべき内容も知ることができます。

言わば、これらFreeRTOSサンプルコードは、ベアメタル開発のLチカサンプルに相当する訳です。

MCUXpresso IDEには、FreeRTOS動作解析ツールも付属しています。じっくりとこれらのサンプルコードを味わって、FreeRTOSを習得してください。

なお、RTOSを使わないベアメタルソフト開発には、各種マイコン対応のテンプレートを1000円(税込)にて販売中です。詳細はマイコンテンプレートを参照してください。

[PR] おすすめ情報