STM32 HAL:タイマ機能を使った制御プログラミング – 詳細解説
組み込みシステム開発において、タイマは時間ベースの処理、周期的なタスクの実行、イベントのトリガー、PWM制御など、非常に重要な役割を果たします。STM32マイコンは、汎用タイマ(TIMx)、高度な制御タイマ(TIM1, TIM8など)、基本タイマ(TIM6, TIM7)、低消費電力タイマ(LPTIM)、システムタイマ(SysTick)など、豊富な種類のタイマを内蔵しており、多様な用途に対応できます。
この記事では、STM32マイコンのHALライブラリを用いて、これらのタイマ機能を効果的に活用し、制御プログラミングを行うための詳細な解説を行います。タイマの基本的な概念から、HALライブラリによる初期化と設定、割り込み処理、PWM制御、入力キャプチャ、エンコーダインターフェースなど、実践的な応用例まで幅広くカバーします。
1. タイマの基本概念
まず、STM32マイコンのタイマについて理解を深めるために、基本的な概念を整理します。
- カウンタ: タイマの中核となる要素で、クロック信号が入力されるたびに値を増加(または減少)させます。カウンタの最大値は、タイマの分解能とオーバーフロー周期を決定します。
- プリスケーラ: クロック信号の周波数を分周するための機能です。プリスケーラを使用することで、カウンタのクロック周波数を下げ、より長い時間間隔を計測したり、より細かい時間分解能を実現したりできます。
- オートリロードレジスタ(ARR): カウンタがこのレジスタに設定された値に達すると、カウンタは0(または別の設定値)にリセットされ、カウント動作を再開します。ARRは、タイマの周期を設定するために使用されます。
- 比較レジスタ(CCR): カウンタの値とCCRの値が一致すると、特定のイベント(割り込みの発生、出力ピンのレベル変更など)をトリガーできます。PWM制御や入力キャプチャなどで使用されます。
- 割り込み: タイマの特定のイベント(カウンタのオーバーフロー、CCRとの一致など)が発生した際に、割り込みを発生させることができます。割り込みハンドラ内で、必要な処理(タスクの実行、フラグの設定など)を行うことができます。
2. STM32 HALライブラリにおけるタイマの初期化と設定
STM32 HALライブラリを使用すると、タイマの初期化と設定を容易に行うことができます。HALライブラリは、タイマの種類や動作モードに応じて、様々な関数を提供しています。
2.1. タイマインスタンスの作成
まず、使用するタイマに対応するHALの構造体変数を作成します。例えば、TIM2を使用する場合、以下のようになります。
c
TIM_HandleTypeDef htim2;
2.2. タイマ初期化構造体の設定
次に、TIM_HandleTypeDef
構造体のメンバを設定し、タイマの初期化パラメータを指定します。
c
htim2.Instance = TIM2; // 使用するタイマのインスタンス
htim2.Init.Prescaler = 71; // プリスケーラの設定 (72MHz / (71 + 1) = 1MHz)
htim2.Init.CounterMode = TIM_COUNTERMODE_UP; // カウンタのモード(アップカウント)
htim2.Init.Period = 999; // オートリロード値の設定 (1MHz / 1000 = 1kHz)
htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; // クロック分割の設定
htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE; // オートリロードプリロードの設定
Instance
: 使用するタイマのインスタンスを指定します。TIM1
,TIM2
,TIM3
などの定義済みマクロを使用します。Init.Prescaler
: プリスケーラを設定します。入力クロック周波数を分周することで、カウンタのクロック周波数を調整できます。Init.CounterMode
: カウンタのモードを設定します。TIM_COUNTERMODE_UP
(アップカウント)、TIM_COUNTERMODE_DOWN
(ダウンカウント)、TIM_COUNTERMODE_CENTER_ALIGNED1
(中央揃えモード1)などがあります。Init.Period
: オートリロードレジスタ(ARR)の値を設定します。カウンタがこの値に達すると、カウンタはリセットされ、カウントを再開します。Init.ClockDivision
: タイマのクロック分割を設定します。TIM_CLOCKDIVISION_DIV1
,TIM_CLOCKDIVISION_DIV2
,TIM_CLOCKDIVISION_DIV4
などがあります。Init.AutoReloadPreload
: オートリロードプリロードを有効にするかどうかを設定します。有効にすると、ARRの値を実行中に変更しても、次のオーバーフローから有効になります。
2.3. HAL_TIM_Base_Init()関数によるタイマの初期化
設定したパラメータに基づいて、HAL_TIM_Base_Init()
関数を呼び出し、タイマを初期化します。
c
if (HAL_TIM_Base_Init(&htim2) != HAL_OK)
{
Error_Handler(); // エラー処理
}
2.4. タイマの起動
タイマを起動するには、HAL_TIM_Base_Start()
関数を使用します。
c
HAL_TIM_Base_Start(&htim2);
2.5. タイマの停止
タイマを停止するには、HAL_TIM_Base_Stop()
関数を使用します。
c
HAL_TIM_Base_Stop(&htim2);
3. 割り込み処理
タイマの割り込みを利用することで、特定の時間間隔でタスクを実行したり、イベントをトリガーしたりできます。
3.1. 割り込みの有効化
まず、HAL_TIM_Base_Start_IT()
関数を使用して、タイマの割り込みを有効にします。
c
HAL_TIM_Base_Start_IT(&htim2);
この関数は、タイマのオーバーフロー割り込み(Update Event)を有効にします。
3.2. 割り込みハンドラの定義
次に、割り込みハンドラを定義します。STM32の割り込みベクトルテーブルにおいて、TIM2の割り込みハンドラはTIM2_IRQHandler()
という名前で定義されています。
c
void TIM2_IRQHandler(void)
{
HAL_TIM_IRQHandler(&htim2);
}
HAL_TIM_IRQHandler()
関数は、どのタイマイベントが発生したかを判断し、対応するコールバック関数を呼び出します。
3.3. コールバック関数の定義
HAL_TIM_IRQHandler()
関数によって呼び出されるコールバック関数を定義します。タイマのオーバーフロー割り込みの場合、HAL_TIM_PeriodElapsedCallback()
関数が呼び出されます。
c
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if (htim == &htim2)
{
// TIM2のオーバーフロー割り込みが発生した場合の処理
HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5); // LEDをトグル
}
}
この例では、TIM2のオーバーフロー割り込みが発生するたびに、GPIOAのピン5に接続されたLEDをトグルします。
3.4. NVICの設定
最後に、NVIC(Nested Vectored Interrupt Controller)を設定し、タイマの割り込みを有効にする必要があります。これは、HAL_TIM_Base_Init()
関数が内部的に行っていますが、念のため確認しておきましょう。
c
HAL_NVIC_SetPriority(TIM2_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(TIM2_IRQn);
4. PWM制御
PWM(Pulse Width Modulation)制御は、デューティ比を調整することで、アナログ信号をエミュレートする技術です。STM32のタイマは、PWM信号を生成するための専用のモードを備えています。
4.1. PWMモードの設定
まず、タイマをPWMモードに設定します。TIM_OC_InitTypeDef
構造体を使用して、PWMモードのパラメータを指定します。
“`c
TIM_OC_InitTypeDef sConfigOC;
sConfigOC.OCMode = TIM_OCMODE_PWM1; // PWMモード1
sConfigOC.Pulse = 500; // パルス幅の設定 (デューティ比 50%)
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH; // 極性の設定 (HIGHアクティブ)
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE; // 高速モードの無効化
if (HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_1) != HAL_OK)
{
Error_Handler();
}
“`
OCMode
: PWMモードを設定します。TIM_OCMODE_PWM1
またはTIM_OCMODE_PWM2
を選択します。Pulse
: パルス幅(オン時間)を設定します。この値は、オートリロード値(ARR)との比率によってデューティ比が決まります。OCPolarity
: 極性を設定します。TIM_OCPOLARITY_HIGH
(HIGHアクティブ)またはTIM_OCPOLARITY_LOW
(LOWアクティブ)を選択します。OCFastMode
: 高速モードを有効にするかどうかを設定します。高速モードを有効にすると、立ち上がり/立ち下がり時間を短縮できますが、消費電力が増加します。
HAL_TIM_PWM_ConfigChannel()
関数は、指定されたチャネル(TIM_CHANNEL_1, TIM_CHANNEL_2, TIM_CHANNEL_3, TIM_CHANNEL_4)に対してPWMモードを設定します。
4.2. PWMの起動
PWM信号の生成を開始するには、HAL_TIM_PWM_Start()
関数を使用します。
c
HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1);
4.3. デューティ比の変更
PWM信号のデューティ比を動的に変更するには、__HAL_TIM_SET_COMPARE()
マクロを使用します。
c
__HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1, new_pulse_width);
new_pulse_width
は、新しいパルス幅の値です。この値を変更すると、PWM信号のデューティ比がリアルタイムに更新されます。
5. 入力キャプチャ
入力キャプチャは、外部信号のエッジを検出して、その時点のカウンタ値を読み取る機能です。これを使用することで、信号の周波数、パルス幅、周期などを計測できます。
5.1. 入力キャプチャモードの設定
まず、タイマを入力キャプチャモードに設定します。TIM_IC_InitTypeDef
構造体を使用して、入力キャプチャモードのパラメータを指定します。
“`c
TIM_IC_InitTypeDef sConfigIC;
sConfigIC.ICPolarity = TIM_ICPOLARITY_RISING; // 立上がりエッジでキャプチャ
sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI; // 入力チャンネルの選択
sConfigIC.ICPrescaler = TIM_ICPSC_DIV1; // プリスケーラの設定 (分周なし)
sConfigIC.ICFilter = 0; // フィルタの設定 (フィルタなし)
if (HAL_TIM_IC_ConfigChannel(&htim2, &sConfigIC, TIM_CHANNEL_1) != HAL_OK)
{
Error_Handler();
}
“`
ICPolarity
: キャプチャするエッジの極性を設定します。TIM_ICPOLARITY_RISING
(立上がりエッジ)またはTIM_ICPOLARITY_FALLING
(立下がりエッジ)を選択します。ICSelection
: 入力チャンネルの選択を設定します。TIM_ICSELECTION_DIRECTTI
(直接入力)またはTIM_ICSELECTION_INDIRECTTI
(間接入力)を選択します。ICPrescaler
: プリスケーラを設定します。入力信号の周波数が高い場合に、プリスケーラを使用して分周できます。ICFilter
: フィルタを設定します。ノイズの影響を軽減するために、フィルタを使用できます。
HAL_TIM_IC_ConfigChannel()
関数は、指定されたチャネル(TIM_CHANNEL_1, TIM_CHANNEL_2, TIM_CHANNEL_3, TIM_CHANNEL_4)に対して入力キャプチャモードを設定します。
5.2. 入力キャプチャの起動
入力キャプチャを開始するには、HAL_TIM_IC_Start_IT()
関数を使用します。
c
HAL_TIM_IC_Start_IT(&htim2, TIM_CHANNEL_1);
この関数は、入力キャプチャ割り込みを有効にします。
5.3. キャプチャ値の読み取り
入力キャプチャが発生すると、割り込みハンドラが呼び出されます。割り込みハンドラ内で、HAL_TIM_ReadCapturedValue()
関数を使用して、キャプチャされたカウンタ値を読み取ります。
c
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
if (htim == &htim2)
{
if (htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1)
{
// TIM2のチャネル1で入力キャプチャが発生した場合の処理
uint32_t capture_value = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1);
// キャプチャされた値を使用して、周波数、パルス幅などを計算
}
}
}
6. エンコーダインターフェース
STM32のタイマは、インクリメンタルエンコーダのインターフェースとしても使用できます。エンコーダインターフェースを使用することで、エンコーダの回転方向と回転量を正確に検出できます。
6.1. エンコーダモードの設定
まず、タイマをエンコーダモードに設定します。TIM_Encoder_InitTypeDef
構造体を使用して、エンコーダモードのパラメータを指定します。
“`c
TIM_Encoder_InitTypeDef sConfig;
sConfig.EncoderMode = TIM_ENCODERMODE_TI12; // エンコーダモードの設定 (TI1とTI2を使用)
sConfig.IC1Polarity = TIM_ICPOLARITY_RISING; // TI1の極性の設定 (立上がりエッジ)
sConfig.IC1Selection = TIM_ICSELECTION_DIRECTTI; // TI1の入力チャンネルの選択
sConfig.IC1Prescaler = TIM_ICPSC_DIV1; // TI1のプリスケーラの設定 (分周なし)
sConfig.IC1Filter = 0; // TI1のフィルタの設定 (フィルタなし)
sConfig.IC2Polarity = TIM_ICPOLARITY_RISING; // TI2の極性の設定 (立上がりエッジ)
sConfig.IC2Selection = TIM_ICSELECTION_DIRECTTI; // TI2の入力チャンネルの選択
sConfig.IC2Prescaler = TIM_ICPSC_DIV1; // TI2のプリスケーラの設定 (分周なし)
sConfig.IC2Filter = 0; // TI2のフィルタの設定 (フィルタなし)
if (HAL_TIM_Encoder_ConfigChannel(&htim2, &sConfig, TIM_CHANNEL_ALL) != HAL_OK)
{
Error_Handler();
}
“`
EncoderMode
: エンコーダモードを設定します。TIM_ENCODERMODE_TI12
(TI1とTI2を使用)、TIM_ENCODERMODE_TI1
(TI1のみを使用)、TIM_ENCODERMODE_TI2
(TI2のみを使用)を選択します。IC1Polarity
,IC2Polarity
: エンコーダのA相、B相に対応する入力信号の極性を設定します。IC1Selection
,IC2Selection
: 入力チャンネルの選択を設定します。IC1Prescaler
,IC2Prescaler
: プリスケーラを設定します。IC1Filter
,IC2Filter
: フィルタを設定します。
HAL_TIM_Encoder_ConfigChannel()
関数は、指定されたチャネル(TIM_CHANNEL_ALL)に対してエンコーダモードを設定します。
6.2. エンコーダの起動
エンコーダインターフェースを開始するには、HAL_TIM_Encoder_Start()
関数を使用します。
c
HAL_TIM_Encoder_Start(&htim2, TIM_CHANNEL_ALL);
6.3. エンコーダ値の読み取り
エンコーダの回転量を読み取るには、__HAL_TIM_GET_COUNTER()
マクロを使用します。
c
int32_t encoder_value = __HAL_TIM_GET_COUNTER(&htim2);
この値は、エンコーダの回転方向と回転量に応じて、増加または減少します。オーバーフローまたはアンダーフローが発生する可能性があるため、適切な範囲に制限する必要があります。
7. 応用例
7.1. 周期的なタスクの実行
タイマの割り込みを利用して、特定の時間間隔でタスクを実行できます。例えば、10msごとにセンサーの値を読み取り、処理を行うことができます。
c
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if (htim == &htim2)
{
// 10msごとに実行されるタスク
read_sensor_data();
process_data();
}
}
7.2. モータ制御
PWM制御を利用して、モータの速度やトルクを制御できます。デューティ比を調整することで、モータに供給する電圧を制御し、速度を調整できます。
7.3. LED調光
PWM制御を利用して、LEDの明るさを調整できます。デューティ比を調整することで、LEDに供給する電流量を制御し、明るさを調整できます。
7.4. 距離センサの計測
入力キャプチャを利用して、超音波距離センサの距離を計測できます。トリガー信号を送信し、エコー信号を受信するまでの時間を計測することで、距離を算出できます。
7.5. 回転速度の計測
入力キャプチャを利用して、回転体の回転速度を計測できます。回転体に設置されたエンコーダからの信号をキャプチャし、単位時間あたりのパルス数を計測することで、回転速度を算出できます。
8. まとめ
この記事では、STM32マイコンのHALライブラリを用いて、タイマ機能を効果的に活用し、制御プログラミングを行うための詳細な解説を行いました。タイマの基本的な概念から、HALライブラリによる初期化と設定、割り込み処理、PWM制御、入力キャプチャ、エンコーダインターフェースなど、実践的な応用例まで幅広くカバーしました。
タイマは、組み込みシステム開発において非常に重要な役割を果たします。この記事で学んだ知識を活かし、様々な制御アプリケーションを開発してください。また、STM32のデータシートやリファレンスマニュアルを参照することで、さらに高度なタイマ機能についても理解を深めることができます。