FreeRTOSアプリケーションのQueueデータ送受信

FreeRTOSアプリケーションテンプレートのQueue利用タスク間データ送受信を説明します。これは、テンプレートのベースとしたMCUXpresso SDK付属FreeRTOSサンプルプロジェクトfreertos_generic解説の後半に相当します。

FreeRTOSアプリケーションテンプレートのベースプロジェクト(まとめ図参照)は、本稿で全て説明済みとなります。下記1~3が、関連投稿です。1~3の順にお読み頂くと、内容が解り易いと思います

  1. FreeRTOSアプリケーションテンプレート構想
  2. FreeRTOS新規プロジェクト作成からアプリケーションテンプレートまで
  3. テンプレートベース:freertos_generic前半(ソフトウェアタイマ関連)

freertos_genericのQueueによるデータ送受信

freertos_genericのQによるデータ送受信
freertos_genericのQによるデータ送受信

FreeRTOSでは、送信タスクと受信タスクの2つに分離しデータ送受信処理を開発します。

送信タスクは、自分の都合の良いタイミングで、いつでもデータ送信を始めます。受信タスクは、いつ始まるか判らないデータ送信に対し、常時受信できるように待機中です。但し、受信タスクの受信開始タイミングは、その他のタスク優先度により変動します。

この受信開始変動があっても送信データを取りこぼし無く受信するために、送受タスク間で一時データを格納するFIFOバッファが必要で、これがQueue(以下Q)です。マルチタスクによる副作用と言えます。

もちろん、データ溢れが生じないようQには複数データを蓄える深さも必要です。受信タスクは、データのQ完了イベントで、FreeRTOSが受信開始を起動します。また、送信タスクよりも、受信タスクの方が優先度が高いのは、Qデータ溢れ防止策です。

1データ長、データ送信タイミング、その他のタスク数やその優先度にも影響されるQの深さは、十分な検討が必要です。

freertos_genericでは、Qの深さを1データ、送信タスク優先度を1、受信タスク優先度を2とし、データを100固定値にして1バイトデータ長の送受信を行い、送信開始タイミングは、vTaskDelayUntil(後述)で作成しています。その他のタスクが、freertos_generic前半で説明したソフトウェアタイマタスク、この優先度が4です。

同一優先度のタスクが無く、他のタスク数もタイマタスク1個で高優先、1バイトデータ長のため、深さ1のQでも送受信データは溢れません。

少ないタスク数と同一優先度無しのシンプルなサンプルプロジェクトですが、簡単に説明しても上記のように長くなります。

ベアメタル開発経験者であれば、RTOS個々の文章説明(=Serial or Sequential動作)は、理解できます。しかし、RTOSによりシングルコアMCUであっても各タスクが時分割Parallel動作しますので、途端に解り難くなります。

BareMetal/SerialからRTOS/Parallelへ、従来の設計手法をステップアップする必要がありそうです。

vTaskDelayUntil対vTaskDelay

vTaskDelayUntilとvTaskDelay比較
vTaskDelayUntilとvTaskDelay比較

freertos_genericは、データ送信タイミングの作成に、他のサンプルでよく見かける待ち処理:vTaskDelayではなく、vTaskDelayUntilを使っている点に注意が必要です。

vTaskDelayは、指定待ち「時間」後、RTOSにより優先度に応じて次処理が実行されます。ゆえに、高優先の他タスクやその状況により、次処理実行までの待ち時間が変動します。

vTaskDelayUntilは、指定待ち「周期」後、次処理が実行されます。仮に2周期よりも長い待ちが発生した時は、次処理はスキップされます。つまり、vTaskDelayよりもデータ送信の(スキップも含めると)定間隔、周期性に優れています。

サンプルプロジェクトの場合、Qの深さが最小の1のため、vTaskDelayUntilの方が、Q溢れ対策として効果があります。vTaskDelayだと、変動を考慮し、より深いQが必要になりそうです。但し、vTaskDelayUntilのRAM使用量は、vTaskDelayよりも増えます。

つまり、vTaskDelayUntil 利用か、それとも、vTaskDelayとQの深さ利用か、どちらにRAM資源を割当てる方が、よりQ溢れ対策として効果的か、これが検討ポイントの1つです。freertos_genericでは、前者を選択した、と筆者は解釈しました。

参考資料:Kernel > API Reference > Task ControlのvTaskDelayUntilとvTaskDelay

本稿まとめとFreeRTOSアプリケーションテンプレート目的

