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

マイコンRTOS習得

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

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

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

FreeRTOS習得の開発環境

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

  • 使用RTOS:FreeRTOS v8.0.1 (NXPのIDE:LPCXpresso v8.2.2無料版に付属、LPCOpen v2.19も併用、全て2017年3月版)
  • 評価ボード:NXP LPCXpresso824-MAX
  • RTOSサンプルソフト:Lチカ(LPCXpresso付属版に日本語コメント付加)、タスク間通信、同期、排他制御(弊社作成サンプルソフト)

選定理由は、(1)MCUのLPC824で動作するFreeRTOSがポーティング済み、(2)入手性が良く低価格な評価ボード、(3)RTOS基本動作のBlinky:Lチカ、通信、同期、排他制御のサンプルソフトを使ってRTOSを効率的に学べる、ことです。

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

掲載内容


第1部:RTOSの利点

RTOSサンプルソフトで提供している「Lチカサンプルソフト」を例に、RTOSを使うメリットを解説します。

第1部:RTOS利点のまとめ

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

RTOSの必要性

評価ボード実装済みの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タスクのみが登録された場合、LPCXpresso824ボードなら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付属のfreertos_blinkyサンプルソフトを理解するには、第2部までの説明で十分です。

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

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

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

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

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

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での具体的な方法を示します。


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なので順序が保たれて通信されます。


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()では、初期値:動作許可が有ることです。


第3部に登場したRTOS API

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

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


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

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

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

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

学習とは食べること

ソフト開発は、つまるところ「ソースコードと評価ボードによる開発環境に勝る解説書は無い」と思います。
ソースコードを読み理解するのに最低限必要な知識と、実際のマイコンで使えるFreeRTOSサンプルソフトを示す、これが本RTOSページの目的です。

最初に述べたようにIoTマイコンでは、複雑な通信処理のためにRTOSが必要になると思います。今のうちにRTOSへ慣れておくと良いでしょう。

そこで、提案したRTOS開発環境で動作し、RTOS習得や理解が進むソースコード付きのRTOSサンプルソフトを無償公開します。

サンプルソフトは、NXP製LPCXpresso824-MAX評価ボードで動作します。LPCXpresso812、LPCXpresso1114/5の動作確認状況が下表です。

FreeRTOSサンプルソフト動作確認表

※LPCXpresso824-MAXで動作するソースを使って、IO割付、利用LPCOpenライブラリのみ変更しても他の評価ボードでは、動作確認ができません。今のところこの原因などは不明です。原因判明しましたら、本サイトへ記載します。

第3部のタスク間データ通知、同期、排他制御の弊社作成サンプルソースや、日本語コメントを追加した第1回のLチカサンプルソース、付属説明資料には、IDEへのインポート方法やTipsなども記載しています。

RTOSサンプルソフトのメリットは、下記です。

  • より具体的に、日本語コメント付きソースコードを参照しながらFreeRTOS習得や理解ができる
  • 評価ボードで動作確認できるソースコードが付いているので、開発の出だしつまずき回避に有効
  • 従来ソフトの処理が複雑な箇所にのみRTOSを利用するなどの「ハイブリッド構成」を試すことも可能

RTOSのAPIは、多くのパラメタを含みます。パラメタを変えた時に、どのように動作が変わるかサンプルソースに修正を加え、評価ホードで試すことができます。
これは、結構重要です。食べ方を自分で変えて消化することに相当するからです。また、このパラメタ変化を細かに記述する術は(多分)ありません。

しかし実際の開発では、この細かな事柄を知っていないと、トラブルやバグ回避ができません。これが「サンプルソフト+評価ボード」が最上の解説書とする理由です。

RTOSサンプルソフトは、通信処理が複雑なIoTマイコンだけではなく、今後MCUのROM/RAMが増えて、低価格MCUのRTOS活用が現実的になりそうな場合などにも応用できると思います。是非ご活用ください。

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