FreeRTOS カーネルの基礎 - FreeRTOS

翻訳は機械翻訳により提供されています。提供された翻訳内容と英語版の間で齟齬、不一致または矛盾がある場合、英語版が優先します。

FreeRTOS カーネルの基礎

FreeRTOS カーネルは、多くのアーキテクチャをサポートするリアルタイムのオペレーティングシステムです。組み込みマイクロコントローラアプリケーションの構築に最適です。次の機能があります。

  • マルチタスクスケジューラ。

  • 複数のメモリ割り当てオプション (完全に静的に割り当てられたシステムを作成する機能を含む)。

  • タスク通知、メッセージキュー、複数タイプのセマフォ、ストリームおよびメッセージバッファを含むタスク間調整のプリミティブ。

  • マルチコアマイクロコントローラーでの対称型マルチプロセッシング (SMP) のサポート。

FreeRTOS カーネルは、クリティカルなセクションや割り込みの中でリンクされたリストを処理するなど、非決定的なオペレーションは実行しません。FreeRTOS カーネルには、タイマーが処理を必要としない限り CPU 時間を使用しない効率的なソフトウェアタイマーが実装されています。ブロックされたタスクは、時間を消費する定期的な処理を必要としません。タスクへのダイレクト通知により、実質的に RAM オーバーヘッドが無くなり、タスクシグナリングが高速になります。これらの機能は、ほとんどのタスク間、および割り込みとタスク間でのシグナリングのシナリオで使用できます。

FreeRTOS カーネルは、小さく、シンプルで使いやすく設計されています。一般的な RTOS カーネルバイナリイメージは、4000〜9000 バイトの範囲です。

FreeRTOS カーネルに関する最新のドキュメントについては、FreeRTOS.org を参照してください。FreeRTOS.org は、クイックスタートガイドや、さらに詳しいFreeRTOS リアルタイムカーネルをマスターするなど、FreeRTOS カーネルの使用についての詳細なチュートリアルとガイドを数多く提供しています。

FreeRTOS カーネルスケジューラ

RTOS を使用する組み込みアプリケーションは、独立したタスクのセットとして構成できます。各タスクは、他のタスクに依存することなく、独自のコンテキスト内で実行されます。どの時点においても、アプリケーション内の 1 つのタスクだけが実行されます。リアルタイム RTOS スケジューラは、各タスクの実行時期を決定します。各タスクには独自のスタックが用意されています。他のタスクを実行できるようにタスクをスワップすると、そのタスクの実行コンテキストがタスクスタックに保存されるため、後で同じタスクをスワップバックして実行を再開すると復元できます。

決定的なリアルタイム動作を提供するために、FreeRTOS タスクスケジューラは、タスクに厳密な優先順位を割り当てます。RTOS は、実行可能な最優先タスクに確実に処理時間が与えられるようにします。これは、同時に実行する準備ができている場合に、等しい優先順位のタスク間で処理時間を共有することを意味します。FreeRTOS は、実行準備が整っているタスクが他にない場合にのみ実行されるアイドルタスクも作成します。

メモリ管理

このセクションでは、カーネルのメモリ割り当てとアプリケーションのメモリ管理について説明します。

カーネルメモリの割り当て

RTOS カーネルは、タスク、キュー、または他の RTOS オブジェクトが作成されるたびに RAM を必要とします。RAM は以下のように割り当てることができます。

  • コンパイル時に静的に

  • RTOS API オブジェクト作成関数によって RTOS ヒープから動的に

RTOS オブジェクトを動的に作成する場合、標準の C ライブラリ malloc()free() 関数を使用することは、次の理由から必ずしも適切ではありません。

  • 組み込みシステムでは使用できない可能性があります。

  • 貴重なコードスペースを占有します。

  • 通常はスレッドセーフではありません。

  • 決定的ではありません。

これらの理由から、FreeRTOS はメモリ割り当て API をポータブル層に保持します。ポータブル層は、コア RTOS 機能を実装するソースファイルの外部にあるため、開発中のリアルタイムシステムに適したアプリケーション固有の実装を提供できます。RTOS カーネルに RAM が必要な場合は、malloc() の代わりに pvPortMalloc() を呼び出します。RAM が解放されると、RTOS カーネルは free() の代わりに vPortFree() を呼び出します。