FreeRTOSアプリケーションテンプレートのQ利用タスク間データ送受信を説明しました。Qにより、送信タスクと受信タスクを分離して開発できますが、Qの深さは、送受タスク以外の他タスク優先度など多くの要因にも関係するため、設計に注意が必要です。

FreeRTOS利用メリットは、ベアメタル開発に比べ、独立性や流用性が高いタスク開発ができることです。反面、優先度に応じたマルチタスク環境では、ベアメタル開発には無かったスキルも必要になります。

本稿説明のSDK付属FreeRTOSサンプルプロジェクトfreertos_genericをベースにした、Cortex-M4コア最高速150MHz動作LPCXpresso54114対応のFreeRTOSアプリケーションテンプレートは、6E目標に開発中です。

FreeRTOS Application Template (NXP Version)
FreeRTOS Application Template (NXP Version)

同じ動作のアプリケーションを、FreeRTOSとベアメタル、それぞれで開発した2つのプロジェクトを添付します。

FreeRTOSアプリケーションテンプレートの目的は、FreeRTOS特有スキルを、ベアメタルと比較し、具体的に理解・習得すること、基本的なFreeRTOSアプリケーション開発テンプレート(=スタートプロジェクト)としても使えること、の2点です。

なお現行のLPCXpresso54114開発SDKツールデフォルトコア速度100MHzを150MHz動作へ変える方法は、前稿を参照してください。

FreeRTOSサンプルプロジェクトfreertos_generic詳細

前稿の弊社FreeRTOSアプリケーションテンプレートのベースとなったMCUXpresso SDK付属FreeRTOSサンプルプロジェクトfreertos_genericの詳細、主にソフトウェアタイマ関連とその用途を説明します。

freertos_genericのHook関数とFreeRTOSConfig.h

Step5:FreeRTOS低電力動作
Step5:FreeRTOS低電力動作

前稿Step5でOSアイドルタスクの「Hook関数」にWFI()を追記し、FreeRTOSに低電力動作を追加しました。ベアメタル開発者には馴染みの少ないHook関数とは、MCU開発者がOSに独自処理を追加する仕組み(Wikipedia)です。ハッカーに悪用される危険性もありますが、元のOSソースはそのままで動作を変更できる便利な機能です。

freertos_genericは、このアイドルタスクの他に、スタックオーバーフロー、マロックエラーと、ソフトウェアタイマの周期割込みに後述のHook関数を用いています。スタックオーバーフローやマロックエラー発生時は、Hook関数内でMCUが動作停止しますので、対策検討の手始めになります。

これらソースコード内に記述したHook関数を有効にするには、FreeRTOSConfig.h内のマクロ設定が必須です。試しに、Step5で追記したWFIへブレークポイントを設定し、FreeRTOSConfig.hのconfigUSE_IDLE_HOOKマクロを1以外に設定するとWFIでブレークしません。configUSE_IDLE_HOOKを1に戻すと、当然ですがブレークします。

このように、ソースファイル記述よりも「FreeRTOSConfig.hのマクロが優先」されます。これが、前稿でFreeRTOSConfig.hを最重要ファイルとした理由です。

万一FreeRTOSConfig.hに記述ミスがあると、開発者が所望処理をソースへ加えても、何も無かったかのようにFreeRTOSは処理します。

そこで前稿新規プロジェクトのStep4で示したfreertos_genericのFreeRTOSConfig.hを上書きコピー後、オリジナルと内容一致を確認するのが良いと思います。但し、MCUXpresso IDEのColors&Fontを、例えばメイリオ11Pへ変えると、インデントやタブ表示なども変わるため見易く修正を加えた場合、Consolas利用のオリジナルと完全一致では無くなります。

Notepad++のCompareプラグイン実行結果。スペースが異なっても内容同じなら黄色、異なれば赤表示。
Notepad++のCompareプラグイン実行結果。スペースが異なっても内容同じなら黄色、異なれば赤表示。

そんな時に役立つツールが、Notepad++のCompareプラグイン機能です。コード内容が一致していれば挿入スペース数などが異なっても黄色、内容自体が異なれば赤で表示されるので、一目でソースコード内容差が判ります。筆者は、この機能を使ってFreeRTOSConfig.h記述ミスを回避しています。

freertos_genericソフトウェアタイマISRのHook関数

ソフトウェアタイマは、tick間隔2msの周期割込みを発生し、このISRがvExampleTimerCallback関数、このHook関数がvApplicationTickHookです。

vApplicationTickHookで2ms割込み発生を500回カウント後、イベントセマフォをprvEventSemaphoreTaskへ送出、イベントセマフォを取得したprvEventSemaphoreTaskが1秒毎に”Event task is running”をコンソールへ出力します。

