RTOS(リアルタイムOS)

Amazon FeeRTOS

FReeRTOSConfig.hでFreeRTOSの設定を編集可能.

configUSE_MUTEXES  # ミューテックス使うよ
configUSE_RECURSIVE_MUTEXES # 再帰的ミューテックス使うよ
configUSE_RATE_HZ # RTカーネルのタスクスケジューラが走る頻度,

configUSE_RATE_HZを大きくすればより早い応答ができると期待するがその分コンテキストスイッチが増えてオーバヘッドが大きくなりCPUの使用率が落ちてしまうので,ただ上げればいいというものではない.

FPUを使用する場合,コンテキストスイッチの際に保存するレジスタが17ワードも追加で必要になる.
調べてると17だったり18だったりするがこれはレジスタへのアラインが4バイトか8バイトかの違いによる模様.
FPUを使う場合,FreeRTOSでではconfigUSE_TASK_FPU_SUPPORT設定をしておく必要がありそう.stm32CubeMXにはこの設定があったはず.
Cortex-M FPUも参照
Using the floating point unit (FPU) in interrupts

configTOTAL_HEAP_SIZEでRTOSの管理するRAMの最大領域を設定する。スタック領域の総量がこのサイズを超えるとタスクの生成に失敗、スタックオーバーフローになる。 このためこの値は大きめに設定したい気持ちはあるけど、ここを大きくしすぎると今後はグローバル変数やOS管理外のローカル変数の領域が足りなくなってそれはそれでリンクエラーになるので注意。
動的メモリ確保をするとき、このHeap領域からメモリを使っていく。言い方を変えると静的メモリ確保しか使わないならここの領域は増やさなくてよい。

printfなどの標準Cライブラリを使うのであれば注意が必要, mallocを使ったメモリ周りでひと悶着あり.
Using FreeRTOS with newlib and newlib-nano
Implementing Malloc With FreeRTOS
FreeRTOSでmalloc(Newlib)を使おうとすると生じる問題にについては下記にまとめた.
CubeMXでFreeRTOSプロジェクト作った際のNewlibまわりに生じる問題

stm32は優先度を主優先度と副優先度と分けることができるが、FreeRTOSの管理ではめんどくさいので全部主優先度にまとめておいたほうがよい。

※これはBASEPRIレジスタを含むコア(Cortex-M3, M4, M4F, M7)にのみ適用される。Cortex-M0, M0+には適用されない。 

ARM Cortex-Mアーキテクチャは最大で8ビットの優先度ビットがある(2^8=256の優先度設定が可能。)。しかしメーカ、デバイスによって実装されてる割込み優先度はもっと少ない。
最大8ビットのうちいくつかの優先ビットが実装されている。 RTOSカーネルが起動する前にconfigMAX_SYSCALL_INTERRUPT_PRIORITYを使ってFreeRTOSのクリティカルセクションによりマスクされる優先度とそうでない優先度の境界を設定する。 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);が呼び出されていることを確認しておく。

Running the RTOS on a ARM Cortex-M Core

taskENTER_CRITICAL()

FreeRTOSで管理してる割込みをすべて無効化し、クリティカルセクションに入る。configMAX_SYSCALL_INTERRUPT_PRIORITYで設定されたFreeRTOSよりも優先度の高い割り込みははこれに関係なく実行される.

taskEXIT_CRITICAL()

FreeRTOSの管理下にある割込みを有効化し、クリティカルセクションを抜ける。 クリティカルセクションのマクロでは、Cortex-MのBASEPRIレジスタを書き換えることで割込みを制御する。 BASEPRIに書き込まれた優先度以下(大きい数字)の割込みをマスク(割込み禁止)する。 0が書き込まれた場合、マスクは実行されない。taskEXIT_CRITICAL()はBASEPRIに0を書き込むマクロ

6. クリティカルセクション

タスクはセマフォ取得まち、割込みハンドラでセマフォをリリースすると、タスクはこのセマフォを取得して処理を行う。 終わったらタスクは再びセマフォの取得待ちになる。

TCB:タスクコントロールブロック、それぞれのタスクに存在する。タスクの各情報を管理するためのやつ。 osSemaphoreCreate()ではセマフォはGiveされた状態になっている。これはvSemaphoreCreateBinaryというFreeRTOSの古いAPIを使っているから。新しいAPIであるxSemaphoreCreateBinaryではそのようなことはない。
ODriveはセマフォを作った直後にosSemaphoreWaitをしてセマフォをTakeしてキューを空にしておく、これでブロッキング状態になる。つまりセマフォが開放されるまでその動作は実行されない.

CUbeMXやCudeIDEを使うと,FreeRTOSを初期化してコードを実装できる.実装はFreeRTOSだけど,CMSIS-RTOSでラッピングしてある. stm32でFreeRTOSを使う場合にはCMSIS-RTOSも参照
systickはRTOS用のタイムベースカウンタとして使用されるので,stm32のHALやLLのためのTimebase sourceには別途タイマを設定することが強く推奨されるので,適宜設定する.stm32はタイマの種類が多いので,機能が少ないものを設定しておけばいいでしょう.
MPU/FPUの設定を切り替えることができる部分があるが、これはARMv8-Mでしか現状使用されない?(CubeMX5.6.0時点) でもF4だと一応設定はできるようになっている。