アプリケーションメモリ管理

アプリケーションがメモリを必要とする場合、FreeRTOS ヒープからメモリを割り当てることができます。FreeRTOS には、複雑さと機能に幅があるいくつかのヒープ管理スキームがあります。独自のヒープ実装を提供することもできます。

FreeRTOS カーネルには、次の 5 つのヒープ実装が含まれています。

heap_1

最も簡単な実装です。メモリを解放することはできません。

heap_2

メモリを解放することはできますが、フリーブロックに隣接するメモリを結合することはできません。

heap_3

スレッドの安全性のために標準の malloc()free() をラップします。

heap_4

断片化を避けるために、隣接するフリーブロックを結合します。絶対アドレス配置オプションを含みます。

heap_5

これは heap_4 に似ています。ヒープは複数の隣接していないメモリ領域にまたがることができます。

タスク間の調整

このセクションには、FreeRTOS プリミティブについての情報が含まれています。

キュー

キューは、タスク間通信の主要な形式です。タスク間や、割り込みとタスク間でメッセージを送信するために使用できます。ほとんどの場合、スレッドセーフな先入れ先出し (FIFO) バッファとして使用され、新しいデータがキューの後ろに送られます。(データはキューの先頭に送ることもできます) メッセージはコピーでキューに送られます。つまり、データへの参照を単に格納するのではなく、データ (より大きなバッファへのポインタでも可能) 自体がキューにコピーされます。

キュー API は、ブロック時間を指定することを許可します。タスクが空のキューからの読み取りを試行すると、タスクは、データがキューで使用可能になるか、ブロック時間が経過するまでブロック状態になります。ブロック状態のタスクは CPU 時間を消費せずに他のタスクを実行できます。同様に、タスクがフルキューに書き込もうとすると、タスクはキュー内のスペースが使用可能になるかブロック時間が経過するまでブロック状態になります。複数のタスクが同じキューでブロックされている場合は、優先度の最も高いタスクが最初にブロック解除されます。

タスクへのダイレクト通知やストリーム、メッセージバッファなどの他の FreeRTOS プリミティブは、多くの一般的な設計シナリオでキューに対する軽量の代替手段を提供します。

セマフォとミューテックス

FreeRTOS カーネルは、相互排除と同期のためにバイナリセマフォ、カウントセマフォ、およびミューテックスを提供します。

バイナリセマフォは 2 つの値しか持てません。これらは、(タスク間またはタスクと割り込みの間の) 同期の実装に適しています。カウンティングセマフォは、2 つ以上の値を持てます。これにより、多くのタスクがリソースを共有したり、より複雑な同期操作を実行できます。

ミューテックスは、優先度継承メカニズムを含むバイナリセマフォです。つまり、現在優先度の低いタスクが保持しているミューテックスを取得しようとしている際に優先度の高いタスクがブロックした場合、トークンを保持しているタスクの優先度を一時的にブロックタスクの優先度に上げます。このメカニズムは、発生した優先度逆転を最小限に抑えるために、より高い優先度のタスクのブロックされた状態ができるだけ短時間になるよう設計されています。

直接タスク通知

タスク通知により、セマフォのような別個の通信オブジェクトを必要とせずに、タスクは他のタスクとやり取りし、割り込みサービスルーチン (ISR) と同期することができます。各 RTOS タスクには、通知に内容があればそれを格納するために使用される 32 ビットの通知値があります。RTOS タスク通知は、受信タスクのブロックを解除し、オプションで受信タスクの通知値を更新することができるタスクに直接送信されるイベントです。

RTOS タスク通知は、バイナリとカウンティングセマフォ、場合によってはキューの代わりに、より高速で軽量の代替として使用できます。タスク通知は、同等の機能を実行できる他の FreeRTOS 機能よりも、スピードおよび RAM フットプリントの両方で利点があります。ただし、タスク通知は、イベントの受信側になることができるタスクが 1 つしかない場合にのみ使用できます。

ストリームバッファ

ストリームバッファは、バイトのストリームを割り込みサービスルーチンからタスクに、またはあるタスクから別のタスクに渡すことができます。バイトストリームは任意の長さにすることができ、必ずしも先頭または末尾を必要としません。任意の数のバイトを一度に書き込み、および読み取りすることができます。プロジェクトに stream_buffer.c ソースファイルを含めることで、ストリームバッファ機能を有効にします。

