STM32マイコンとFreeRTOS:RTOSの基本と導入方法
1. はじめに
組み込みシステムの開発において、複数の異なる処理を同時に、かつ決められたタイミングで行う必要性が増しています。例えば、センサーデータの収集、モーター制御、ユーザーインターフェースの応答、ネットワーク通信など、これらすべてを効率的に、かつリアルタイム性を損なわずに実行しなければなりません。
伝統的な組み込みソフトウェア開発では、「スーパーループ」と呼ばれる無限ループ内で、ポーリングや状態遷移を用いて処理を順番に実行する方法がよく用いられてきました。しかし、処理が複雑化したり、時間的な制約(デッドライン)が厳しくなったりすると、スーパーループではシステム全体の応答性を維持することが難しくなります。特に、ある処理がブロック(待ち状態)になると、他のすべての処理が停止してしまうという問題があります。
ここで登場するのが、リアルタイムオペレーティングシステム(RTOS)です。RTOSは、複数のタスク(実行単位)を管理し、それぞれのタスクにCPU時間を適切に割り当てることで、並行処理を実現します。これにより、システムの応答性が向上し、時間的な制約を満たすことが容易になります。また、RTOSが提供するタスク間通信や同期の仕組みを利用することで、複雑なシステムをモジュール化し、開発効率を高めることができます。
STM32マイコンは、STマイクロエレクトロニクス社が製造する高性能かつ多機能なArm Cortex-Mベースのマイクロコントローラーファミリーです。豊富なラインナップ、使いやすい開発ツール、そして活発なコミュニティに支えられ、組み込み開発の分野で非常に広く普及しています。STM32マイコンの高い処理能力と豊富な周辺機能を活かすためには、RTOSとの組み合わせが非常に有効です。
数あるRTOSの中でも、FreeRTOSは特に組み込み分野で人気があります。その理由として、軽量であること、多くのマイコンアーキテクチャをサポートしていること、豊富な機能を提供していること、そして何よりもオープンソース(MITライセンス)であり、商用利用も含めて無償で利用できる点が挙げられます。STM32CubeIDEのような統合開発環境では、FreeRTOSが標準でサポートされており、簡単にプロジェクトに組み込むことができます。
本記事では、まずリアルタイムOSの基本的な概念を解説し、次にFreeRTOSの概要と特徴を紹介します。そして、STM32CubeMXとSTM32CubeIDEを使ったSTM32マイコンへのFreeRTOSの導入方法をステップバイステップで詳しく説明します。最後に、FreeRTOSを使ったアプリケーション開発におけるヒントやよくある問題についても触れます。この記事を通じて、STM32マイコン上でFreeRTOSを使ったマルチタスクプログラミングの基礎を習得し、より高度な組み込みシステム開発に進むための一助となることを目指します。
2. リアルタイムOS (RTOS) の基本概念
2.1 RTOSとは何か?
RTOS(Real-Time Operating System)は、特定の時間的制約(デッドライン)を持つタスクの実行を保証するために設計されたオペレーティングシステムです。「リアルタイム」とは、処理速度が速いという意味ではなく、「決められた時間内に必ず処理を完了させる」という意味です。
RTOSは、システム内の複数の処理(タスク)を管理し、CPU時間をこれらのタスクに適切に割り当てます。主な役割は以下の通りです。
- タスク管理: タスクの生成、削除、実行、停止などを管理します。
- タスクスケジューリング: 実行可能なタスクの中から、どのタスクにCPUを割り当てるかを決定します。
- タスク間通信と同期: 複数のタスクが安全にデータをやり取りしたり、互いに処理のタイミングを合わせたりするためのメカニズム(キュー、セマフォなど)を提供します。
- メモリ管理: タスクが必要とするメモリ(スタック、ヒープ)を管理します。
- 時間管理: システムタイマーを利用して、タスクの遅延実行や周期実行などを実現します。
RTOSは、組み込みシステム、特に自動車、航空宇宙、医療機器、産業用制御システムなど、時間的な正確性が非常に重要となる分野で広く利用されています。
2.2 非RTOS (ベアメタル) との違い
RTOSを使用しない開発手法は「ベアメタル」と呼ばれます。ベアメタル開発では、OSを使わずにハードウェア上で直接アプリケーションコードを実行します。
特徴 | RTOSを使用するシステム | 非RTOS (ベアメタル) システム |
---|---|---|
構造 | 複数のタスクで構成され、RTOSが管理する。 | スーパーループ内で処理を順番に実行するか、割り込みで対応。 |
並行処理 | タスクスイッチにより、擬似的な並行処理を実現。 | 基本的に直列処理。割り込みにより割り込まれる。 |
時間的制約 | スケジューリングにより、時間的制約を満たしやすい。 | 時間的制約を満たすには、ループ設計や割り込み管理が複雑になる。 |
システム応答性 | 特定のタスクがブロックしても、他のタスクは実行可能。 | ある処理がブロックすると、システム全体が停止しやすい。 |
開発効率 | タスク分割によりモジュール化しやすく、開発やデバッグが容易になる。 | スーパーループが肥大化しやすく、変更や拡張が難しい。 |
リソース消費 | RTOSカーネル、タスクスタックなどのメモリを消費する。 | OSのオーバーヘッドがないため、リソース消費は少ない。 |
複雑さ | RTOSの概念やAPIを学習する必要がある。 | 比較的シンプルだが、複雑な処理には高度な設計が必要。 |
リソースが非常に限られている場合や、システムが極めて単純で時間的制約が緩い場合はベアメタル開発が適していることもありますが、ある程度複雑なシステムではRTOSの導入が開発効率、保守性、および時間的制約の遵守において有利になる場合が多いです。
2.3 タスク (Task)
タスクは、RTOSにおける独立した実行単位です。アプリケーションの各機能は、通常、1つ以上のタスクとして実装されます。例えば、センサー監視タスク、モーター制御タスク、通信処理タスクなどが考えられます。
タスクは、独自のスタック領域とプログラムカウンタを持ち、あたかも自分だけがCPUを占有しているかのように記述されます。RTOSは、タスク間でCPUを高速に切り替える(タスクスイッチ)ことで、複数のタスクが同時に実行されているかのように見せかけます。
タスクの状態遷移 (Task States)
RTOSによって管理されるタスクは、通常以下のいずれかの状態にあります。
- Running: CPUを獲得し、実際にコードを実行している状態。同時にこの状態になれるタスクは、シングルコアCPUでは常に1つだけです。
- Ready: 実行可能であるが、より高い優先度のタスクがRunning状態にあるか、またはスケジューラーによってまだCPUが割り当てられていない状態。スケジューラーはReady状態のタスクの中から次に実行するタスクを選択します。
- Blocked: 外部イベント(タイマー満了、キューからのデータ受信待ち、セマフォの獲得待ちなど)が発生するまで、実行を一時停止している状態。イベントが発生するとReady状態に遷移します。
- Suspended: 開発者によって明示的に実行を保留されている状態。Blocked状態とは異なり、外部イベントによっては自動的にReady状態に戻ることはありません。
vTaskResume()
などのAPIを呼び出すことでReady状態に戻ります。
タスクはこれらの状態間を遷移しながら実行されます。この状態遷移を管理するのがRTOSカーネルの中核であるスケジューラーです。
タスク優先度 (Task Priority)
各タスクには優先度が割り当てられます。RTOSは、この優先度に基づいてタスクの実行順序を決定します。優先度が高いタスクは、低い優先度のタスクよりも優先して実行されます。
FreeRTOSでは、優先度は通常0から configMAX_PRIORITIES - 1
までの範囲で設定します (configMAX_PRIORITIES
は FreeRTOSConfig.h
で定義)。優先度が高いほど重要なタスクとみなされます。一般的に、リアルタイム性の要求が厳しいタスク(例: 制御ループ、緊急処理)には高い優先度を、そうでないタスク(例: UI更新、バックグラウンド処理)には低い優先度を割り当てます。
2.4 スケジューリング (Scheduling)
スケジューリングとは、複数のReady状態にあるタスクの中から、次にどのタスクにCPUを割り当てるかを決定するプロセスです。RTOSの性能とリアルタイム性は、スケジューラーの設計に大きく依存します。
プリエンプティブ・スケジューリング (Preemptive Scheduling)
多くのRTOS、FreeRTOSもこれに含まれますが、プリエンプティブ・スケジューリングを採用しています。これは、より高い優先度を持つタスクがReady状態になった場合、現在実行中の低い優先度のタスクの実行を中断(プリエンプト)し、高い優先度のタスクに即座にCPUを割り当てる方式です。これにより、時間的制約が厳しいタスクが確実に優先して実行されることが保証されます。
協調的スケジューリング (Cooperative Scheduling)
協調的スケジューリングでは、現在実行中のタスクは、自らが自主的にCPUを開放する(例: 遅延関数を呼び出す、ブロック状態に入る)まで実行を継続します。より高い優先度のタスクがReady状態になっても、実行中のタスクが自らCPUを開放しない限り、タスクスイッチは発生しません。この方式は実装がシンプルですが、悪意または不注意によってあるタスクがCPUを長時間専有してしまうと、他のタスクが実行できなくなる可能性があります。
タイムスライシング (Time Slicing)
同じ優先度のタスクが複数Ready状態にある場合、どのようにCPUを割り当てるかが問題になります。タイムスライシングでは、同じ優先度のタスクに短い時間(タイムスライス)ごとに順番にCPUを割り当てます。例えば、同じ優先度のタスクA、B、Cがある場合、Aが一定時間実行されたらBに、Bが一定時間実行されたらCに、Cが一定時間実行されたらまたAに、というようにCPUを切り替えます。これにより、同じ優先度を持つタスク間での公平性が保たれます。FreeRTOSでは、configUSE_TIME_SLICING
を1に設定することでタイムスライシングを有効にできます。
2.5 タスク間通信と同期 (Inter-Task Communication and Synchronization)
複数のタスクが協調して動作するためには、互いに情報を交換したり、処理のタイミングを合わせたりする必要があります。RTOSは、このための様々なメカニズムを提供します。
キュー (Queue)
キューは、タスク間でメッセージ(データ)を安全に受け渡しするためのメカニズムです。メッセージはFIFO(先入れ先出し)方式で格納されます。あるタスクがキューにメッセージを「送信」し、別のタスクがキューからメッセージを「受信」します。キューは、タスク間でのデータ転送、または割り込みサービスルーチン(ISR)からタスクへのデータ転送によく利用されます。受信タスクは、キューにメッセージが到着するまでブロックすることも可能です。
セマフォ (Semaphore)
セマフォは、タスク間の同期やリソースへのアクセス制御に用いられます。セマフォにはいくつかの種類があります。
- バイナリセマフォ (Binary Semaphore): 値が0または1のセマフォです。ロック/アンロックや、タスクとISR間の同期(イベント通知)によく使われます。例えば、あるイベントが発生したことをタスクに通知するために、ISRからバイナリセマフォを「解放(give)」し、タスクがそれを「獲得(take)」するのを待つ、という使い方ができます。
- カウンティングセマフォ (Counting Semaphore): 複数のリソースの空き状況を管理したり、特定のイベントが複数回発生したことを通知したりするのに使われます。セマフォの初期値を設定し、タスクがリソースを獲得するたびに値を減らし、解放するたびに値を増やします。値が0のときに獲得しようとしたタスクはブロックされます。
- ミューテックス (Mutex): Mutual Exclusion(相互排他)を意味し、共有リソース(例えば、グローバル変数やハードウェアペリフェラル)への同時アクセスを防ぐためのセマフォです。ミューテックスは、セマフォとは異なり、「所有権」の概念を持ちます。ミューテックスを獲得したタスクだけがそれを解放でき、また、優先度逆転問題を緩和するためのメカニズム(優先度継承など)を備えているのが一般的です(FreeRTOSのミューテックスも優先度継承をサポートしています)。
イベントグループ (Event Group)
イベントグループは、複数のイベントフラグの集合です。タスクは、特定のイベントフラグの組み合わせがセットされるのを待つことができます。複数のイベントが発生したことを待つ必要がある場合に便利です。例えば、「データ受信完了」フラグと「処理開始要求」フラグの両方がセットされるのを待ってから次の処理に進む、といった同期が可能です。
2.6 割り込み処理 (Interrupt Handling)
組み込みシステムでは、外部からのイベント(ボタン押下、センサーデータ到着、タイマー満了など)によって発生する割り込みへの応答が非常に重要です。RTOS環境下でも割り込み処理は実行されますが、いくつかの注意点があります。
- ISRは短い処理に限定する: 割り込みサービスルーチン(ISR)は、可能な限り短く、高速に実行される必要があります。ISRが長時間かかると、他の割り込みやタスクのリアルタイム性が損なわれる可能性があります。
- RTOS APIの利用制限: RTOSのAPIの多くは、タスクコンテキストから呼び出されることを想定しています。ISRから呼び出し可能なAPIは限られています。FreeRTOSでは、ISRから呼び出し可能なAPIは末尾に
FromISR
または_FROM_ISR
が付きます(例:xQueueSendFromISR
)。 - タスクへの通知: ISRで発生したイベントに基づいてタスクに処理を行わせたい場合、ISR内でRTOSの同期プリミティブ(キュー、セマフォ、イベントグループなど)の
FromISR
バージョンを使ってタスクに通知するのが一般的な方法です。これにより、ISRは短く保ちつつ、時間のかかる処理はタスクに任せることができます。
2.7 メモリ管理 (Memory Management)
RTOSシステムでは、タスクスタックやRTOSオブジェクト(キュー、セマフォなど)の作成にメモリが必要です。これらのメモリは、静的に割り当てるか、動的に割り当てるかのいずれかの方法で管理されます。
- スタック (Stack): 各タスクは、関数呼び出し時のローカル変数やリターンアドレスを格納するための独自のスタック領域を持ちます。タスク作成時に必要なスタックサイズを指定します。スタックサイズが不足するとスタックオーバーフローが発生し、システムがクラッシュする原因となります。適切なスタックサイズの見積もりは重要です。
- ヒープ (Heap): RTOSオブジェクトを動的に生成する場合や、
malloc
/free
のような一般的な動的メモリ割り当てを利用する場合に、ヒープメモリが使用されます。FreeRTOSは、組み込みシステム向けにいくつかのヒープ管理方式(Heap_1からHeap_5)を提供しています。- Heap_1: 非常にシンプル。
pvPortMalloc
のみ提供(解放機能なし)。スタックやRTOSオブジェクトの静的割り当てと組み合わせて使う場合に適しています。 - Heap_2: 解放機能付き。単純な最良適合アルゴリズム。断片化しやすい。
- Heap_3: 標準Cライブラリの
malloc
/free
をラップするだけ。スレッドセーフではありません。 - Heap_4: 解放機能付き。断片化を考慮したアルゴリズム。より複雑なシステムに適しています。
- Heap_5: Heap_4に似ているが、複数の不連続なメモリ領域を管理できる。
どのヒープ方式を選択するかは、アプリケーションの要件とメモリの状況によって異なります。STM32CubeIDEでFreeRTOSを有効にすると、デフォルトでは通常Heap_4が選択されます。
- Heap_1: 非常にシンプル。
2.8 RTOSのメリット・デメリット
メリット:
- リアルタイム性の確保: 時間的制約を満たす設計が容易になります。
- 並行処理の実現: 複数の処理を擬似的に同時に実行できます。
- システム構造の明確化: 機能ごとにタスクを分割することで、コードがモジュール化され、理解しやすく、保守性が向上します。
- 開発効率の向上: RTOSが提供するタスク管理やタスク間通信の機能を利用することで、ゼロからこれらの機能を作る手間が省けます。
- 応答性の向上: あるタスクがブロックしても、他のタスクは実行を続けられるため、システム全体の応答性が高まります。
デメリット:
- 学習コスト: RTOSの概念、API、およびマルチタスクプログラミング特有の問題(デッドロック、優先度逆転など)を学習する必要があります。
- リソース消費: RTOSカーネル自体や各タスクのスタック領域のために、非RTOSシステムに比べて多くのROM/RAMを消費します。
- 複雑さの増加: システム全体の起動プロセスや、タスク間の連携を設計・デバッグする際に、非RTOSシステムよりも考慮すべき点が増えます。
- タイミング依存性の問題: マルチタスク環境では、タスクの実行タイミングによって予期せぬ問題(競合状態など)が発生する可能性があり、デバッグが難しくなることがあります。
これらのメリット・デメリットを理解し、システムの要件に合わせてRTOSを導入するかどうかを検討することが重要です。
3. FreeRTOSの概要と特徴
3.1 FreeRTOSとは?
FreeRTOSは、マイクロコントローラーのような小規模な組み込みシステム向けに設計された、軽量かつ高機能なリアルタイムオペレーティングシステムです。Richard Barry氏によって開発が開始され、現在はAmazon Web Services (AWS) によってFreeRTOSプロジェクトとしてホスト・維持されています。
3.2 ライセンス (License: MIT)
FreeRTOSはMITライセンスで配布されています。これは非常に寛容なライセンスであり、個人・商用を問わず、ほとんど制限なく無償で利用、改変、再配布することが可能です。ただし、一部のファイル(特に移植レイヤー)には異なるライセンスが適用されている場合があるため、使用前に確認することが推奨されます。
3.3 主な機能 (Key Features)
FreeRTOSは、基本的なRTOS機能に加えて、組み込み開発に役立つ多くの機能を提供しています。
-
タスク管理 (Task Management):
- タスクの生成 (
xTaskCreate
)、削除 (vTaskDelete
) - タスク優先度の設定、変更 (
vTaskPrioritySet
) - タスクの状態制御(遅延
vTaskDelay
,vTaskDelayUntil
) - タスクへの通知 (
xTaskNotify
,xTaskNotifyGive
,ulTaskNotifyTake
) – 軽量なタスク間通信/同期方法
- タスクの生成 (
-
キュー、セマフォ、ミューテックス、イベントグループ (IPC/Synchronization):
- タスク間で固定長のメッセージを安全にやり取りするためのキュー (
xQueueCreate
,xQueueSend
,xQueueReceive
) - 同期やリソース管理のためのセマフォ(バイナリ、カウンティング)(
xSemaphoreCreateBinary
,xSemaphoreCreateCounting
,xSemaphoreGive
,xSemaphoreTake
) - 共有リソースへの排他アクセスを保証し、優先度逆転を緩和するミューテックス (
xSemaphoreCreateMutex
) - 複数のイベントの同期に使用するイベントグループ (
xEventGroupCreate
,xEventGroupSetBits
,xEventGroupWaitBits
) - ISRから呼び出し可能な対応するAPI (
xQueueSendFromISR
,xSemaphoreGiveFromISR
など)
- タスク間で固定長のメッセージを安全にやり取りするためのキュー (
-
ソフトウェアタイマー (Software Timer):
- ハードウェアタイマーとは別に、指定した時間後にコールバック関数を実行するためのタイマー機能。ワンショットタイマーと周期タイマーが利用可能 (
xTimerCreate
,xTimerStart
,xTimerStop
)。ソフトウェアタイマーは専用のソフトウェアタイマータスクによって管理されます。
- ハードウェアタイマーとは別に、指定した時間後にコールバック関数を実行するためのタイマー機能。ワンショットタイマーと周期タイマーが利用可能 (
-
メモリ管理 (Memory Management):
- 前述のHeap_1からHeap_5までのヒープ管理方式を選択可能。
- タスクスタックの自動アロケーション (
xTaskCreate
) または静的アロケーション (xTaskCreateStatic
) をサポート。
-
ストリームバッファ、メッセージバッファ (Stream Buffer, Message Buffer):
- FreeRTOS V9.0から追加された、より軽量かつ高速なタスク間またはISR-タスク間通信メカニズム。
- ストリームバッファ: 可変長のバイトストリームを送信するのに適しています。
- メッセージバッファ: 可変長の個々のメッセージを送信するのに適しています。
-
トレース機能 (Trace):
- RTOSのイベント(タスクスイッチ、API呼び出しなど)を記録し、実行状況を視覚的に分析するためのトレース機能を提供します。外部ツール(Tracealyzerなど)と連携して使用することで、デバッグや性能解析に非常に役立ちます。
3.4 コンフィグレーション (FreeRTOS Configuration: FreeRTOSConfig.h)
FreeRTOSの動作は、FreeRTOSConfig.h
というヘッダーファイル内のマクロ定義によって詳細にカスタマイズできます。このファイルは、FreeRTOSの移植性(異なるマイコンアーキテクチャへの対応)と、必要な機能だけを有効にしてコードサイズやRAM使用量を削減するための鍵となります。
主なコンフィグレーションオプションの例:
configCPU_CLOCK_HZ
: CPUクロック周波数configTICK_RATE_HZ
: RTOSのタイマーティック割り込み周波数(通常1msごと、1000Hzなど)configMAX_PRIORITIES
: 利用可能な最大優先度数configMINIMAL_STACK_SIZE
: アイドルタスクやタイマータスクなどに必要な最小スタックサイズconfigTOTAL_HEAP_SIZE
: ヒープ管理に使用するメモリ領域の合計サイズ(静的割り当ての場合)configUSE_PREEMPTION
: プリエンプション(優先度によるタスクの横取り)を有効にするか (1:有効, 0:無効 – 協調的スケジューリングになる)configUSE_TIME_SLICING
: 同じ優先度のタスク間でタイムスライシングを有効にするか (1:有効, 0:無効)configUSE_TICKLESS_IDLE
: チックレスアイドルモード(電力消費削減のためティック割り込みを停止する機能)を有効にするかconfigUSE_MUTEXES
: ミューテックス機能を使用するかconfigUSE_COUNTING_SEMAPHORES
: カウンティングセマフォ機能を使用するかconfigUSE_RECURSIVE_MUTEXES
: 再帰的ミューテックス機能を使用するかconfigUSE_QUEUE_SETS
: キューセット機能を使用するかconfigUSE_TASK_NOTIFICATIONS
: タスク通知機能を使用するかconfigUSE_TIMERS
: ソフトウェアタイマー機能を使用するかconfigUSE_EVENT_GROUPS
: イベントグループ機能を使用するかconfigHEAP_ALLOCATION_TYPE
: 使用するヒープ管理方式 (例: 1: Heap_1, 2: Heap_2, 4: Heap_4, 5: Heap_5)configCHECK_FOR_STACK_OVERFLOW
: スタックオーバーフロー検出レベル (0:無効, 1 or 2:有効)configUSE_MALLOC_FAILED_HOOK
:pvPortMalloc
が失敗したときに呼び出されるフック関数を有効にするかconfigUSE_IDLE_HOOK
: アイドルタスクが実行されるときに呼び出されるフック関数を有効にするかconfigUSE_TICK_HOOK
: タイマーティック割り込みごとに呼び出されるフック関数を有効にするか
これらの設定は、アプリケーションの要件に合わせて慎重に検討・設定する必要があります。STM32CubeMXを使用する場合、これらの設定の多くはGUI上で簡単に行うことができます。
4. STM32マイコンへのFreeRTOS導入方法
STM32マイコンへのFreeRTOSの導入は、STマイクロエレクトロニクスが提供する開発ツールであるSTM32CubeMXとSTM32CubeIDEを使用すると非常に容易です。
4.1 開発環境の準備
- IDE: STM32CubeIDEをインストールします。これは、Eclipseベースの統合開発環境で、STM32マイコン向けのコード編集、ビルド、デバッグ機能を持ち、STM32CubeMXの機能も統合されています。
- ハードウェア: プロジェクトで使用するSTM32マイコンを搭載した開発ボード(例: Nucleoボード、Discoveryボード)またはカスタムハードウェアを準備します。
- STM32CubeMX: STM32CubeIDEに統合されています。スタンドアロン版もありますが、統合版が推奨されます。マイコンのペリフェラル設定やクロック設定、ミドルウェア(FreeRTOS含む)の設定を行い、初期化コードを生成するツールです。
4.2 STM32CubeMXを使ったFreeRTOSプロジェクトの作成
ここでは、STM32CubeIDEを使ってプロジェクトを作成する手順を説明します。STM32CubeIDEはCubeMXの機能を含んでいるため、別途CubeMXを起動する必要はありません。
-
STM32CubeIDEを起動し、新しいSTM32プロジェクトを作成します。
File
->New
->STM32 Project
を選択します。
-
使用するマイコンまたは開発ボードを選択します。
MCU/MPU Selector
タブで、使用するSTM32マイコンの型番を入力して選択するか、Board Selector
タブで開発ボードを選択します。ボードを選択すると、必要なピン設定やクロック設定がある程度自動で行われます。ここでは例として、Nucleo-G431KBボードを選択してみます。Next
をクリックします。
-
プロジェクト名を入力し、プロジェクト設定を行います。
Project Name
: 例としてMyFreeRTOSProject
と入力します。Project Type
:STM32Cube
を選択します。Targeted Project Context
:Empty
またはBasic
を選択します。ここではシンプルにEmpty
を選択します。Firmware Package
: 使用するマイコンシリーズのCubeファームウェアパッケージが選択されていることを確認します(Nucleoボードを選択した場合、自動で適切なものが選択されます)。Finish
をクリックします。
-
.iocファイルが開かれ、マイコンのコンフィグレーション画面が表示されます(CubeMXインターフェース)。
-
System Core設定:
System Core
->SYS
を選択します。Timebase Source
:TIM6
やTIM7
など、RTOSのタイマーティックに使用しない別のタイマーを選択します。FreeRTOS自体が別のタイマー(通常Systickまたは指定したタイマー)を使用するため、デフォルトのSystickは避ける方が良い場合が多いです。ここではTIM6
を選択します。System Core
->RCC
を選択します。High Speed Clock (HSE)
: 使用する外部クリスタルやセラミック発振子に合わせて設定します(例:Crystal/Ceramic Resonator
)。Low Speed Clock (LSE)
: 必要に応じて設定します(例: リアルタイムクロックが必要な場合)。
-
GPIO設定 (LED点滅用):
Pinout & Configuration
ビューで、ボード上のLEDに接続されているピンを探します(Nucleo-G431KBではPA5)。- PA5ピンをクリックし、
GPIO_Output
を選択します。 System Core
->GPIO
->PA5
を選択し、User Label
をLD2
など分かりやすい名前に変更します。
-
Middleware -> FreeRTOSの有効化:
Middleware
->FREERTOS
を選択します。Interface
:CMSIS_V2
またはCMSIS_V1
を選択します。CMSIS-RTOS V2はより新しい標準規格で、FreeRTOSのAPIをラップしたより抽象的なAPIを提供します。FreeRTOS独自のAPI (xTaskCreate
,xQueueSend
など) を直接使う場合は、CMSIS_V1
を選択するか、V2を選択した場合でもFreeRTOS独自のAPIを呼び出すことができます。ここではCMSIS_V2
を選択します。Enabled
:Enabled
を選択します。これによりFreeRTOSが有効になります。
-
FreeRTOS設定の調整:
- FreeRTOSを有効にすると、
Configuration
タブにFreeRTOSの設定画面が表示されます。 - Tasks and Queues:
- デフォルトで
defaultTask
が作成されます。必要に応じてタスクを追加、削除、名前変更できます。スタックサイズや優先度もここで設定できます。 - キュー、セマフォ、イベントグループなども初期状態で作成しておきたい場合は、ここで追加できます。
- デフォルトで
- Config Parameters:
FreeRTOSConfig.h
に対応する主要なコンフィグレーションオプションがGUIで表示されます。Timer frequency (Hz)
(configTICK_RATE_HZ
): RTOSのタイマーティック周波数を設定します(例: 1000)。Total heap size (bytes)
(configTOTAL_HEAP_SIZE
): 動的メモリ割り当てに使用するヒープサイズを設定します(例: 8192)。アプリケーションの必要に応じて調整します。Use time slicing
: タイムスライシングを有効/無効にします。Use Mutexes
,Use Counting Semaphores
,Use Recursive Mutexes
,Use Event Groups
,Use Timers
,Use Task notifications
: 使用するRTOSオブジェクト機能を有効/無効にします。不要な機能を無効にすることでコードサイズを削減できます。Check for stack overflow
: スタックオーバーフロー検出レベルを設定します。開発中は有効にしておくことを強く推奨します(レベル1または2)。Heap Allocation
: 使用するヒープ管理方式を選択します(Heap_1, Heap_2, Heap_4, Heap_5)。通常はHeap_4またはHeap_5が便利です。
Advanced settings
など、さらに詳細な設定も可能です。
- FreeRTOSを有効にすると、
-
Clock Configuration:
Clock Configuration
タブを開き、システムクロックを設定します。- 使用するクロックソース(HSE, HSIなど)を選択し、PLL設定などを行い、CPUやAHB/APBバスのクロック周波数を設定します。RTOSのティックレート (
configTICK_RATE_HZ
) とハードウェアタイマーの設定は、ここで設定したクロック周波数に依存します。FreeRTOSの設定画面で入力したTimer frequency (Hz) に合わせて、必要なタイマーの分周比などが自動計算されます。
-
Project Manager設定:
Project Manager
タブを開きます。Project Name
,Project Location
,Toolchain / IDE
(STM32CubeIDE
) などを確認します。Code Generator
設定を確認します。Generate source files and header files
が選択されていることを確認します。
-
コード生成:
- 設定が完了したら、コードを生成します。
- メニューの
Project
->Generate Code
を選択するか、ツールバーの歯車アイコンをクリックします。 - コード生成に関する確認ダイアログが表示されたらOKをクリックします。
これで、FreeRTOSが組み込まれたSTM32プロジェクトのひな形が生成されました。
4.3 生成されたコードの理解
生成されたプロジェクトには、FreeRTOSを使用するための基本的なファイル構造と初期化コードが含まれています。
-
main.c
:- アプリケーションのエントリポイントです。
SystemInit()
: システム初期化コード(通常はスタートアップコードで呼び出されるため、main
ではコメントアウトされています)。MX_GPIO_Init()
,MX_TIM6_Init()
など: CubeMXで設定したペリフェラルの初期化関数が呼び出されます。MX_FREERTOS_Init()
: FreeRTOS関連の初期化関数。タスク、キュー、セマフォなどのRTOSオブジェクトの生成コードがここに記述されています。osKernelStart()
(CMSIS-RTOS V2) またはvTaskStartScheduler()
(FreeRTOSネイティブAPI): RTOSスケジューラーを開始します。この関数が呼び出されると、RTOSがCPU制御権を握り、Ready状態の最も優先度の高いタスクの実行を開始します。この関数は通常戻りません。
-
FreeRTOSConfig.h
:- 前述したFreeRTOSの各種コンフィグレーションマクロが定義されています。CubeMXのGUIで設定した内容が反映されています。必要に応じて手動で編集することも可能ですが、CubeMXで設定可能な項目はGUIで行う方が管理しやすいです。
-
FreeRTOSカーネルファイル:
FreeRTOS/Source
ディレクトリ以下に、FreeRTOSカーネルのソースコード(tasks.c
,queue.c
,list.c
,portable/GCC/ARM_CM4F/port.c
など)が含まれています。通常、これらのファイルを直接編集する必要はありません。
-
CMSIS-RTOS V2 ラッパーファイル (CMSIS_V2を選択した場合):
Middlewares/Third_Party/FreeRTOS/Source/CMSIS_RTOS_V2
ディレクトリ以下に、CMSIS-RTOS V2 APIをFreeRTOS APIにマッピングするラッパーコードが含まれています(cmsis_os2.c
,cmsis_os2.h
)。ユーザーアプリケーションは通常、cmsis_os2.h
をインクルードして、osThreadDef
,osThreadCreate
,osDelay
などのCMSIS-RTOS V2 APIを使用します。
-
タスクファイル:
- CMSIS-RTOS V2を使用する場合、デフォルトで作成された
defaultTask
の実装が記述されているファイル(例:app_freertos.c
またはmain.c
内のタスク関数)が含まれます。CubeMXでタスクを追加した場合、対応する関数宣言や定義が生成されます。
- CMSIS-RTOS V2を使用する場合、デフォルトで作成された
4.4 ユーザーコードの記述
生成されたプロジェクトをベースに、アプリケーション固有の処理を実装します。ここでは、LEDを点滅させる簡単なタスクを追加する例を考えます。
1. 新しいタスクの定義と作成 (CMSIS-RTOS V2を使用する場合):
CubeMXで既にdefaultTask
が作成されている場合は、その関数を変更するか、新しいタスクを追加します。CubeMXで新しいタスクを追加するのが最も簡単ですが、手動でコードを書く場合は以下のようになります。
まず、タスク関数を定義します。タスク関数は引数として void *argument
を受け取り、戻り値は void
です。通常、タスクのメイン処理は無限ループ (for(;;)
または while(1)
) 内に記述します。
c
/* LED点滅用タスク関数 */
void StartLedTask(void *argument)
{
/* 無限ループ */
for(;;)
{
HAL_GPIO_TogglePin(LD2_GPIO_Port, LD2_Pin); // LEDをトグル
osDelay(500); // 500ミリ秒待機
}
}
次に、MX_FREERTOS_Init()
関数内で、このタスクを作成します。CMSIS-RTOS V2 APIを使用します。
“`c
/ Definition for defaultTask /
osThreadId_t defaultTaskHandle;
const osThreadAttr_t defaultTask_attributes = {
.name = “defaultTask”,
.stack_size = 128 * 4, // スタックサイズ (ワード単位 or バイト単位かはコンフィグによる、CubeMXで確認)
.priority = (osPriority_t) osPriorityNormal, // 優先度
};
/ creation of defaultTask /
defaultTaskHandle = osThreadNew(StartDefaultTask, NULL, &defaultTask_attributes);
/ Definition for LedTask / // 新しいタスクを追加する場合
osThreadId_t ledTaskHandle;
const osThreadAttr_t ledTask_attributes = {
.name = “LedTask”,
.stack_size = 128 * 4, // スタックサイズ
.priority = (osPriority_t) osPriorityLow, // 優先度 (例: Normalより低く設定)
};
/ creation of LedTask /
ledTaskHandle = osThreadNew(StartLedTask, NULL, &ledTask_attributes);
“`
osThreadNew()
関数は、タスク関数、引数、タスク属性(名前、スタックサイズ、優先度など)を指定してタスクを作成し、そのハンドルを返します。
2. タスク関数内の処理:
タスク関数 StartLedTask
内では、以下の処理を行っています。
HAL_GPIO_TogglePin(LD2_GPIO_Port, LD2_Pin);
: HALライブラリを使って、LEDに接続されたGPIOピンの状態をトグル(点灯/消灯を反転)させています。LD2_GPIO_Port
とLD2_Pin
はCubeMXでGPIOに名前をつけた場合に生成されるマクロです。osDelay(500);
: 指定した時間(ここでは500ミリ秒)だけ、タスクの実行を遅延させ、Blocked状態に遷移させます。この間に、RTOSスケジューラーは他のReady状態のタスクにCPUを割り当てることができます。osDelay
はCMSIS-RTOS V2の関数で、内部的にはFreeRTOSのvTaskDelay
またはvTaskDelayUntil
を使用します。
重要: タスク関数内の無限ループには、必ずRTOSのAPIによる待ち状態(osDelay
, osMessageQueueGet
, osSemaphoreAcquire
など)や、自発的なCPU解放(協調的スケジューリングの場合の taskYIELD()
など、ただしFreeRTOSはプリエンプティブが基本なので通常不要)を含める必要があります。もしタスクが無限にCPUを専有し続けると、他のタスクが全く実行できなくなってしまいます。
3. main
関数の確認:
main.c
の main
関数内で、システム初期化、ペリフェラル初期化、FreeRTOS初期化 (MX_FREERTOS_Init()
) が順に行われていることを確認し、最後に osKernelStart()
が呼び出されていることを確認します。
“`c
int main(void)
{
/ Reset of all peripherals, Initializes the Flash interface and the Systick. /
HAL_Init();
/ Configure the system clock /
SystemClock_Config(); // クロック設定関数
/ Initialize all configured peripherals /
MX_GPIO_Init();
// 他のペリフェラル初期化…
/ Initilialize the FreeRTOS kernel /
MX_FREERTOS_Init(); // FreeRTOS初期化(タスク作成など)
/ Start scheduler /
osKernelStart(); // スケジューラー開始
/ We should never get here as control is now taken by the scheduler /
for(;;); // スケジューラー開始後はここには来ない
}
“`
MX_FREERTOS_Init()
関数内で定義・作成されたタスクは、osKernelStart()
が呼び出された後に、優先度に基づいてスケジューラーによって実行が開始されます。
4.5 ビルドとデバッグ
コードの記述が完了したら、プロジェクトをビルドしてマイコンに書き込み、デバッグします。
- ビルド: メニューの
Project
->Build Project
を選択するか、ハンマーアイコンをクリックします。コンパイルとリンクが行われ、実行ファイル(.elfなど)が生成されます。ビルドエラーがないことを確認します。 - デバッグ構成の作成: デバッグを行う前に、デバッグ構成を設定します。通常、STM32CubeIDEはデフォルトの構成を自動で作成しますが、必要に応じて修正します。メニューの
Run
->Debug Configurations...
を選択します。STM32 Embedded C/C++ Application
を選択し、新しい構成を作成または既存の構成を編集します。Debugger
タブで、使用するデバッガー(例: ST-Link)が正しく設定されていることを確認します。
- デバッグの開始: メニューの
Run
->Debug
を選択するか、虫アイコンをクリックします。コンパイルがまだ行われていない場合はビルドが実行され、その後マイコンへの書き込み(フラッシュ)が行われます。書き込みが成功すると、デバッグセッションが開始され、通常main
関数の先頭で実行が一時停止します。 - ステップ実行とブレークポイント: デバッガーの機能を使って、コードを1行ずつ実行したり、任意の場所にブレークポイントを設定して実行を一時停止させたりしながら、プログラムの動作を確認します。
- RTOSオブジェクトビューワー: STM32CubeIDEは、FreeRTOSのデバッグに役立つ機能を持っています。
Debug
パースペクティブで、Window
->Show View
->Other...
を選択し、Debug
カテゴリからRTOS
->FreeRTOS
を選択します。これにより、FreeRTOSのタスク、キュー、セマフォなどの状態を確認できるビューが表示されます。- Tasks: 各タスクの状態(Running, Ready, Blockedなど)、優先度、スタック使用量などを確認できます。スタックオーバーフローが発生していないかを確認するのに非常に有用です。
- Queues, Semaphores, Mutexes, Timers: それぞれのRTOSオブジェクトの状態、保持しているデータ、待っているタスクなどを確認できます。
これらのビューを活用することで、マルチタスク環境における問題の原因特定が容易になります。
5. FreeRTOSを使ったアプリケーション開発のヒントとベストプラクティス
RTOSを使った開発は、単一の無限ループで行う開発とは異なる考慮事項があります。ここでは、効率的かつ堅牢なRTOSアプリケーションを開発するためのヒントやベストプラクティスを紹介します。
5.1 タスク設計の考え方
- 機能ごとの分割: アプリケーションを独立した機能単位に分解し、それぞれをタスクとして実装します。これにより、コードがモジュール化され、開発や保守が容易になります。
- 優先度の割り当て:
- リアルタイム性: 時間的な制約(デッドライン)が厳しいタスクには高い優先度を割り当てます。例えば、制御ループ、緊急応答処理など。
- 応答性: UI処理など、ユーザー操作に対する応答性が重要なタスクにも適切な優先度を割り当てる必要があります。
- バックグラウンド処理: リアルタイム性があまり要求されない処理(ログ記録、ネットワーク通信など)には低い優先度を割り当てます。
- 優先度を高くしすぎると、低い優先度のタスクが全く実行できなくなる「スタベーション」が発生する可能性があるため、慎重に設計します。
- スタックサイズの決定: 各タスクに必要なスタックサイズを適切に見積もることは非常に重要です。スタックサイズが小さすぎるとスタックオーバーフローが発生し、システムがクラッシュします。大きすぎると貴重なRAMを無駄に消費します。
- 関数の呼び出し深度、ローカル変数のサイズ、RTOS API呼び出しによって使用されるスタックサイズなどを考慮する必要があります。
- 開発中はスタックオーバーフロー検出 (
configCHECK_FOR_STACK_OVERFLOW
) を有効にし、デバッガーのRTOSビューでタスクスタックの使用状況を監視することを強く推奨します。最大使用量を確認し、余裕を持ったサイズに設定します。 - 関数の呼び出し深度を減らす、大きなローカル変数を避ける、動的メモリ割り当てを制限するなどのコーディング手法もスタック使用量の削減に役立ちます。
5.2 タスク間通信と同期の正しい使い方
- 共有リソースへのアクセス制御: 複数のタスクからアクセスされるグローバル変数やハードウェアレジスタなどの共有リソースは、必ずミューテックスなどで保護する必要があります。保護を怠ると、競合状態が発生し、データが破壊されたり予期せぬ動作を引き起こしたりします。
- デッドロックの回避: 複数のタスクが互いに相手が保持しているリソースの解放を待ち続け、永久に処理が進まなくなる状態(デッドロック)に注意が必要です。複数のリソースを獲得する場合、すべてのタスクが同じ順序で獲得するようにルールを設けるなどの対策が必要です。
- 優先度逆転 (Priority Inversion): 高い優先度のタスクが、低い優先度のタスクが保持しているミューテックスの解放を待っている間に、中間の優先度を持つタスクが実行されてしまう現象です。これにより、高い優先度のタスクの実行が間接的に妨げられ、リアルタイム性が損なわれる可能性があります。FreeRTOSのミューテックスは「優先度継承 (Priority Inheritance)」というメカニズムでこの問題を緩和します。これは、低い優先度のタスクが高い優先度のタスクが待っているミューテックスを保持している間、その低い優先度のタスクの優先度を一時的に高い優先度のタスクと同じかそれ以上に引き上げることで実現します。
- 割り込みとタスク間の通信: ISRからタスクへの通知やデータ受け渡しには、
FromISR
バージョンのRTOS API(xQueueSendFromISR
,xSemaphoreGiveFromISR
,xTaskNotifyFromISR
など)を必ず使用します。また、ISRからこれらのAPIを呼び出した際に、より高い優先度のタスクがReady状態になった場合、ISRからの戻り際に即座にそのタスクにスイッチするように (xHigherPriorityTaskWoken
フラグなどを使って) RTOSに指示する必要があります。
5.3 メモリ管理の注意点
- スタックオーバーフロー (Stack Overflow): 前述の通り、開発中に検出機能を有効にし、十分にテストを行います。リリース版では、検出機能を無効にすることで実行時間を短縮できますが、開発中の十分なテストとサイズ見積もりが前提となります。
- ヒープの断片化 (Heap Fragmentation): 動的なメモリ割り当て/解放を繰り返すと、利用可能なメモリ領域が細切れになり、大きなメモリブロックを確保できなくなる断片化が発生することがあります。システムを長時間稼働させる場合や、様々なサイズのオブジェクトを動的に作成・破棄する場合は、断片化に強いHeap_4やHeap_5を選択したり、固定サイズのバッファプールを用意するなどの対策を検討します。
- 静的割り当ての活用: RTOSオブジェクト(タスク、キュー、セマフォなど)を静的に割り当てる (
xTaskCreateStatic
,xQueueCreateStatic
など) ことも可能です。これにより、ヒープ管理のオーバーヘッドや断片化の問題を避け、システム起動時のメモリ確保失敗を防ぐことができます。ただし、コードサイズは増加する傾向があります。
5.4 タイム測定とプロファイリング
- トレースツール: RTOSの実行状況を詳細に把握するために、トレースツールが非常に有用です。FreeRTOSにはトレース機能が組み込まれており、Percepio社のTracealyzerなどの外部ツールと連携することで、タスクの実行履歴、状態遷移、RTOS API呼び出し、割り込み発生などを時系列で視覚的に確認できます。これにより、予期しないタスクスイッチや遅延、デッドロックなどの問題を効率的に解析できます。
- 実行時間の測定: 特定の処理ブロックの実行時間を測定したい場合は、高分解能タイマーやCPUのサイクルカウンタを利用します。これにより、タスクの計算負荷やボトルネックを特定できます。
5.5 エラーハンドリング
FreeRTOSは、特定の重要なイベントやエラーが発生したときに呼び出される「フック関数」を提供しています。これらのフック関数を実装することで、問題発生時のデバッグやエラー処理を行うことができます。
vApplicationMallocFailedHook()
:pvPortMalloc
(ヒープからの動的メモリ確保) が失敗した場合に呼び出されます。メモリ不足のエラーを検出できます。vApplicationStackOverflowHook()
: スタックオーバーフローが検出された場合に呼び出されます (configCHECK_FOR_STACK_OVERFLOW
が有効な場合)。どのタスクでスタックオーバーフローが発生したかを知る手がかりが得られます。vApplicationIdleHook()
: アイドルタスクが実行されるときに呼び出されます。システムのアイドル状態を利用して、低優先度のバックグラウンド処理(例: CPU使用率の計測、低電力モードへの移行など)を実行するのに利用できます。vApplicationTickHook()
: タイマーティック割り込みごとに呼び出されます。周期的な処理(例: ソフトウェアタイマーの処理、CPU使用率の計測など)を実行するのに利用できます。
これらのフック関数を実装することで、システムの状態をより詳細に監視し、問題発生時に適切な対処を行うことが可能になります。
5.6 効率的なコーディング
- タスクのウェイト状態を活用: タスクが何かを待っている間(データ受信待ち、イベント待ち、時間待ちなど)は、ビジーループでポーリングするのではなく、
osDelay()
,osMessageQueueGet()
,osSemaphoreAcquire()
などのRTOS APIを使って明示的にBlocked状態に遷移させます。これにより、CPUリソースを解放し、他のタスクが実行できるようになります。ビジーループはCPU時間を無駄に消費し、他のタスクの実行を妨げます。 - 割り込み処理の簡潔化: ISR内では必要最低限の処理(例えば、ペリフェラルの状態クリア、データの簡単な読み込み、RTOSオブジェクトへの通知)だけを行い、時間のかかる処理やRTOS API呼び出し(
FromISR
なし)はタスクに任せます。これにより、割り込み応答時間を短く保ち、他の割り込みやタスクのリアルタイム性を保護します。
6. よくある問題とその解決策
FreeRTOSを使った開発において、初心者が見落としがちな、または遭遇しやすい問題とその解決策をいくつか紹介します。
-
タスクが実行されない (Task not running):
- 原因:
- スケジューラー (
osKernelStart()
またはvTaskStartScheduler()
) が呼び出されていない。 - タスクが正しく作成されていない(
osThreadNew()
/xTaskCreate
が成功しているか確認)。 - タスクがすぐにBlocked状態になり、待っているイベントが発生していない。
- 他の高い優先度のタスクがCPUを専有している(無限ループや非常に長い計算処理)。
- タイマーティック割り込みが正しく設定・有効化されていない。
- スケジューラー (
- 解決策:
main
関数の最後にスケジューラー開始関数があるか確認。- タスク作成関数の戻り値を確認。
- デバッガーのRTOSビューでタスクの状態を確認。Blockedになっている場合、何待ちになっているか確認し、対応するイベントが発生しているか追跡。
- 最も優先度の高いReady状態のタスクが期待通りに実行されているか確認。
- CubeMXのClock ConfigurationとNVIC設定で、RTOSのタイマーティックに使用するタイマー(通常Systick)が正しく設定・有効化され、適切な優先度が付与されているか確認。
- 原因:
-
スタックオーバーフロー (Stack overflow):
- 原因:
- タスク作成時に指定したスタックサイズが小さすぎる。
- タスク関数内で深い関数呼び出しや大きなローカル変数が使用されている。
- ISRのスタック(システムスタック)が小さすぎる。
- 解決策:
configCHECK_FOR_STACK_OVERFLOW
を有効にする。フック関数で検出可能。- デバッガーのRTOSビューで各タスクのスタック使用量の最大値を確認し、スタックサイズに十分な余裕を持たせる。
- ISRが使用するシステムスタックサイズ(スタートアップファイルで設定)を確認し、必要なら増やす。
- 可能な限り、深い関数呼び出しや大きなローカル変数を避けるようにコードをリファクタリングする。
- 原因:
-
デッドロック (Deadlock):
- 原因: 複数のタスクが、それぞれ相手がロックしているリソースを互いに待ち合う状態。
- 解決策:
- リソースを獲得する順序をすべてのタスクで統一する。
- 可能な限り、一度に保持するリソースの数を少なくする。
- タイムアウト付きのセマフォ/ミューテックス獲得API (
xSemaphoreTake(..., timeout)
) を使用し、タイムアウトが発生した場合に適切なエラー処理を行う。
-
優先度逆転 (Priority inversion):
- 原因: 低優先度のタスクがミューテックスを保持している間に、中優先度のタスクが高優先度のタスクの実行を妨げる。
- 解決策:
- ミューテックスを使用し、優先度継承を有効にする(FreeRTOSのミューテックスはデフォルトで優先度継承をサポート)。
- 共有リソースへのアクセス時間は可能な限り短くする。
- 優先度の設計を見直す。
-
タスク間通信/同期の失敗 (IPC/Synchronization issues):
- 原因:
- キューやセマフォの使用方法の間違い(例: ISRから
FromISR
なしで呼び出し)。 - 期待したメッセージがキューに届かない、セマフォが解放されない。
- タイムアウトなしで待ち続け、システムが停止する。
- キューやセマフォの使用方法の間違い(例: ISRから
- 解決策:
- RTOS APIのリファレンスをよく確認し、正しいAPI(特にISRから呼び出す場合)を使用する。
- デバッガーのRTOSビューでキューの内容やセマフォの状態、待ちタスクを確認する。
- 重要な待ち処理にはタイムアウトを設定し、タイムアウト時のエラー処理を実装する。
- 原因:
-
タイマーの精度 (Timer accuracy):
- 原因:
- RTOSのタイマーティック周波数 (
configTICK_RATE_HZ
) が低すぎる。 - 他の割り込みや高い優先度のタスクによってティック割り込みが遅延する。
- RTOSのタイマーティック周波数 (
- 解決策:
- 要求されるリアルタイム精度に合わせてティック周波数を設定する。ただし、ティック周波数を高くしすぎると、ティック割り込み処理のオーバーヘッドが増加する。
- 割り込み優先度を適切に設定する。RTOSのティック割り込みよりも高い優先度を持つ割り込みが多すぎると、ティック割り込みの応答性が低下する可能性がある。STM32では、RTOSのシステム割り込み(Systick, PendSV, SysTick)の優先度は、FreeRTOSConfig.hで
configKERNEL_INTERRUPT_PRIORITY
とconfigMAX_SYSCALL_INTERRUPT_PRIORITY
で設定される(数値が小さいほど高優先度)。アプリケーションで使用する割り込みの優先度は、これらの設定と競合しないように注意深く設定する必要がある。 - より高精度の時間測定が必要な場合は、RTOSの機能だけでなく、高分解能タイマーモジュールなどを併用する。
- 原因:
これらの問題は、RTOS開発における典型的なものです。デバッガー、RTOSビュー、トレースツールなどを積極的に活用することで、効率的に原因を特定し、解決に導くことができます。
7. まとめ
本記事では、STM32マイコン上でFreeRTOSを使用するための基礎として、リアルタイムOSの基本概念、FreeRTOSの概要と特徴、そしてSTM32CubeMXとSTM32CubeIDEを使った具体的な導入方法について詳細に解説しました。また、FreeRTOSを使ったアプリケーション開発におけるヒント、ベストプラクティス、そしてよくある問題とその解決策についても触れました。
STM32マイコンの豊富な機能を、FreeRTOSのような強力なRTOSと組み合わせることで、複雑でリアルタイム性が要求される組み込みシステムを、より効率的に、より堅牢に開発することが可能になります。タスク分割によるモジュール化、RTOSが提供する同期・通信メカニズムの活用は、開発効率と保守性の向上に大きく貢献します。
RTOSを使った開発には、タスク管理、スケジューリング、タスク間通信、同期、メモリ管理など、特有の概念や注意点があります。これらの概念を正しく理解し、デッドロックや優先度逆転といったマルチタスク固有の問題を回避するための知識を習得することが重要です。最初から完璧な設計を目指すのではなく、小さなタスクから始めて徐々に機能を拡張していくのが良いアプローチでしょう。また、開発ツール(STM32CubeIDE/CubeMX)やデバッガー、RTOSビューなどの機能を最大限に活用することで、開発プロセスを円滑に進めることができます。
FreeRTOSは組み込み分野で非常に広く使われており、豊富な資料やコミュニティのサポートも得られます。本記事が、STM32マイコンとFreeRTOSを使った組み込みシステム開発の第一歩を踏み出すための一助となれば幸いです。
さらなる学習リソースとして、以下のものを参考にすることをお勧めします。
- FreeRTOS公式サイト: APIリファレンス、チュートリアル、FAQなど、公式の最新情報が得られます。
- STM32Cube IDE/CubeMXのドキュメント: STマイクロエレクトロニクスが提供するマニュアルやアプリケーションノートは、具体的な実装方法やペリフェラルの使い方について非常に詳細な情報を提供しています。
- 書籍やオンラインコース: RTOS全般やFreeRTOSに特化した専門書、オンラインの学習プラットフォームには、体系的な知識や実践的な演習が豊富に用意されています。
- サンプルコード: STM32Cubeファームウェアパッケージや、FreeRTOS公式サイト、GitHubなどで公開されているサンプルコードは、実際の使い方を学ぶ上で非常に参考になります。
FreeRTOSを使ったリアルタイムシステム開発の世界へ、ぜひ挑戦してみてください。