STM32 タイマー設定完全ガイド:目的別設定例とトラブルシューティング
STM32 マイクロコントローラは、汎用タイマーから高度な制御タイマーまで、多様なタイマー機能を備えています。これらのタイマーは、時間計測、PWM制御、入力キャプチャ、出力比較など、様々なアプリケーションに利用できます。 本記事では、STM32 タイマーの基本的な概念から、具体的な設定例、そしてトラブルシューティングまで、詳細に解説します。
1. STM32 タイマーの基礎
STM32 のタイマーは、時間経過をカウントする基本的な機能に加え、様々なペリフェラル機能と連携することで、複雑な制御を実現します。 タイマーの種類によって、機能や設定項目が異なりますが、基本的な構造は共通しています。
1.1 タイマーの種類
STM32 マイクロコントローラには、通常、以下の種類のタイマーが搭載されています。
-
基本タイマー (TIM6/TIM7): 最もシンプルなタイマーで、タイマー割り込みを生成することに特化しています。 PWM 生成や入力キャプチャなどの機能は備えていません。
-
汎用タイマー (TIM2/TIM3/TIM4/TIM5): 時間計測、PWM 生成、入力キャプチャ、出力比較など、様々な機能を提供します。 ほとんどのアプリケーションで利用可能な汎用性の高いタイマーです。
-
アドバンストタイマー (TIM1/TIM8): モーター制御など、より高度なアプリケーション向けに設計されています。 デッドタイム挿入機能、相補 PWM 出力、緊急停止入力など、高度な機能が搭載されています。
-
ローパワータイマー (LPTIM1/LPTIM2): 低消費電力動作に最適化されたタイマーです。 動作モードやクロックソースの選択肢が豊富で、バッテリー駆動のアプリケーションに適しています。
-
システムタイマー (SysTick): Cortex-M コアに内蔵されているタイマーで、RTOS の時間管理や遅延処理などに利用されます。
1.2 タイマーの基本構造
STM32 タイマーの基本的な構造は以下の通りです。
-
クロックソース: タイマーの動作クロックを決定します。 内部クロック (PCLK1, PCLK2) または外部クロックを選択できます。
-
プリスケーラ (Prescaler): クロックソースの周波数を分周し、タイマーのカウント周波数を調整します。 これにより、タイマーの分解能を向上させることができます。
-
カウンター (Counter): クロックパルスをカウントするレジスタです。 アップカウント、ダウンカウント、センターアライメントなど、カウントモードを選択できます。
-
自動リロードレジスタ (Auto-Reload Register, ARR): カウンターが最大値 (または最小値) に達したときに、カウンターにロードされる値を設定します。 これにより、タイマーの周期を決定します。
-
コンペアレジスタ (Capture/Compare Register, CCRx): 出力比較モードで使用され、カウンターの値と比較される値を設定します。
-
割り込み (Interrupt): タイマーイベント (オーバーフロー、アンダーフロー、コンペアマッチなど) が発生したときに生成される割り込みです。
1.3 タイマーの設定項目
STM32 タイマーを設定するには、以下のレジスタを設定する必要があります。
-
制御レジスタ (CR1, CR2): タイマーのイネーブル、カウントモード、クロックディビジョンなどの設定を行います。
-
モードレジスタ (SMCR): スレーブモード制御の設定を行います。
-
DMA/割り込みイネーブルレジスタ (DIER): 割り込みまたは DMA 要求をイネーブルにします。
-
ステータスレジスタ (SR): タイマーイベントのフラグを確認します。
-
イベント生成レジスタ (EGR): タイマーイベントをソフトウェアで生成します。
-
キャプチャ/コンペアモードレジスタ (CCMRx): キャプチャ/コンペアモードの設定を行います。
-
キャプチャ/コンペアイネーブルレジスタ (CCER): キャプチャ/コンペア出力をイネーブルにします。
-
カウンターレジスタ (CNT): 現在のカウンター値を読み書きします。
-
プリスケーラレジスタ (PSC): プリスケーラ値を設定します。
-
自動リロードレジスタ (ARR): 自動リロード値を設定します。
-
キャプチャ/コンペアレジスタ (CCRx): キャプチャ/コンペア値を設定します。
2. 目的別 タイマー設定例
ここでは、具体的なアプリケーション例を挙げて、STM32 タイマーの設定方法を解説します。
2.1 時間計測 (タイマー割り込み)
最も基本的なタイマーの利用方法として、一定時間ごとに割り込みを発生させる時間計測があります。 これは、時間ベースのタスクスケジューリングや、周期的なデータ収集などに利用できます。
設定例:
- 使用タイマー: TIM2 (汎用タイマー)
- 割り込み周期: 1ms
- クロックソース: PCLK1 (84MHz)
- プリスケーラ: 84 – 1 (1MHz に分周)
- 自動リロード値: 1000 – 1 (1ms = 1000us)
コード例 (HAL ライブラリ使用):
“`c
// 1. タイマーハンドラ定義
TIM_HandleTypeDef htim2;
// 2. タイマー初期化関数
void TIM2_Init(void) {
htim2.Instance = TIM2;
htim2.Init.Prescaler = 83; // 84MHz / 84 = 1MHz
htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
htim2.Init.Period = 999; // 1MHz / 1000 = 1kHz (1ms)
htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
HAL_TIM_Base_Init(&htim2);
// 3. 割り込み設定
HAL_NVIC_SetPriority(TIM2_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(TIM2_IRQn);
}
// 4. タイマー割り込みハンドラ
void TIM2_IRQHandler(void) {
HAL_TIM_IRQHandler(&htim2);
// 5. 割り込み処理
// 1ms ごとに実行される処理を記述
// 例: LED をトグルする
HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);
}
// 6. HAL ライブラリのコールバック関数
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {
if (htim->Instance == TIM2) {
// ここでも割り込み処理を記述可能 (TIM2_IRQHandler と同じ処理を実行)
}
}
// 7. タイマースタート
HAL_TIM_Base_Start_IT(&htim2);
“`
解説:
TIM_HandleTypeDef
構造体を定義し、タイマーのインスタンス (TIM2
) を指定します。TIM2_Init
関数で、プリスケーラ、カウンターモード、自動リロード値、クロックディビジョンなどのタイマーパラメータを設定します。HAL_NVIC_SetPriority
とHAL_NVIC_EnableIRQ
関数で、タイマー割り込みの優先度を設定し、割り込みを有効にします。TIM2_IRQHandler
は、タイマー割り込みが発生したときに実行される割り込みハンドラです。HAL_TIM_IRQHandler
関数は、HAL ライブラリが提供する標準的な割り込み処理関数で、タイマー割り込みフラグをクリアします。HAL_TIM_PeriodElapsedCallback
は、HAL ライブラリが提供するコールバック関数で、タイマーの周期が経過したときに実行されます。HAL_TIM_Base_Start_IT
関数で、タイマーを割り込みモードで起動します。
注意点:
- クロックソース、プリスケーラ、自動リロード値は、目的の割り込み周期に合わせて適切に設定する必要があります。
- 割り込みハンドラ内での処理は、できるだけ短時間で完了するように心がけてください。 長時間の処理を行う場合は、別のタスクに処理を委譲することを検討してください。
2.2 PWM 制御
PWM (Pulse Width Modulation) 制御は、デューティサイクルを変化させることで、出力電圧を制御する技術です。 モーター制御、LED の明るさ調整、サーボモーター制御など、様々なアプリケーションに利用されます。
設定例:
- 使用タイマー: TIM3 (汎用タイマー)
- PWM 周波数: 1kHz
- クロックソース: PCLK1 (84MHz)
- プリスケーラ: 84 – 1 (1MHz に分周)
- 自動リロード値: 1000 – 1 (1ms = 1000us)
- 出力チャネル: TIM3_CH1
- PWM モード: PWM1
コード例 (HAL ライブラリ使用):
“`c
// 1. タイマーハンドラ定義
TIM_HandleTypeDef htim3;
GPIO_InitTypeDef GPIO_InitStruct = {0};
// 2. タイマー初期化関数
void TIM3_PWM_Init(void) {
htim3.Instance = TIM3;
htim3.Init.Prescaler = 83; // 84MHz / 84 = 1MHz
htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
htim3.Init.Period = 999; // 1MHz / 1000 = 1kHz
htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim3.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
HAL_TIM_PWM_Init(&htim3);
// 3. PWM チャネル設定
TIM_OC_InitTypeDef sConfigOC = {0};
sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = 0; // 初期デューティサイクル 0%
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
HAL_TIM_PWM_ConfigChannel(&htim3, &sConfigOC, TIM_CHANNEL_1);
// 4. GPIO 設定
__HAL_RCC_GPIOB_CLK_ENABLE();
GPIO_InitStruct.Pin = GPIO_PIN_4; // TIM3_CH1 出力ピン (例: PB4)
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.Alternate = GPIO_AF2_TIM3; // Alternate Function
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}
// 5. PWM デューティサイクル設定関数
void TIM3_PWM_SetDutyCycle(uint32_t duty) {
TIM_OC_InitTypeDef sConfigOC = {0};
sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = duty;
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
HAL_TIM_PWM_ConfigChannel(&htim3, &sConfigOC, TIM_CHANNEL_1);
HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1); // デューティサイクル変更後は必ず再スタート
}
// 6. PWM スタート
HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1);
“`
解説:
TIM_HandleTypeDef
構造体を定義し、タイマーのインスタンス (TIM3
) を指定します。TIM3_PWM_Init
関数で、プリスケーラ、カウンターモード、自動リロード値、クロックディビジョンなどのタイマーパラメータを設定します。TIM_OC_InitTypeDef
構造体で、PWM モード、初期デューティサイクル、極性、高速モードなどの PWM チャネルパラメータを設定します。HAL_TIM_PWM_ConfigChannel
関数で、PWM チャネルを設定します。GPIO_InitTypeDef
構造体で、PWM 出力ピンの GPIO 設定を行います。GPIO_MODE_AF_PP
は、Alternate Function Push-Pull モードを指定します。GPIO_AF2_TIM3
は、TIM3 の Alternate Function を指定します。TIM3_PWM_SetDutyCycle
関数で、PWM のデューティサイクルを設定します。 デューティサイクル変更後は、HAL_TIM_PWM_Start
関数で PWM を再スタートする必要があります。HAL_TIM_PWM_Start
関数で、PWM を起動します。
注意点:
- PWM 周波数、デューティサイクル、出力ピンは、アプリケーションに合わせて適切に設定する必要があります。
- PWM 周波数が高すぎると、出力波形が歪む可能性があります。 PWM 周波数が低すぎると、ちらつきが発生する可能性があります。
- デューティサイクルの範囲は、0 から自動リロード値 (ARR) までです。
- PWM 出力ピンは、タイマーの Alternate Function として正しく設定されている必要があります。
2.3 入力キャプチャ
入力キャプチャは、外部信号のエッジを検出して、その時点のカウンター値を読み取る機能です。 エンコーダーの回転速度計測、パルス幅計測、周波数計測など、様々なアプリケーションに利用されます。
設定例:
- 使用タイマー: TIM4 (汎用タイマー)
- 入力チャネル: TIM4_CH1
- 入力エッジ: 上昇エッジ
- クロックソース: PCLK1 (84MHz)
- プリスケーラ: 0 (分周なし)
コード例 (HAL ライブラリ使用):
“`c
// 1. タイマーハンドラ定義
TIM_HandleTypeDef htim4;
GPIO_InitTypeDef GPIO_InitStruct = {0};
// 2. タイマー初期化関数
void TIM4_IC_Init(void) {
htim4.Instance = TIM4;
htim4.Init.Prescaler = 0;
htim4.Init.CounterMode = TIM_COUNTERMODE_UP;
htim4.Init.Period = 0xFFFF; // 最大値までカウント
htim4.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim4.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
HAL_TIM_IC_Init(&htim4);
// 3. 入力キャプチャチャネル設定
TIM_IC_InitTypeDef sConfigIC = {0};
sConfigIC.ICPolarity = TIM_ICPOLARITY_RISING; // 上昇エッジ検出
sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI;
sConfigIC.ICPrescaler = TIM_ICPSC_DIV1;
sConfigIC.ICFilter = 0;
HAL_TIM_IC_ConfigChannel(&htim4, &sConfigIC, TIM_CHANNEL_1);
// 4. GPIO 設定
__HAL_RCC_GPIOD_CLK_ENABLE();
GPIO_InitStruct.Pin = GPIO_PIN_12; // TIM4_CH1 入力ピン (例: PD12)
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.Alternate = GPIO_AF2_TIM4; // Alternate Function
HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
// 5. 割り込み設定
HAL_NVIC_SetPriority(TIM4_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(TIM4_IRQn);
}
// 6. タイマー割り込みハンドラ
void TIM4_IRQHandler(void) {
HAL_TIM_IRQHandler(&htim4);
}
// 7. HAL ライブラリのコールバック関数
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim) {
if (htim->Instance == TIM4) {
// 8. キャプチャ値の読み取り
uint32_t capture_value = HAL_TIM_ReadCapturedValue(&htim4, TIM_CHANNEL_1);
// 9. キャプチャ値を使った処理
// 例: パルス幅計測
}
}
// 10. 入力キャプチャスタート
HAL_TIM_IC_Start_IT(&htim4, TIM_CHANNEL_1);
“`
解説:
TIM_HandleTypeDef
構造体を定義し、タイマーのインスタンス (TIM4
) を指定します。TIM4_IC_Init
関数で、プリスケーラ、カウンターモード、自動リロード値、クロックディビジョンなどのタイマーパラメータを設定します。TIM_IC_InitTypeDef
構造体で、入力エッジ、入力選択、プリスケーラ、フィルタなどの入力キャプチャチャネルパラメータを設定します。HAL_TIM_IC_ConfigChannel
関数で、入力キャプチャチャネルを設定します。GPIO_InitTypeDef
構造体で、入力ピンの GPIO 設定を行います。GPIO_MODE_AF_PP
は、Alternate Function Push-Pull モードを指定します。GPIO_AF2_TIM4
は、TIM4 の Alternate Function を指定します。HAL_NVIC_SetPriority
とHAL_NVIC_EnableIRQ
関数で、タイマー割り込みの優先度を設定し、割り込みを有効にします。TIM4_IRQHandler
は、タイマー割り込みが発生したときに実行される割り込みハンドラです。HAL_TIM_IRQHandler
関数は、HAL ライブラリが提供する標準的な割り込み処理関数で、タイマー割り込みフラグをクリアします。HAL_TIM_IC_CaptureCallback
は、HAL ライブラリが提供するコールバック関数で、入力キャプチャが発生したときに実行されます。HAL_TIM_ReadCapturedValue
関数で、キャプチャ値を読み取ります。- キャプチャ値を使って、パルス幅計測などの処理を行います。
HAL_TIM_IC_Start_IT
関数で、入力キャプチャを割り込みモードで起動します。
注意点:
- 入力エッジ、入力ピンは、アプリケーションに合わせて適切に設定する必要があります。
- 入力信号の周波数が高すぎると、キャプチャ値が正しく読み取れない可能性があります。
- 入力ピンは、タイマーの Alternate Function として正しく設定されている必要があります。
3. トラブルシューティング
STM32 タイマーの設定で問題が発生した場合、以下の点を確認してください。
- クロック設定: タイマーに適切なクロックが供給されているか確認してください。 RCC (Reset and Clock Control) ペリフェラルで、タイマーのクロックイネーブルビットが設定されているか確認してください。
- プリスケーラ値: プリスケーラ値が意図した値になっているか確認してください。
- 自動リロード値: 自動リロード値が意図した値になっているか確認してください。
- 割り込み設定: 割り込みが有効になっているか確認してください。 NVIC (Nested Vector Interrupt Controller) で、タイマー割り込みのイネーブルビットが設定されているか確認してください。
- GPIO 設定: タイマーの出力ピンまたは入力ピンの GPIO 設定が正しいか確認してください。 Alternate Function が正しく設定されているか確認してください。
- デバッグ: デバッガーを使用して、タイマーのレジスタの値を確認してください。 カウンター値、キャプチャ値、コンペア値などを確認することで、問題の原因を特定できる場合があります。
- データシート: STM32 のデータシートを参照して、タイマーの仕様を確認してください。 設定可能な範囲や、制限事項などを確認してください。
- サンプルコード: STMicroelectronics が提供するサンプルコードを参考にしてください。 類似のアプリケーションのサンプルコードを参考にすることで、設定方法のヒントが得られる場合があります。
- コミュニティ: STM32 のコミュニティフォーラムや、Stack Overflow などの Q&A サイトで質問してください。 他のユーザーの経験や知識を参考にすることで、問題解決の糸口が見つかる場合があります。
3.1 よくある問題とその解決策
-
割り込みが発生しない:
- クロックが有効になっていない: RCC ペリフェラルのクロックイネーブルビットを確認
- 割り込みが有効になっていない: NVIC のイネーブルビットを確認
- 割り込みハンドラが定義されていない: 正しい名前の割り込みハンドラ関数が定義されているか確認
- ステータスフラグがクリアされていない: 割り込みハンドラ内でステータスフラグをクリア
-
PWM 出力が意図した通りにならない:
- クロックが有効になっていない: RCC ペリフェラルのクロックイネーブルビットを確認
- PWM モードが正しく設定されていない: TIM_OCMODE_PWM1 または TIM_OCMODE_PWM2 を確認
- デューティサイクルが範囲外: 0 から ARR の範囲内であることを確認
- GPIO 設定が正しくない: Alternate Function が正しく設定されているか確認
- コンペアレジスタの値が期待値と異なる: デバッガーで CCRx の値を確認
-
入力キャプチャが機能しない:
- クロックが有効になっていない: RCC ペリフェラルのクロックイネーブルビットを確認
- 入力エッジが正しく設定されていない: TIM_ICPOLARITY_RISING または TIM_ICPOLARITY_FALLING を確認
- GPIO 設定が正しくない: Alternate Function が正しく設定されているか確認
- キャプチャ値が更新されない: デバッガーで CCRx の値を確認
- 入力信号の周波数が高すぎる: タイマーのクロック周波数に対して適切か確認
- 入力信号の振幅が小さすぎる: マイコンの入力電圧範囲内か確認
4. まとめ
STM32 タイマーは、多様な機能を持つ強力なペリフェラルです。 本記事では、STM32 タイマーの基本的な概念から、具体的な設定例、そしてトラブルシューティングまで、詳細に解説しました。 本記事が、STM32 タイマーを活用したアプリケーション開発の一助となれば幸いです。 ぜひ、様々なアプリケーションに挑戦してみてください。