freertos_genericソフトウェアタイマ処理の流れ
freertos_genericソフトウェアタイマ処理の流れ

vExampleTimerCallbackへのコード記述を少なくするのは常套手段で、ベアメタル開発でもFreeRTOSでも同じです。通常は割込みフラグクリアなどを行いますが、リロードタイマですのでvExampleTimerCallbackはカウンタインクリメントのみを行っています。

Hook関数vApplicationTickHookの500回カウント判断を変更すれば、2ms分解能で任意間隔のイベントセマフォ送出が可能です。これは、SWチャタリングやADCノイズ対策などの周期処理同期に最適です。

イベントドリブンが基本のFreeRTOSですが、割込み処理によりSWチャタリング対策を行うよりも、ソフトウェアタイマとそのHook関数を利用した周期ポーリング処理で行う方が簡単なのが判ります。

freertos_genericの原本

freertos_genericは、FreeRTOS DemoのHardware Independent FreeRTOS exampleがその原本です。原本には、より詳しい英文解説が付いています。また、前章のイベントセマフォによるタスク同期に比べ、45%高速でRAM使用量も少ないタスク通知方法など、FreeRTOS機能強化やTipsなど開発者向け情報も満載です。

6E販売開始予定の弊社FreeRTOSアプリケーションテンプレートを入手後、この開発者向け情報を活用し、FreeRTOSの更なる基礎固めと習得をお勧めします。

なお原本とfreertos_genericには、異なる動作箇所もあります。例えば、FRO:Free Run Oscillator=12MHz、System Clock=96MHzなどです。これはHardware Independentな原本を、LPCXpresso54114動作用にポーティングした結果、生じた箇所です。この部分が、freertos_genericに明記されていない場合もありますので、参照時には注意してください。

弊社は、NXP以外のMCUベンダCortex-M4クラス向けFreeRTOSアプリケーションテンプレートにも、このベンダ非依存のHardware Independent FreeRTOS exampleをベースとして使う予定です。

同一アプリケーションによるベアメタルとFreeRTOS比較が出来る特徴に加え、同一ベース利用によりベンダ間(例えば、STマイクロエレクトロニクスやCypressとNXP)のFreeRTOSアプリケーション開発比較も可能となると考えています。

freertos_genericソフトウェアタイマまとめ

開発中のNXP)LPCXpresso54114向けFreeRTOSアプリケーションテンプレートのベース、MCUXpresso SDK付属FreeRTOSサンプルプロジェクトfreertos_genericのソフトウェアタイマを説明しました。

ソフトウェアタイマのISRフック関数により任意周期イベントセマフォ送出が可能で、イベントドリブンが基本のFreeRTOSにおいて、SWチャタリングやADCノイズ対策などの周期ポーリング処理を行うのに適しています。

ソースコードに記述したフック関数は、FreeRTOSConfig.hのマクロ設定で有効になります。設定ミスを避けるため、オリジナルとの設定内容比較にNotepad++のCompareプラグイン機能を使いました。

なお、freertos_genericのQによるデータ送受信機能は、後半部分として別稿で説明する予定です。

あとがき

MCU開発初心者~中級の方が対象の販売中Cortex-M0/M0+/M3コアMCUテンプレートとは異なり、本稿Cortex-M4コア利用FreeRTOSアプリケーションテンプレートは、ベアメタルMCU開発経験を既にお持ちの方を対象としています。
※投稿内容と対象MCUは、コチラの一覧表を参照してください。

そこで本稿は、ベアメタルとFreeRTOS開発の差分などに関する説明は加えますが、ベアメタルは既知の内容として説明を省略しています(筆不精で、すいません😌)。

投稿内容が判りにくい場合やご質問などは、customerservice@happytech.jpへお寄せください。

ARM Cortex-M4とM0+アプリケーションコード互換

NXP MCUXpresso SDK利用を利用すると、LPC845 Breakout board用に開発した1秒赤LED点滅アプリケーションコードが、そのままFRDM-KL25Zへ流用できることを前回投稿で示しました。ただ、どちらも同じARM Cortex-M0+コアのMCU評価ボードなので、読者インパクトは少なかったかもしれません。

そこで、LPC845 Breakout board(Cortex-M0+/30MHzコア)のLED点滅アプリケーションコードが、そのままCortex-M4/100MHzコアのLPCXpreosso54114へ流用できることを示します。異なるARMコア間でのアプリケーションコード互換の話です。

ARMコアバイナリ互換