ストリームバッファは、バッファ (ライター) に書き込むタスクまたは割り込みが 1 つだけであり、バッファ (リーダー) から読み取るタスクまたは割り込みが 1 つしかないことを前提としています。ライターとリーダーが異なるタスクになることやサービスルーチンを中断することは安全と言えますが、複数のライターやリーダーがあるのは安全とは言えません。

ストリームバッファの実装では、タスクへのダイレクト通知が使用されます。したがって、呼び出し元のタスクをブロックされた状態に配置するストリームバッファ API を呼び出すと、呼び出し元のタスクの通知状態と値が変更される可能性があります。

データの送信

xStreamBufferSend() は、タスク内のストリームバッファにデータを送信するために使用されます。xStreamBufferSendFromISR() は、割り込みサービスルーチン (ISR) 内のストリームバッファにデータを送信するために使用されます。

xStreamBufferSend() で、ブロック時間の指定が行えます。xStreamBufferSend() が呼び出され、ストリームバッファへの書き込みブロック時間が 0 以外になっている場合、バッファがいっぱいであると、メッセージバッファ領域が使用可能になるか、またはブロック時間が切れるまで、タスクはブロック状態になります。

sbSEND_COMPLETED() および sbSEND_COMPLETED_FROM_ISR() は、データがストリームバッファに書き込まれたときに (FreeRTOS API によって内部的に) 呼び出されるマクロです。更新されたストリームバッファのハンドルが必要です。これらのマクロはどちらも、データ待ちのストリームバッファにブロックされているタスクがあるかどうかを確認し、そのようなタスクが存在する場合はブロック状態からタスクを削除します。

このデフォルトの動作は、FreeRTOSConfig.hsbSEND_COMPLETED() の独自の実装を提供することで変更できます。これは、ストリームバッファを使用してマルチコアプロセッサ上のコア間でデータを渡す場合に便利です。このシナリオでは、他の CPU コアに割り込みを生成するために sbSEND_COMPLETED() を実装することができ、割り込みのサービスルーチンは xStreamBufferSendCompletedFromISR() API を使用してデータを待機しているタスクをチェックし、必要に応じてブロックを解除します。

データの受信

xStreamBufferReceive() は、タスク内のストリームバッファからデータを読み込むために使用されます。xStreamBufferReceiveFromISR() は、割り込みサービスルーチン (ISR) 内のストリームバッファからデータを読み取るために使用されます。

xStreamBufferReceive() で、ブロック時間の指定が行えます。ストリームバッファから読み出すブロック時間が 0 以外で xStreamBufferReceive() が呼び出され、かつバッファが空の場合、指定された量のデータがストリームバッファで使用可能になるか、またはブロック時間が切れるまで、タスクはブロックされた状態になります。

タスクがブロック解除される前にストリームバッファ内に必要なデータの量は、ストリームバッファのトリガーレベルと呼ばれます。トリガーレベル 10 でブロックされたタスクは、少なくとも 10 バイトがバッファに書き込まれるか、タスクのブロック時間が切れるとブロック解除されます。トリガーレベルに達する前に読み出しタスクのブロック時間が終了すると、タスクはバッファに書き込まれたすべてのデータを受け取ります。タスクのトリガーレベルは、1 からストリームバッファサイズの間の値に設定する必要があります。xStreamBufferCreate() が呼び出されると、ストリームバッファのトリガーレベルが設定されます。また、xStreamBufferSetTriggerLevel() を呼び出すことで変更できます。

sbRECEIVE_COMPLETED() および sbRECEIVE_COMPLETED_FROM_ISR() は、ストリームバッファからデータを読み込むときに (FreeRTOS API によって内部的に) 呼び出されるマクロです。マクロは、ストリームバッファに領域が使用可能になるまで待機しているブロックされたタスクがあるかどうかを確認し、そのようなタスクがあれば、ブロック状態から削除します。sbRECEIVE_COMPLETED() のデフォルトの動作は、FreeRTOSConfig.h に代替の実装を提供することで変更できます。

メッセージバッファ