vTaskList

void vTaskList( signed char *pcWriteBuffer );

を使うことでタスクの状態を知ることが可能.
引数として渡すバッファ(pcWriteBuffer)の中にASCIIでタスクの状態を保存してくれる. 得られる情報は以下

  • タスク名
  • タスクの状態
  • タスクの優先度
  • 呼び出し時のタスクのスタック量
  • タスク番号

引数として渡すバッファにはこのタスク情報全体の情報を保持できる大きさが必要.バッファの境界チェックは行われない.
タスク一つあたりに40バイトあればいいと書いてあるが,これはタスク名長がいくつの場合の数字なのかがちょっと不明. 
標準だとタスク名長は16になっているはず,これ以上長いタスク名をつけるとこの関数のではタスク名長までしか表示されない. これを解決するには,タスク名長を短くするか,タスク名長の最大値を長くする必要がある.タスク名長を長くするにはMAX_TASK_NAME_LENの数字を大きくする. しかしその場合は1タスク当たりに必要と思われるバッファの容量を多く見積もる必要がある. タスクの状態はアルファベットで示される.それぞれの状態は以下

  • X:Run(実行中) vTaskListを呼び出したタスク
  • R:Ready(準備完了)
  • B:Blocked(ブロック)
  • S:Suspended(停止)
  • D:Deleted(削除済み)

この関数は実行中に割り込みを禁止するので頻繁に呼び出すはやめおこう.
スタック量は使用するマイコンによって異なるので改めて注意すること,32ビットマイコンなら1スタックはWORDなので4バイト.
FreeRTOSのタスクの動作状況を調べる

uxTaskGetStackHighWaterMark

UBaseType_t uxTaskGetStackHighWaterMark( TaskHandle_t xTask );

渡したタスクの残りのスタック量を返す.タスクが実行されてから最も低いスタック量を返す.
この数字が少ないほどスタックオーバーフローに近い.
これもスタック値に注意する.32ビットマイコンなら戻り値(スタック)が1なら空き領域は4バイトである.
CubeMXで初期設定をするときはInclude paramtetersから有効にすることを忘れず.

タスクの切り替わりタイミングの測定など。configUSE_APPLICATION_TASK_TAGを定義しておく。 コレを定義することでタスクコントロールブロック内部にタグという属性を持たせることができる。これはタスクにおける特定の値を割り当てることが可能となる。このタグと言う値をGPIOやDACの出力に使うことでコンテキストスイッチングによるタスクの切り替わりをRTOSのTickよりも細かい単位で計測が可能となる。 traceTASK_SWITCHED_IN()がタスクの切り替わり時に呼ばれる関数のdefineとなる。このdefineをタスクの値を使用してDACやGPIOの値を変化させてオシロスコープで引っ掛けることが可能となる。

 /* First task sets its tag value to 1. */
void vTask1( void *pvParameters )
{
 /* This task is going to be represented by a voltage scale of 1. */
 vTaskSetApplicationTaskTag( NULL, ( void * ) 1 );
 
 for( ;; )
 {
 /* Task code goes here. */
 }
}
/*************************************************/
 
/* Second task sets its tag value to 2. */
void vTask2( void *pvParameters )
{
 /* This task is going to be represented by a voltage scale of 2. */
 vTaskSetApplicationTaskTag( NULL, ( void * ) 2 );
 
 for( ;; )
 {
 /* Task code goes here. */
 }
}
/*************************************************/
 
/* Define the traceTASK_SWITCHED_IN() macro to output the voltage associated
   with the task being selected to run on port 0. */
#define traceTASK_SWITCHED_IN() vSetAnalogueOutput( 0, (int)pxCurrentTCB->pxTaskTag )

Trace Hook Macros

FreeRTOS+CLI An Extensible Command Line Interface Framework
FreeRTOSにCLI環境を追加する.
CLI用のタスクが追加され,このタスクが文字列処理を行いCLI環境を実現.
必要な項目は以下の通り

  • FreeRTOS+CLIに必要なFreeRTOSでの設定を追加
  • API関数を用いてコマンドを登録
  • 入出力の関数を統合

という流れ.

コマンドの実行により生成された出力を保持するために用いるため,configCOMMAND_INT_MAX_OUTPUT_SIZEの定義を追加しておく.
FreeRTOSConfig.hにでも追加しておけばいいだろう.
関数FreeRTOS_CLIGetOutputBuffer()を使う場合に,上記のていぎが必要となる.
この関数を使わないのであれば,RAMの容量を節約するために1にしておく.

動作しているタスクの一覧を表示するコマンドにて,関数vTaskList()が実装されている.
この関数を呼び出すにはFreeRTOSにて以下の設定をゆうこうにする必要がある.
configUSE_TRACE_FACILITY
configUSE_STATS_FORMATTING_FUNCTIONS
FreeRTOSConfig.hに追記しておく.
CubeMX上であれば,「Config Parameters」より設定可能.

参考文献

  • rtos/freertos.txt
  • 最終更新: 2024/11/26
  • by yuqlid