STM32のFreeRTOSでタイマーの使い方を、サンプルコードと合わせてわかりやすく解説します。
作成するプログラム
まずはゴールを確認しておきましょう。今回はタイマーを使ってLEDを点滅するプログラムを作ります。1000ms間隔にタイムアウトするタイマーを用意し、タイムアウトの度にLEDの点灯・消灯を切り替えることで実現します。

前提条件
前提条件は目次の記事を参照してください。
プログラムの作成
タイマーの有効化
FreeRTOSではタイマーを使う場合、OSの設定で有効化しておく必要があります。
- Pinout & Configurationsタブ→Middleware→FREERTOS→Configurationタブ→Config Parameters
- USE_TIMERSをEnabledに変更してください

タイマーの作成
タイマーを作ってみましょう。
- Pinout & Configurationsタブ→Middleware→FREERTOS→Configurationタブ→Timers and Semaphores
- TimersのAddボタンをクリックして、タイマーの設定画面を開いてください
- 下記の図のように入力して、OKボタンを押してください

ここの設定項目が気になる方は下記の表をご覧になってください。気にせず、とにかく動かしたい方は読み飛ばしてくださいね。
| 項目 | 説明 |
| Timer Name | タイマーの名前。 |
| Callback | コールバック関数の名前。 |
| Type |
タイマーの種類。 osTimerOnceだと1回だけタイムアウトする。 osTimerPeriodicだと周期的にタイムアウトする。 |
| Code Generation Option |
コールバック関数の接頭語。 Defaultだと何も付けない。 As externalだとexternが付く。 As weakだと__weakが付く。 |
| Parameter | コールバック関数に渡すパラメータ。 |
| Allocation |
メモリ確保の方法。 Dymanicだと動的確保。 Staticだと静的確保。 |
| Control Block Name | コントロールブロックの名前。Staticの時のみ使用。 |
Parameterで『&ledState』を指定しました。これはタイマー側でLEDのON/OFF状態を管理できるようにするためです。ON/OFF状態を保持するledStateという変数を別途用意し、そのポインタを指定している形です。
設定が終わったらコードを自動生成してください。
LEDのON/OFF状態を保持する変数を作成
先ほどタイマの設定で指定したledStateを用意してあげましょう。0の時OFF、1の時ONということにします。
main.c:ledState
/* USER CODE BEGIN 0 */ uint32_t ledState = 0; /* USER CODE END 0 */
タイマーを実装
LEDを点滅するタイマーを実装しましょう。まずはコードをご覧になってください。
main.c:LEDTimerCallback
void LEDTimerCallback(void const * argument)
{
/* USER CODE BEGIN LEDTimerCallback */
uint16_t ledState = getLEDState(argument);
if (ledState == 0) {
HAL_GPIO_WritePin(LD2_GPIO_Port, LD2_Pin, GPIO_PIN_RESET);
} else {
HAL_GPIO_WritePin(LD2_GPIO_Port, LD2_Pin, GPIO_PIN_SET);
}
/* USER CODE END LEDTimerCallback */
}
ledStateの状態に応じてLEDの点灯・消灯を行うようにしているのがわかると思ます。
ledStateの取得はgetLEDState()関数から行うようにしました。これには理由があります。次にgetLEDState()関数の中身をご覧になってください。
main.c:getLEDState()
/* USER CODE BEGIN 4 */
static uint16_t getLEDState(void const *argument) {
uint16_t *ledState = pvTimerGetTimerID((TimerHandle_t)argument);
if (*ledState == 0) {
*ledState = 1;
} else {
*ledState = 0;
}
return *ledState;
}
コールバック関数の引数からledStateを取得するかと思いきや、pvTimerGetTimerID()からledStateを取得していますね。
実は、コールバック関数の引数には、本来Parameterで指定したものが入っているべきところ、STM32のFreeRTOSではタイマーのハンドラが入っています。
そのため、そのタイマーのハンドラを使ってpvTimerGetTimerID()からParameterを取得しているのです。
おそらくこれはSTM32のFreeRTOSのバグと思われます。そのため、将来修正される可能性があることを考慮して、ledStateの取得は関数にまとめておくことにしました。こうしておけば修正が入った時に、この関数を修正すればコールバック関数に影響はありません。
タイマーの開始
タイマーは作っただけでは動作しません。タイマーの開始処理を忘れずに入れておきましょう。
main.c:タイマーの開始処理
/* USER CODE BEGIN RTOS_TIMERS */ /* start timers, add new ones, ... */ osTimerStart(LEDTimerHandle, 1000); /* USER CODE END RTOS_TIMERS */
osTimerStart()でタイマーを開始できます。第1引数はタイマーのハンドラを指定します。第2引数でタイムアウトまでの時間をms単位で指定します。
プログラムの作成は以上です。実際に動かしてLEDが点滅することを確認してみてください。
まとめ
STM32のFreeRTOSのタイマーの使い方は以上です。注意すべき点がいくつかありますが、手順通り使えば問題なく動作しますので、記事をよく読み直して使ってみてください。
下記の手順は忘れやすいので気を付けてください。
- OSの設定でタイマーの有効化
- タイマーの開始
また、タイマーにパラメータを渡す時は、バグと思われる箇所があるので、解説した方法で取得するようにしてください。