メッセージバッファでは、可変長の個別メッセージを割り込みサービスルーチンからタスクに、またはあるタスクから別のタスクに渡すことができます。たとえば、長さが 10、20 および 123 バイトのメッセージは、すべて同じメッセージバッファに書き込まれ、そこから読み取られます。10 バイトのメッセージは、個々のバイトではなく、10 バイトのメッセージとしてのみ読み取ることができます。メッセージバッファはストリームバッファの実装に基づいています。プロジェクトに stream_buffer.c ソースファイルを含めることで、メッセージバッファ機能を有効にすることができます。

メッセージバッファは、バッファ (ライター) に書き込むタスクまたは割り込みが 1 つだけであり、バッファ (リーダー) から読み取るタスクまたは割り込みが 1 つしかないことを前提としています。ライターとリーダーが異なるタスクになることやサービスルーチンを中断することは安全と言えますが、複数のライターやリーダーがあるのは安全とは言えません。

メッセージバッファの実装では、タスクへのダイレクト通知が使用されます。したがって、呼び出し元のタスクをブロックされた状態に配置するストリームバッファ API を呼び出すと、呼び出し元のタスクの通知状態と値が変更される可能性があります。

メッセージバッファが可変サイズのメッセージを処理できるようにするため、メッセージバッファの前に各メッセージの長さがメッセージ自体に書き込まれます。長さは、size_t 型の変数に格納されます。これは、32 バイトのアーキテクチャでは通常 4 バイトです。したがって、メッセージバッファに 10 バイトのメッセージを書き込むと、実際には 14 バイトのバッファスペースが消費されます。同様に、メッセージバッファに 100 バイトのメッセージを書き込むと、実際には 104 バイトのバッファスペースが使用されます。

データの送信

xMessageBufferSend() は、タスクからメッセージバッファにデータを送信するために使用されます。xMessageBufferSendFromISR() は、割り込みサービスルーチン (ISR) からメッセージバッファにデータを送信するために使用されます。

xMessageBufferSend() で、ブロック時間の指定が行えます。メッセージバッファへ書き込むブロック時間が 0 以外で xMessageBufferSend() が呼び出され、かつバッファがいっぱいの場合、スペースがメッセージバッファ内で使用可能になるか、またはブロック時間が切れるまで、タスクはブロックされた状態になります。

sbSEND_COMPLETED() および sbSEND_COMPLETED_FROM_ISR() は、データがストリームバッファに書き込まれたときに (FreeRTOS API によって内部的に) 呼び出されるマクロです。これは更新されたストリームバッファのハンドルである単一のパラメータをとります。これらのマクロはどちらも、データ待ちのストリームバッファにブロックされているタスクがあるかどうかを確認し、存在する場合、マクロはブロック状態からタスクを削除します。

このデフォルトの動作は、FreeRTOSConfig.hsbSEND_COMPLETED() の独自の実装を提供することで変更できます。これは、ストリームバッファを使用してマルチコアプロセッサ上のコア間でデータを渡す場合に便利です。このシナリオでは、他の CPU コアに割り込みを生成するために sbSEND_COMPLETED() を実装することができ、割り込みのサービスルーチンは xStreamBufferSendCompletedFromISR() API を使用してデータを待機していたタスクをチェックし、必要に応じてブロックを解除します。

データの受信

xMessageBufferReceive() は、タスク内のメッセージバッファからデータを読み込むために使用されます。xMessageBufferReceiveFromISR() は、割り込みサービスルーチン (ISR) 内のメッセージバッファからデータを読み取るために使用されます。xMessageBufferReceive() は、ブロック時間を指定できるようにします。メッセージバッファから読みだすブロック時間が 0 以外で xMessageBufferReceive() が呼び出され、かつバッファが空の場合、データが使用可能になるか、またはブロック時間が切れるまで、タスクはブロックされた状態になります。

sbRECEIVE_COMPLETED() および sbRECEIVE_COMPLETED_FROM_ISR() は、ストリームバッファからデータを読み込むときに (FreeRTOS API によって内部的に) 呼び出されるマクロです。マクロは、ストリームバッファに領域が使用可能になるまで待機しているブロックされたタスクがあるかどうかを確認し、そのようなタスクがあれば、ブロック状態から削除します。sbRECEIVE_COMPLETED() のデフォルトの動作は、FreeRTOSConfig.h に代替の実装を提供することで変更できます。

