UCT — μT-Kernel 3.0 Migration Guide
FreeRTOS からμT-Kernel 3.0 への移行を検討する開発者向け
機能比較と機能毎の移植要点
UCTがGitHubで公開しているInfineonのマイコン向けμT-Kernel 3.0を移植した時の経験をもとにしたチュートリアルです。
今回の事例では、InfineonのPSoC 6評価ボード(CY8CKIT-062S2-43012)にμT-Kernel 3.0を搭載し、InfineonがModusToolboxで提供しているWi-FiサンプルアプリをFreeRTOSからμT-Kernel 3.0に移植しました。サンプルアプリにはFreeRTOSが含まれていたため、FreeRTOS用に作られたコードをμT-Kernel 3.0版に書き換える作業が必要となりました。
本解説では、FreeRTOSベースで作られたモジュールをμT-Kernel 3.0上に移植する場合のノウハウとして、両RTOSの機能の差異やコンセプトの違いについて述べています。
今回の目的は「FreeRTOS(のAPI)をμT-Kernel 3.0(のAPI)に置き換える」ことなので、FreeRTOSにはあるがμT-Kernel 3.0にはない機能が問題となります。μT-Kernel 3.0にない機能は別の機能で置き換える必要があることを確認する目的でFreeRTOSの機能を元に両OSの機能を比較すると以下のようになります。
◯:対応あり △:一部対応または仕様のみ ×:対応なし(別途実装が必要)
| 機能 | FreeRTOS | μT-Kernel 3.0 | 備考 |
|---|---|---|---|
| Task Creation | ◯ | ◯ | タスク生成、削除 |
| Task Control | ◯ | ◯ | 実行待ち、強制待ち/再開など |
| Task Utilities | ◯ | △ | 基本機能は同じ。 |
| RTOS Kernel Control | ◯ | △ | |
| Direct To Task Notifications | ◯ | ◯ | tk_slp_tsk/tk_wup_tsk に類似の機能 |
| Queues | ◯ | × | メッセージバッファで類似の機能を実装可能 |
| Queue Sets | ◯ | × | Queue の拡張機能 |
| Stream Buffers | ◯ | × | |
| Message Buffers | ◯ | ◯ | FreeRTOSはStream Buffersベースなのでその制限を引き継ぐ |
| Semaphore / Mutexes | ◯ | △ | μT-Kernel 3.0に再帰ミューテックスはない。 |
| Software Timers | ◯ | ◯ | 周期ハンドラ、アラームハンドラ |
| Event Groups (or 'flags') | ◯ | ◯ | イベントフラグ |
| FreeRTOS-MPU Specific | ◯ | × | |
| Co-routines | ◯ | × | 非プリエンプティブ方式のマルチタスク |
上の比較表に含まれていないμT-Kernel 3.0の機能をベースにして表の続きを作ると以下になります。
| 機能 | FreeRTOS | μT-Kernel 3.0 | 備考 |
|---|---|---|---|
| タスク例外処理機能 | × | △ | μT-Kernel 3.0も仕様のみ。実装はされていない。 |
| メールボックス | × | ◯ | |
| ランデブ | × | 仕様範囲外 | μT-Kernel 3.0では仕様範囲外。実装には含まれる。 |
| 固定長メモリプール | × | ◯ | |
| 割込み管理機能 | × | ◯ | |
| システム状態管理機能 | × | ◯ | |
| サブシステム管理機能 | × | △ | μT-Kernel 3.0も仕様のみ。実装はされていない。 |
| デバッグサポート機能 | × | ◯ | (*注) |
ModusToolboxでは、アプリケーションやドライバ、ライブラリが必要としている関数がCOMPONENT_FREERTOS/ のディレクトリの中にまとめられています。それを参考にCOMPONENT_MTKERNEL3/ を作り、必要な機能(関数)をμT-Kernel 3.0で実装する必要があります。
実際に作成したファイルは以下のとおりです。
※各実装にはリビジョンが複数あったが、今回はWi-FiのTCP ClientサンプルでCOMPONENT_FREERTOS/ の並びにCOMPONENT_MTKERNEL3/ を配置した。
各ファイルの概要は以下のとおりである。
RTOSの機能を汎用化してModusToolboxのサンプルアプリケーションに提供するためのラッパーらしい。このファイルではμT-Kernel 3.0とFreeRTOSの以下の機能を利用しているので、これらの差異について説明する。
この他に、キューも用意されているが、μT-Kernel 3.0にはキューが含まれていないので、メッセージバッファで代用した。FreeRTOSとμT-Kernel 3.0で別の機能を用いて実装しているのではキューという機能の比較にならないのでこの機能については省略する。
lwIPに対して排他制御機構などを提供するためのファイルであり、以下の機能が利用されている。
cyabs_rtos_mtkernel3.c にない機能としてはメールボックスが含まれている。メールボックス機能の差異について比較したいところだが、FreeRTOSにはメールボックスがない(Queueで代用)。このため機能や個別のAPIの比較はできないのでこれも省略する。
clib用に提供するRTOS機能であり、ミューテックスが利用されている。ミューテックスの差異については既に説明しているので、ここでは省略する。
移植作業ではヘッダファイルの追加も必要であるが、本記事の主眼であるμT-Kernel 3.0とFreeRTOSの差異の説明とは直接関係しないので、これらの説明は省略する。
cyabs_rtos_mtkernel3.c には、RTOSの機能を汎用化してModusToolboxのサンプルアプリケーションに提供するためのラッパー関数が集められている。以下、各機能を実装するために利用されている各OSのAPIとその違いについて説明する。
| ラッパー関数 | FreeRTOSのAPI | μT-Kernel 3.0のAPI |
|---|---|---|
| cy_rtos_create_thread() | xTaskCreateStatic() | tk_cre_tsk(), tk_sta_tsk() |
タスクを生成し、実行を開始する。xTaskCreateStatic() を tk_cre_tsk() と tk_sta_tsk() に置き換えることで同じ機能を実現できる。
FreeRTOS では xTaskCreateStatic() で生成と実行が同時に行われる。"Create" とあるので、生成だけかと思ったがそうではなく、生成と同時に起動されている。確かにFreeRTOSのタスク状態遷移図を見ると、(μT-Kernel 3.0の)「休止状態(DORMANT)」がないので、生成すればREADY状態(またはRUNNING状態)になってしまう。このため、μT-Kernel 3.0では tk_cre_tsk() でタスクを生成した後で、tk_sta_tsk() によってタスクを開始することで同じ動作にしている。
なお、FreeRTOS では cy_rtos_create_thread() でタスク用のメモリを確保してスタックとしてAPIに渡していた。このメモリ領域を解放するタイミングを調整する必要があるのでやや面倒な処理が入っており、以下のAPIも合わせて利用されている。
μT-Kernel ではその必要はないので、単純に tk_cre_tsk() と tk_sta_tsk() を呼び出すだけで済んでいる。
| ラッパー関数 | FreeRTOSのAPI | μT-Kernel 3.0のAPI |
|---|---|---|
| cy_rtos_exit_thread() | vTaskDelete() | tk_exd_tsk() |
タスクの終了と削除はFreeRTOSであれば vTaskDelete()、μT-Kernel 3.0であれば tk_exd_tsk() である。ただし、FreeRTOSの方はタスクの生成と開始で説明したとおり、cy_rtos_create_thread() でメモリの確保も行っているのでこのメモリの解放が必要となる。しかも、解放するタイミングの調整が必要らしく、若干面倒な処理が追加されていた。
一方、μT-Kernel 3.0では tk_exd_tsk() のみでタスクの終了と削除が実行できる。また、μT-Kernel 3.0ではタスクを削除せずに残しておくこともできる(タスク状態遷移図を参照)。タスクを削除せずに休止状態にする場合は tk_ext_tsk() で終了する。この場合、tk_sta_tsk() でタスクを再度起動(タスクの最初から実行)することができる。
| ラッパー関数 | FreeRTOSのAPI | μT-Kernel 3.0のAPI |
|---|---|---|
| cy_rtos_terminate_thread() | vTaskDelete() | tk_ter_tsk(), tk_del_tsk() |
FreeRTOSにはタスクを強制終了(Terminate)する機能はないので、vTaskDelete()で削除している。また、先に述べたとおりタスクを終了する場合はライブラリ内で確保したメモリの解放処理が必要である。
μT-Kernel 3.0ではそのような処理は必要ないので、tk_ter_tsk() と tk_del_tsk() を連続して呼び出すだけで済む。当然、μT-Kernel 3.0であればタスクを強制終了するだけで削除しないでおけばタスクを再度起動させることも可能であるが、機能的にFreeRTOS版と同じ動作にする必要があるのでタスクの削除まで実行している。
| ラッパー関数 | FreeRTOSのAPI | μT-Kernel 3.0のAPI |
|---|---|---|
| cy_rtos_is_thread_running() | eTaskGetState() | tk_ref_tsk() |
指定されたタスクが実行中かどうかを判定している。FreeRTOS では eTaskGetState() で実装するが、μT-Kernel 3.0ではそのような単機能なAPIは用意していないので、タスクの各種情報を一括で取得できる tk_ref_tsk() を用いて実装した。
| ラッパー関数 | FreeRTOSのAPI | μT-Kernel 3.0のAPI |
|---|---|---|
| cy_rtos_get_thread_handle() | xTaskGetCurrentTaskHandle() | tk_get_tid() |
このAPIは両OSで対応しているAPIがあるのでそのまま置き換えた。
| ラッパー関数 | FreeRTOSのAPI | μT-Kernel 3.0のAPI |
|---|---|---|
| cy_rtos_wait_thread_notification() | ulTaskNotifyTake() | tk_slp_tsk(), tk_can_wup() |
| cy_rtos_set_thread_notification() | xTaskNotifyGive(), vTaskNotifyGiveFromISR(), portEND_SWITCHING_ISR() | tk_wup_tsk() |
タスクの待ちと待ち解除の機能である。FreeRTOSではこの機能のためにいろいろなAPI(やマクロ)が用意されているようだが、本機能はその中でも単純なAPIを利用して実装されていた。FreeRTOSとμT-Kernel 3.0のAPIの基本的な対応は以下になる。
| 機能 | FreeRTOS | μT-Kernel 3.0 |
|---|---|---|
| 待ち | ulTaskNotifyTake() | tk_slp_tsk() |
| 待ち解除 | xTaskNotifyGive() | tk_wup_tsk() |
本機能でのFreeRTOSとμT-Kernel 3.0の大きな違いは待ち解除要求をキューイングするかどうかだろう。μT-Kernel 3.0では、タスクが待っていない状態でtk_wup_tsk()を呼び出すと待ち解除要求の数が追加(インクリメント)されていく。tk_slp_tsk()では、待ち解除要求の数が1以上であれば待ちに入らない(デクリメントして処理を継続)ので、待ち解除の要求数が0になるまでは、tk_slp_tsk()を発行しても待ちにはならない。一方、FreeRTOSでは待ち解除の要求はキューイングされない(0から1にはなるが、2以上にはならない)。このため、単純に実装すると両者の挙動に違いがでるので、μT-Kernel 3.0では tk_slp_tsk() の待ちが解除された後で起床要求のキューイング数をクリアする処理を追加してある。
ところで、FreeRTOSではタスクから呼び出すAPIと割込みハンドラ(ISR)から呼び出すAPIが別になっている。ISRから呼び出すAPIは 〜FromISR() という名称になっており、こちらを呼び出さなければならない。このため、cy_rtos_wait_thread_notification() には呼び出したコンテキストがタスクかISRかを呼び出し元に指定させるための引数が用意されている。一方、μT-Kernel 3.0ではタスクでもISR(μT-Kernel 3.0では「タスク独立部」と呼ぶ)でも同じAPIを利用可能である。呼び出してよいかどうかはRTOS側で判定し、呼び出せない場合はエラーが返される。しかも、原則として待ちが発生しないAPIであれば「タスク独立部」から呼出可能であり、FreeRTOSのように待ち解除用のAPIで呼び出し元のコンテキストを意識する必要はない。
| ラッパー関数 | FreeRTOSのAPI | μT-Kernel 3.0のAPI |
|---|---|---|
| cy_rtos_delay_milliseconds() | vTaskDelay() | tk_dly_tsk() |
タスクを指定された時間だけ待ち状態に移行させる。機能としては同じである。
ただし、FreeRTOSのAPIでは待ち時間をティックに変換する必要がある。マクロは用意されているが、これはいささか面倒である。しかも、"Mastering the FreeRTOS Real Time Kernel" のp.62には以下のように書かれている。
ティックでタイムアウトを指定することのデメリットを理解しているからこのように書いているのであれば、「タイムアウトにはミリ秒を指定する」と仕様で決めてくれてもいいような気がする。この処理を仕様に持って行った(仕様として決めた)のがITRONであり、μT-Kernelであるとも言える。
| ラッパー関数 | FreeRTOSのAPI | μT-Kernel 3.0のAPI |
|---|---|---|
| cy_rtos_init_mutex2() | xSemaphoreCreateMutex(), xSemaphoreCreateRecursiveMutex() | tk_cre_mtx() |
本関数ではミューテックスを生成する。FreeRTOS では(通常の)ミューテックスと再帰ミューテックスが利用可能である。一方のμT-Kernel 3.0には(通常の)ミューテックスはあるが、再帰ミューテックスはない。(必要であれば通常のミューテックスとタスクIDを取得するtk_get_tid()、カウンタ変数などを組み合わせて実装することになる。)
この他に優先度逆転を防ぐための機構にも違いがある。FreeRTOSのミューテックスでは優先度継承プロトコルしか利用できないが、μT-Kernel 3.0は優先度継承プロトコルと優先度上限プロトコルに対応している。
| ミューテックス機能 | FreeRTOS | μT-Kernel 3.0 |
|---|---|---|
| (通常の)ミューテックス | ◯ | ◯ |
| 再帰ミューテックス | ◯ | × |
| 優先度継承プロトコル | ◯ | ◯ |
| 優先度上限プロトコル | × | ◯ |
| ラッパー関数 | FreeRTOSのAPI | μT-Kernel 3.0のAPI |
|---|---|---|
| cy_rtos_deinit_mutex() | vSemaphoreDelete() | tk_del_mtx() |
| cy_rtos_get_mutex() | xSemaphoreTake(), xSemaphoreTakeRecursive() | tk_loc_mtx() |
| cy_rtos_set_mutex() | xSemaphoreGive(), xSemaphoreGiveRecursive() | tk_unl_mtx() |
これらはそれぞれに対応したAPIが用意されているので、特に問題なく移植できる。ただし、FreeRTOSでは呼び出しコンテキストの区別が必要(FromISRのない関数を呼ぶか、FromISRのある関数を呼ぶか)であり、このようなラッパーを用意する場合には実装が面倒になっている。
セマフォについてはそれぞれに対応したAPIが用意されているので、特に問題なく移植できる。ただし、FreeRTOSでは「呼び出しコンテキストの区別が必要」な点、「待ちの時間指定をティックで指定する」といった点がやはり面倒くさい。(このあたりはμT-Kernelに慣れているせいもあるのだろう。)
| ラッパー関数 | FreeRTOSのAPI | μT-Kernel 3.0のAPI |
|---|---|---|
| cy_rtos_init_semaphore() | xSemaphoreCreateCounting() | tk_cre_sem() |
| cy_rtos_deinit_semaphore() | vSemaphoreDelete() | tk_del_sem() |
セマフォの生成、削除という意味では同じ機能である。両OS共にバイナリセマフォとカウンティングセマフォを生成可能である。ただし、μT-Kernel 3.0ではタスクの待ち解除方式としてFIFOだけでなくタスク優先度順も選択することができるので、セマフォの応用の幅が広がる。一方、FreeRTOSの待ち解除方式はFIFOのみである。
| ラッパー関数 | FreeRTOSのAPI | μT-Kernel 3.0のAPI |
|---|---|---|
| cy_rtos_get_semaphore() | xSemaphoreTake(), xSemaphoreTakeFromISR() | tk_wai_sem() |
| cy_rtos_set_semaphore() | xSemaphoreGive(), xSemaphoreGiveFromISR(), portYIELD_FROM_ISR() | tk_sig_sem() |
セマフォ資源の獲得/解放という機能としては同じである。ただし、FreeRTOS ではカウンティングセマフォであっても操作できる資源数が1に制限されている。μT-Kernel 3.0では任意の資源数を指定可能であり、一度に複数個の資源を獲得/解放することも可能である。
さて、実は上記の2点よりも、大きな違いがある。FreeRTOSでは割込みハンドラでセマフォを制御した結果としてタスクの切り替えが必要になった場合、開発者が明示的に portYIELD_FROM_ISR() を呼び出さなければならない。このAPIを呼び出さない場合、割込みハンドラからタスクコンテキストに戻ってもタスク切り替えが発生せず、次回明示的にタスクスイッチが呼ばれた時にタスクが切り替わることになる。これはFreeRTOSの仕様であり、他のCommunication Objectでも同様の処理が必要となる。μT-Kernel 3.0ではこのような処理は一切不要であり、割込みハンドラを終了する時点で自動的にディスパッチされる仕様になっている。これを「遅延ディスパッチ(delayed dispatching)の原則」という。
| ラッパー関数 | FreeRTOSのAPI | μT-Kernel 3.0のAPI |
|---|---|---|
| cy_rtos_init_event() | xEventGroupCreate() | tk_cre_flg() |
FreeRTOSの「イベントグループ」がμT-Kernel 3.0の「イベントフラグ」に相当する。ここでは「イベントフラグ」で説明する。
イベントフラグ自体は類似の機能ではあるが、μT-Kernel 3.0とFreeRTOSには以下の違いがある。
| イベントフラグの機能 | FreeRTOS | μT-Kernel 3.0 |
|---|---|---|
| フラグのビット数 | 8ビット or 24ビットを選択 | 処理系に依存(unsigned int) |
| 待ちタスクのキューイング | FIFOのみ | FIFO、または優先度順を選択可能 |
| 待ちタスク数 | 複数タスクの待ちが可能 | 1個、または複数個を選択可能 |
フラグのビット数はμT-Kernel 3.0ではunsigned int型になっているので、CPUのビット幅(処理系)によって自動的に決まる。これは効率的に処理できるようにするための配慮である。一方のFreeRTOSでは configUSE_16_BIT_TICKS の設定によって使用可能なビット数が8ビットか24ビットとなる。
configUSE_16_BIT_TICKS はTickType_t型を定義するためのマクロだが、なぜかこれに依存するらしい。コードでは以下のように定義されており:
TickType_t はconfigUSE_16_BIT_TICKSに従って16ビット or 32ビットと定義されている:
| ラッパー関数 | FreeRTOSのAPI | μT-Kernel 3.0のAPI |
|---|---|---|
| cy_rtos_deinit_event() | vEventGroupDelete() | tk_del_flg() |
両OSともにイベントフラグを削除する機能であり、特に違いはない。
| ラッパー関数 | FreeRTOSのAPI | μT-Kernel 3.0のAPI |
|---|---|---|
| cy_rtos_setbits_event() | xEventGroupSetBits(), xEventGroupSetBitsFromISR() | tk_set_flg() |
指定されたビットをセットする機能としては両OSとも同じである。ただし、FreeRTOSではフラグのセットについても呼び出すコンテキストによってAPIが異なるので、アプリケーション側でコンテキストを区別しておかなければならない。μT-Kernel 3.0ではコンテキストによってフラグをセットするAPIを区別する必要はない。
なお、セマフォでも説明したとおり、FreeRTOSでは割込みハンドラでセマフォを制御した結果としてタスクの切り替えが必要になった場合、開発者が明示的に portYIELD_FROM_ISR() を呼び出さなければならない。この点も注意が必要である。
| ラッパー関数 | FreeRTOSのAPI | μT-Kernel 3.0のAPI |
|---|---|---|
| cy_rtos_clearbits_event() | xEventGroupClearBits(), xEventGroupClearBitsFromISR() | tk_clr_flg() |
イベントのビットをクリアする機能としては両OSとも同じである。ただし、FreeRTOS では1のビットがクリアされ、μT-Kernel 3.0では0のビットがクリアされる。仕様としてどちらが良いとも悪いとも言えないが、移植だけでなく開発時にも注意が必要な点である。
| ラッパー関数 | FreeRTOSのAPI | μT-Kernel 3.0のAPI |
|---|---|---|
| cy_rtos_getbits_event() | xEventGroupGetBits() | tk_ref_flg() |
現在のイベントフラグのビットを取得する。機能としては同じである。
| ラッパー関数 | FreeRTOSのAPI | μT-Kernel 3.0のAPI |
|---|---|---|
| cy_rtos_waitbits_event() | xEventGroupWaitBits() | tk_wai_flg() |
指定されたビットが1になるのを待つ。複数の待ちビットが指定されていた場合、全てのビットがセットされるのを待つか、いずれか1つのビットがセットされるのを待つのかを指定できる点も同じである。条件成立時にビットをクリアするか否かを指定できる点も同じである。
ただし、FreeRTOS では、待ち条件として指定されたビットをクリアするか否かを指定できるだけであるが、μT-Kernel 3.0では全ビットをクリアすることも可能である。
| ビットクリア方法 | FreeRTOS | μT-Kernel 3.0 |
|---|---|---|
| ビットをクリアしない | ◯ | ◯ |
| 待ちビットをクリアする | ◯ | ◯ |
| 全ビットをクリアする | × | ◯ |
また、FreeRTOS では戻り値がビットパターンになっている点(以下)も注意が必要である。
このため、タイムアウトが発生したかどうかは戻り値をビットパターンと比較して検証しなければならない。これに対してμT-Kernel 3.0では戻り値でエラーか否かを判定できる仕様になっているので、APIからリターンした時の処理が簡単になる。ただし、エラー発生時はフラグのビットが読み出されない(不定値となる)ので、ビットパターンを利用したい場合は注意が必要である。
どちらもアラームハンドラと周期ハンドラの機能があるので、両OS方とも機能としては◯となる。ただし、アラームハンドラでは μT-Kernel 3.0 と FreeRTOS で時間のパラメータを指定するタイミングが異なる。コールバック関数(タイマハンドラ)を呼び出す時間を指定するタイミングは以下のとおりである。
| 機能 | FreeRTOS | μT-Kernel 3.0 |
|---|---|---|
| アラームハンドラ | 生成時 | 開始時 |
| 周期ハンドラ | 生成時 | 生成時 |
新規に開発するのであれば特に問題はないが、相互に移植するとなると結構面倒なことになるかもしれない。
また、タイマハンドラが実行されるコンテキストも異なる。μT-Kernel 3.0ではタスク独立部(割込みハンドラ)として実行される。このため、基本的にタスクよりも優先して実行されることが保証されている。一方、FreeRTOSではコールバック関数がタイマ専用のタスクから呼び出されてタスクコンテキストで実行される。タイマサービスタスクの優先度は configTIMER_TASK_PRIORITY で指定可能となっていて、多くのシステムは以下のように定義されている:
つまり、多くのシステムではタイマサービスタスクの優先度は最高に設定されている。ただし、同じ優先度のタスクを作ることも可能なので、必ずしも全てのタスクに優先されることにはならない。さらに、タイマの起動はこのタイマサービスタスクのキューに対してコマンドを送ることで実現しているため、キューがいっぱいになるとエラーが発生することもあるので注意が必要である。
| ラッパー関数 | FreeRTOSのAPI | μT-Kernel 3.0のAPI |
|---|---|---|
| cy_rtos_init_timer() | xTimerCreate() | tk_cre_alm(), tk_cre_cyc() |
タイマを生成する。μT-Kernel 3.0にはアラームハンドラと周期ハンドラの2つの機能がある。FreeRTOSではソフトウェアタイマとして1つの機能としてまとめられているが、オプションの指定でワンショットか繰り返して動作するかを指定できる。ワンショットのタイマがμT-Kernel 3.0のアラームハンドラ、繰り返しタイマが周期ハンドラに相当する。機能としてはほぼ同じだが、以下のような違いがあるので実装(移植)の際には注意が必要である。
| ラッパー関数 | FreeRTOSのAPI | μT-Kernel 3.0のAPI |
|---|---|---|
| cy_rtos_start_timer() | xTimerChangePeriod(), xTimerStart() | tk_sta_alm(), tk_sta_cyc() |
タイマを開始するという機能としては同じである。ただし、「タイマの生成」で説明したとおり、FreeRTOSではタイマの開始時に時間を設定する。μT-Kernel 3.0のアラームハンドラは起動時間をAPI指定することができるので機能としては同じになる。一方、μT-Kernel 3.0の周期ハンドラでは起動時に時間を変更することはできない(生成時に指定する)。
また、先に説明したとおりFreeRTOSのタイマ機能は、タイマサービスタスクによって提供されており、タイマを制御するAPIは、このタスクにコマンドキューを介して操作リクエストを送信している。このため、キューがいっぱいになるとエラーが発生することもあるので注意が必要である。
| ラッパー関数 | FreeRTOSのAPI | μT-Kernel 3.0のAPI |
|---|---|---|
| cy_rtos_stop_timer() | xTimerStop() | tk_stp_alm(), tk_stp_cyc() |
タイマを停止する。機能としては同じである。
| ラッパー関数 | FreeRTOSのAPI | μT-Kernel 3.0のAPI |
|---|---|---|
| cy_rtos_is_running_timer() | xTimerIsTimerActive() | tk_ref_alm(), tk_ref_cyc() |
タイマの起動状態を取得する。FreeRTOSにはこの機能専用のAPIがあるが、μT-Kernel 3.0では tk_ref_alm()、tk_ref_cyc() によって起動状態を含めたタイマの各種情報を一括して取得する。
| ラッパー関数 | FreeRTOSのAPI | μT-Kernel 3.0のAPI |
|---|---|---|
| cy_rtos_deinit_timer() | xTimerDelete() | tk_del_alm(), tk_del_cyc() |
タイマを削除する。機能としては同じである。
FreeRTOS には上記の他に、タイマのリセット、タイマ種類の変更などのいろいろなAPIが用意されている。並べてみるとこんな感じである(〜FromISR() は省略):
| FreeRTOSのタイマ関連のAPI |
|---|
| xTimerCreate() |
| xTimerCreateStatic() |
| xTimerIsTimerActive() |
| xTimerStart() |
| xTimerStop() |
| xTimerChangePeriod() |
| xTimerDelete() |
| xTimerReset() |
| pvTimerGetTimerID() |
| vTimerSetReloadMode() |
| vTimerSetTimerID() |
| xTimerGetTimerDaemonTaskHandle() |
| xTimerPendFunctionCall() |
| pcTimerGetName() |
| xTimerGetPeriod() |
| xTimerGetExpiryTime() |
| xTimerGetReloadMode() |
一方のμT-Kernel 3.0では以下のとおりであり、μT-Kernel 3.0のAPIがいかにシンプルかがよくわかる(〜_u() は省略):
| 周期ハンドラ | アラームハンドラ | 機能 |
|---|---|---|
| tk_cre_cyc | tk_cre_alm | 生成 |
| tk_del_cyc | tk_del_alm | 削除 |
| tk_sta_cyc | tk_sta_alm | 開始 |
| tk_stp_cyc | tk_stp_alm | 停止 |
| tk_ref_cyc | tk_ref_alm | 参照 |
| ラッパー関数 | FreeRTOSのAPI | μT-Kernel 3.0のAPI |
|---|---|---|
| cy_rtos_get_time() | xTaskGetTickCount() | tk_get_otm() |
FreeRTOSのAPIでは時間はティック数で処理されているので、取得される経過時間もティック数となる。このため、アプリケーションでは必要に応じてミリ秒に変換する必要がある。μT-Kernel 3.0ではAPIで取得する時間はミリ秒と規定されているのでこの処理は必要ない。
コンポーネントの差し替え作業時に確認したμT-Kernel 3.0とFreeRTOSの差異は上記で説明したとおりである。その他にもいろいろと違いがあったので、以下にその点についてまとめておく。
FreeRTOS では実行可能状態のタスクが複数あり、それらが同一優先度の場合はラウンドロビンで動作するらしい。これは、同時に実行する準備ができている場合に、等しい優先順位のタスク間で処理時間を共有することを意味する。
μT-Kernel でもラウンドロビン的な機能を実装することは可能である。周期ハンドラを使って tk_rot_rdq() を発行すれば同じような動作になる。もしくは、タスクから明示的に tk_rot_rdq() を発行するのでもよい。tk_rot_rdq() を利用する方式だとある程度処理が完了してからタスクをスイッチすることができるので、実行中のタスクへの影響は限定的にできる。
μT-Kernel 3.0は原本が日本語である。一方のFreeRTOSは原則英語。AWSが公開している日本語のページにも、先頭に以下のように書かれている。
以前の機械翻訳と比べれば格段に読みやすくなっている。だが、込み入った説明になってくると日本語の意味がわからないこともままある。こんな時はやはり直接英文を確認しなければならない。
FreeRTOSには固定長メモリプールがない。組み込み機器ではメモリプールは以下の理由で、固定長を使用すべきだろう。
RAMが乏しいシステムでは少しでもメモリ使用量を削りたいという気持ちはよくわかるが、管理領域を含めて考えると可変長メモリプールは結構無駄が多い。トレードオフになるとは思うが、最近のシステムはRAMも大きいので、バッファとして確保する場合は多少大きくてもいいのではないだろうか?
FreeRTOSは、main()関数からユーザーアプリケーションが始まっていて、その中でFreeRTOSのスケジューラをキックしてRTOSを起動している。一方、μT-Kernel 3.0ではアプリケーションは初期タスクから始まる。main()関数も含まれてはいるが、それはアプリ開発者には見せていない。
RTOS上のアプリケーションの開発者に対してあまり下回りを見せるのは感心しない気もするが、最初に「C言語のプログラムはmain()関数から始まる」と教わった人にはmain()関数から始まってくれるのはわかりやすいかもしれない。特に、Non-OSからOSを導入する段階では理解しやすいだろう。ただし、RTOSに慣れてくるとこの部分は冗長に感じられてくる。
既に説明したが、FreeRTOSではAPIがタスク用とISR(Interrupt Service Routine)用とに分けられている。ISRから呼び出すAPIは 〜FromISR() という名称になっており、こちらを呼び出さなければならない。これはすごく使いにくい。FreeRTOSはこういった細かい対応を開発者に任せている気がする。自由度が高いとも言えるが、注意事項が多く、その分開発者に負担になるのではないだろうか。
μT-Kernel 3.0ではセマフォの生成時に初期値を指定できる。一方の、FreeRTOSでは初期値は0に固定。生成後に資源を返却することで設定する。FreeRTOSでは細かいAPIが別々に用意されていて、μT-Kernel 3.0ではある程度機能がまとめて整理されている。APIのわかりやすさとしてはμT-Kernel 3.0の方が優れていると言えると思う。
μT-Kernel 3.0にはデータキューはない。μT-Kernel 3.0のメッセージバッファやメールボックスで代替可能だが、先入れで受信するという機能はない。ただし、メールボックスでは属性の指定でメッセージの並び順をメッセージの優先度順にすることが可能であり、全く同じではないにしても近い動作を実現することはできるだろう。
FreeRTOSのタスク優先度は0が最低で、(configMAX_PRIORITIES-1)が最高となっている。μT-Kernel 3.0は1が最高で、CNF_MAX_TSKPRI が最低である。これは慣れの問題だとも思うが、「最も高いのが1」と即値で決められている方が理解しやすいのではないだろうか?
FreeRTOSでは生成したオブジェクトの識別子(戻り値)が構造体だったり(Queue, Semaphore, Mutex, …)、void*(Message Buffer)だったり、整数値(Task)だったりして統一されていない。例えば、以下になっている:
FreeRTOSではAPIの先頭に戻り値の型を示す文字が追加されてはいる(下記)が、これはコードを読む側にとってはわかりやすいかもしれないが、実装する側にとっては使いにくい。
| 先頭の文字 | 型 |
|---|---|
| pc | char* |
| pv | void* |
| v | void |
| ux | UBaseType_t |
| x | BaseType_t, TaskHandle_t, … |
一方、μT-Kernel 3.0を含めてTRON系のOSでは以下が原則である。
教訓を重視して設計されていることもあり、このあたりはμT-Kernel 3.0の方が開発しやすい。
FreeRTOSには自動的にアイドルタスクが用意される。省電力機能はアイドルタスクに実装するらしいので、開発者にとってはとっつきやすいかもしれない。一方、μT-Kernel 3.0の省電力機能は low_pow() に実装することになっている。この関数は実行すべきタスクがなくなった場合にディスパッチャが直接呼び出しているので、省電力を実装する目的でアイドルタスクを生成する必要はない。
FreeRTOS にも μT-Kernel 3.0 にもメッセージバッファがある。ただし、FreeRTOS のメッセージバッファはストリームバッファを利用して実装されているため以下のような制限事項がある。
μT-Kernel 3.0のメッセージバッファにはそのような制限はなく、複数のタスクからアクセスできる。
FreeRTOSからμT-Kernel 3.0への移行・移植について、お気軽にご相談ください。