STM32 HAL:タイマ機能を使った制御プログラミング

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のデータシートやリファレンスマニュアルを参照することで、さらに高度なタイマ機能についても理解を深めることができます。

コメントする

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

上部へスクロール