対称型マルチプロセッシング (SMP) のサポート

FreeRTOS カーネルでの SMP サポートにより、FreeRTOS カーネルの 1 つのインスタンスで複数の同じプロセッサコアにわたるタスクをスケジュールできます。コアアーキテクチャは同じで、同じメモリを共有する必要があります。

FreeRTOS-SMP カーネルを使用するようにアプリケーションを変更する

追加の API を除き、FreeRTOS API は、シングルコアバージョンと SMP バージョンで実質的に異なる点はありません。そのため、FreeRTOS シングルコアバージョン用に作成されたアプリケーションは、ほとんど変更することなく、SMP バージョンでコンパイルできます。ただし、シングルコアアプリケーションでは当てはまる前提がマルチコアアプリケーションでは当てはまらない可能性があるため、機能上の問題が発生する場合があります。

一般的な前提の 1 つは、優先度の高いタスクが実行されている間は、優先度の低いタスクを実行できないというものです。これはシングルコアシステムでは当てはまりましたが、複数のタスクを同時に実行できるため、マルチコアシステムには当てはまりません。アプリケーションが相互排他を実現するために相対的なタスクの優先順位に依存している場合、マルチコア環境で予期しない結果が発生する可能性があります。

もう 1 つの一般的な前提は、ISR を相互にまたは他のタスクと同時に実行できないということです。これは、マルチコア環境では当てはまりません。アプリケーション作成者は、タスクと ISR 間で共有されるデータにアクセスするときは、適切な相互排他を確保する必要があります。

ソフトウェアタイマー

ソフトウェアタイマーは、設定された時間に機能が実行されるようにします。タイマーによって実行される関数は、タイマーのコールバック関数と呼ばれます。タイマーが開始されてからコールバック関数が実行されるまでの時間をタイマーの周期と呼びます。FreeRTOS カーネルは以下のように、効率的なソフトウェアタイマー実装を提供します。

  • 割り込みコンテキストからはタイマーコールバック関数を実行しません。

  • タイマーが実際に切れない限り、処理時間は消費されません。

  • ティック割り込みに処理オーバーヘッドを追加することはありません。

  • 割り込みを無効にしている間は、リンクリストの構造を処理しません。

低電力サポート

ほとんどの組込みオペレーティングシステムと同様に、FreeRTOS カーネルは、時間を測定するために使用する、周期的なティック割り込みを生成するためにハードウェアタイマーを使用します。通常のハードウェアタイマー実装は、ティック割り込みを処理するために低電力状態を定期的に終了し、再び低電力状態にする必要があるため、省電力性に限りがあります。ティック割り込みの周期が早すぎる場合、ティックごとの低電力状態の開始/終了時に電力と時間が消費されるため、最低レベルの省電力モードを除くすべての省電力モードで、上限ゲインを上回ることなります。

この制限に対処するために、FreeRTOS には低電力アプリケーション用のティックレスタイマーモードがあります。FreeRTOS のティックレスアイドルモードは、アイドル期間 (実行可能なアプリケーションタスクがない期間) に周期ティック割り込みを停止し、ティック割り込みが再開されたときに RTOS ティックカウント値を修正します。ティック割り込みを停止すると、割り込みが発生するか、RTOS カーネルがタスクを準備完了状態に移行するまで、マイクロコントローラはディープレベルの省電力状態になります。

カーネル設定

FreeRTOSConfig.h ヘッダーファイルを使用して、特定のボードおよびアプリケーション用に FreeRTOS カーネルを設定できます。カーネル上にビルドされたすべてのアプリケーションでは、そのプリプロセッサインクルードパスに FreeRTOSConfig.h ヘッダーファイルが必要です。FreeRTOSConfig.h は、アプリケーションに固有であり、FreeRTOS カーネルのいずれかのソースコードディレクトリにではなく、アプリケーションディレクトリに配置する必要があります。

FreeRTOS デモおよびテストアプリケーション用の FreeRTOSConfig.h ファイルは freertos/vendors/vendor/boards/board/aws_demos/config_files/FreeRTOSConfig.hfreertos/vendors/vendor/boards/board/aws_tests/config_files/FreeRTOSConfig.h にあります。

FreeRTOSConfig.h に指定できる設定パラメータのリストについては、FreeRTOS.org を参照してください。