STM32CubeIDEで簡単にFreeRTOSの初期化コードを生成できますが、その先のFreeRTOSの使い方に関して情報がまだ多くありません。本記事で基礎的な内容からサンプルコードまでわかりやすく解説していきます。
目次
STM32のFreeRTOSとは
STM32でリアルタイムOSを使いたい場合、STM32CubeIDEに入っているFreeRTOSが使用できます。STM32CubeIDEとFreeRTOSがそもそもわからない、という方は下記の記事・URLをご参照ください。
外部リンク:AmazonのFreeRTOS公式ガイド
このFreeRTOSですが、実は独特なところがあり使いこなすのが難しいと思います。理由は2つあって、順番に説明していきますね。
- APIがCMSIS RTOS
- 独自のCMSIS RTOS
APIがCMSIS RTOS
1つ目は、APIがFreeRTOSではなく、CMSIS RTOSベースであることです。
CMSIS RTOSとは、FreeRTOSなどのリアルタイムOSのラッパーAPIです。これを使うことで異なったOSでも共通のインタフェースで操作できるようになります。
CMSIS RTOSのバージョンはv1とv2の2つがありますが、ここではv1を前提に話を進めます。というのも、v2はコード自動生成の部分や、v2の実装自体にも問題があり、まだ安定していないためです。(2019年5月現在)
例えばタスクを生成する際のAPIは、FreeRTOSとCMSIS RTOSで次のように異なります。
FreeRTOS
xTaskCreate(...)
CMSIS RTOS
osThreadCreate(...)
FreeRTOSに使い慣れた人からすると、新しいAPIの使い方を覚える必要があるため、腰が重く感じることでしょう。しかし、APIドキュメントが公開されているので、この点はまだ大きな問題ではありません。
https://www.keil.com/pack/doc/CMSIS/RTOS/html/index.html
独自のCMSIS RTOS
2つ目が話をややこしくしている張本人です。先ほどAPIはCMSIS RTOSと説明したのですが、STM32の実装はCMSIS RTOSそのままではありません。そのため、APIドキュメントを参考にプログラムを作っても思った通りに動いてくれないのです。
例えば、Semaphoreを取得する時ですが、API上は次のように定義されています。
Returns
number of available tokens, or -1 in case of incorrect parameters.
引用:CMSIS RTOS v1 Semaphores osSemaphoreWait
Semaphoreを取得できた時は残っているリソースの数が返され、失敗した時は-1が返されるとあります。しかし、実装はどうなっているかというと、次のようになっています。
int32_t osSemaphoreWait (osSemaphoreId semaphore_id, uint32_t millisec) { ... if (semaphore_id == NULL) { return osErrorParameter; } ... else if (xSemaphoreTake(semaphore_id, ticks) != pdTRUE) { return osErrorOS; } return osOK; }
簡単に説明すると、Semaphoreを取得できた時はosOKを返し、取得できなかった時はosErrorParameterやosErrorOSを返す、という実装になっています。osOKは0、osErrorParameterは128、osErrorOSは255なので、APIドキュメントの仕様を期待してプログラムを作ると、うまく動作しません。
どうしてこのような実装になっているか確実なことは言えませんが、意図したものであるように感じています。一通りソースコードを見渡したところ、CMSIS RTOS v1をベースにしつつ、v2の内容と独自仕様を一部取り込んだ実装になっていました。v1では不十分な所やスマートでない所もあるので、あえてそうしているように思います。
そういう意味では準拠でないのは良い面もあります。CMSIS RTOS v1から追加されているAPIを下記に並べました。関数の名前からして使えそうなものがありますよね。
osThreadState osThreadGetState(osThreadId thread_id);
osStatus osThreadIsSuspended(osThreadId thread_id);
osStatus osThreadSuspend (osThreadId thread_id);
osStatus osThreadResume (osThreadId thread_id);
osStatus osThreadSuspendAll (void);
osStatus osThreadResumeAll (void);
osStatus osDelayUntil (uint32_t *PreviousWakeTime, uint32_t millisec);
osStatus osAbortDelay(osThreadId thread_id);
osStatus osThreadList (uint8_t *buffer);
osEvent osMessagePeek (osMessageQId queue_id, uint32_t millisec);
uint32_t osMessageWaiting(osMessageQId queue_id);
uint32_t osMessageAvailableSpace(osMessageQId queue_id);
osStatus osMessageDelete (osMessageQId queue_id);
osMutexId osRecursiveMutexCreate (const osMutexDef_t *mutex_def);
osStatus osRecursiveMutexRelease (osMutexId mutex_id);
osStatus osRecursiveMutexWait (osMutexId mutex_id, uint32_t millisec);
uint32_t osSemaphoreGetCount(osSemaphoreId semaphore_id);
STM32のFreeRTOSの使い方 – 基本編
ちょっと独特なSTM32のFreeRTOSですが、使い方さえ理解すれば、十分に使えるものです。ここからは具体的な使い方の説明に入っていきますね。
前提条件
これ以降は下記の前提条件で話を進めます。
使用するAPI:CMSIS RTOS v1(STM32仕様)
ボード:NUCLEO-F401RE
FreeRTOSの有効化
Device Configuration Tool(STM32CubeMX)を開いて、FreeRTOSを有効化しましょう。
- Pinout & Configurationタブ→Middleware→FREERTOS→Mode
- Interfaceの箇所で『CMSIS_V1』を選択
タイムベースの変更
FreeRTOSを使う場合、タイムベースにSysTick以外を指定することが強く推奨されています。余っているタイマーを指定してあげましょう。
- SYS→Timebase Sourceで任意の『TIMx』を選択してください。
FreeRTOSの機能の使い方
各機能の使い方は、個別に記事を作成していますので、そちらをご参照ください。
STM32のFreeRTOSのカウンティングセマフォの使い方
STM32のFreeRTOSの再帰的なミューテックスの使い方
STM32のFreeRTOSのシグナルの(タスク通知)使い方
ソースコードはGitHubで公開していますので、ご自由にお使いください。
ソースコード:https://github.com/yuksblog/stm32_freertos
STM32のFreeRTOSの使い方 – 応用編
基礎編だけでもある程度アプリケーションを作ることはできると思いますが、さらに使いこなしたいとお考えの方は、直接ソースコードを除いてみてください。下記の2つのファイルから辿れます。
Middlewares/Third_Party/FreeRTOS/Source/CMSIS_RTOS/cmsis_os.c
cmsis_os.hで機能リストを確認し、cmsis_os.cで実際の挙動を確認しましょう。特に実際の挙動は、CMSIS RTOSの仕様と異なる場合があるので、十分に注意してください。