Cortex-Mxのバイナリ互換性(出典:STM32L0(Cortex-M0+)トレーニング資料)
Cortex-Mxのバイナリ互換性(出典:STM32L0(Cortex-M0+)トレーニング資料)

ARMコアのバイナリ上位互換を示す図です(関連投稿:Cortex-M0/M0+/M3比較とコア選択の2章)。このバイナリコード包含関係は、Cortex-M0バイナリコードならCortex-M4でも動作することを示しています。

但し、この包含関係を理解していても、Cortex-M0バイナリコードをそのままCortex-M4へ流用する開発者はいないと思います。

ARMコアアプリケーションコード互換

Cortex-Mxのアプリケーションコード互換性
Cortex-Mxのアプリケーションコード互換性

MCUXpresso IDEのARMコアアプリケーションコード互換を示す図です。左がLPC845 Breakout board(Cortex-M0+/30MHzコア)の1秒赤LED点滅アプリケーションコード、右がCortex-M4/100MHzコアのLPCXpreosso54114の1秒赤LED点滅アプリケーションコードです。

コード差は、L59:LPCXpreosso54114評価ボード動作クロック設定:48MHz動作のみです。例えば、96MHzなどの他の動作周波数へ設定することも可能です。コード上で動作周波数を明示的に表示するために異なりましたが、機能的には両者同じコードと言えます(L59をマクロで書き換えれば、同一コード記述もできます)。

図下のInstalled SDK Versionが、どちらも2019-06-14で一致していることも重要です。Versionが異なると、例えばGPIOのAPIが異なることがあるからです。各SDKリリースノートでAPI差の有無確認ができます。※LPCXpreosso54114 SDKのMCUXpresso IDE設定方法は、コチラの投稿の5/6章を参考にしてください。

1秒赤LED点滅という簡単なアプリケーションですが、Cortex-M0+とCortex-M4の異なるARMコア間でコード互換性があることが解ります。

動作周波数の隠蔽とIO割付

評価ボード動作周波数が異なれば、無限ループ回転速度も異なります。従って、互換性を持たせるコード内に、無限ループ内の回転数でLED点滅させるような処理記述はできません。コードに時間要素は組込めないのです。

1秒点滅の場合は、L77:SysTick_DelayTicks()でループ回転数をカウントし、1秒遅延を処理しています。これにより、GPIO_PortToggle()が時間要素なしとなり、異なる動作周波数のARMコアでもアプリケーションコード移植性を実現しています。

SysTick_DelayTick()と1ms割込みによりカウントダウンする処理コードが下記です。ここも、割込みを利用することでコード移植性を実現しています。

動作周波数隠蔽によるARMコアアプリケーションコード移植性の実現
動作周波数隠蔽によるARMコアアプリケーションコード移植性の実現

左のLPC845 Breakout boardと、右のLPCXpreosso54114のコード差は、L16:赤LEDのIO割付のみです。評価ボード毎に異なるIO割付となるのは、やむを得ないでしょう。L12からL16のDefinition箇所を、別ファイル(例えばIODefine.hやUserDefine.h)として抽出すれば、同一コード記述も可能です。

ARMコアアプリケーションコード互換メリット

以上のように、ARMコアアプリケーションコード互換を目的にした記述や工夫も必要です。しかし、一旦互換コードを開発しておけば、開発資産として他のARMコア利用時にも使えます。その結果、開発速度/効率が高まります。

IoT MCUは、センサ入力やLED出力などのメイン処理以外にも、日々変化するセキュリティ処理への対応は、必須です。メイン処理が出来上がった後での、セキュリティ処理追加という手順です。

セキュリティ対策は、セキュリティライブラリ等の使用だけでなく、いつどのようにライブラリを活用するか、その時のMCU負荷がメイン処理へ及ぼす影響等、検討が必要な事柄が多くあります。

少しでも早くメイン処理を仕上げ、これら検討項目へ時間配分することがIoT MCU開発者には要求されます。この検討時間稼ぎのためにも、ARMコアアプリケーションコードの開発資産化は必須でしょう。

※プロトタイプ開発は、初めから厳しい条件で開発するよりも、最速のCortex-M4で行い、全体完成後Cortex-M0+/M3などへアプリケーションコードを移植するコストダウンアプローチも名案だと思います。

P.S.:2019年9月4日、MCUXpresso IDEがv11.0.1へ更新されました。旧MCUXpresso IDE v11.0.0 [2516]利用中の方は、Help>Check for Updatesではv11.0.1へ更新されません。新にMCUXpresso IDE v11.0.1 [2563]のインストールが必要です。新MCUXpresso IDEインストール方法は、コチラの4章を参照ください。