タイマ・カウンタ【STM32の高機能・汎用タイマ詳細】
マイコンのタイマ・カウンタは外部から与えた入力パルス数のカウント、入力パルス周波数の計算、任意の周波数パルス出力、PWMパルスの出力、そしてパルス以外では時間計測などタイマ・カウンタを組み合わせた用途は広いです。
STM32のタイマ・カウンタペリフェラルはとても多機能ですべてを理解するのは大変ですがこのサイトでは最も実用的な機能を抜粋して解説しています。タイマ・カウンタの基本動作からタイマを利用したさまざまな出力の仕方を解説しています。
ライブラリはSPLを使用していますが基本動画が理解できれば将来的にHAL等への移植は難しくはないでしょう。
目次
ペリフェラルのタイマ・カウンタ
マイコンのペリフェラル(周辺機能)のなかでもタイマ・カウンタは最も使用頻度が高いものではないでしょうか。
プログラム自体が処理時間、経過時間概念の組み合わせで成り立っていますのでタイマ・カウンタ機能は組み込み技術には欠かせないものです。これを使いこなせるようになれば幅広いアプリケーションの開発ができるようになりますのでここはしっかり理解していきましょう。
前章で解説したシステムタイマであるSysTickタイマは動作周波数であるシステムクロックをカウントして周期的に割り込みを発生させてその中で処理をする比較的簡易なものでした。ただし、割り込み処理をCPUが実行しているので高速になるほどCPUに負担がかかります。
これに対してペリフェラルの高機能・汎用タイマはCPUとは独立した専用回路が直接出力を操作しますので、割り込み処理を必要とするシステムタイマに比べCPUに負担をかけず効率化できます。
高機能タイマTIM1,TIM8(マイコンによります)はAPB2 バスに接続されていてAPB2プリスケーラ、逓倍回路を経てクロックが供給されます(ここでは72MHzのクロック)。汎用タイマTIM2-7(マイコンによります)はAPB1バスに接続されていてAPB1プリスケーラ、逓倍回路を経てクロックが供給されます(ここでは36MHzのクロック)。
これから解説する高機能・汎用タイマはシステムタイマのように一定時間ごとの割り込みを発生させる機能に加えて、任意の周波数のパルスを発生させることもできます。
タイマに供給される内部クロックだけでなく外部からの信号や他のタイマからの出力信号をカウントすることができます。
STM32マイコンタイマの外部のクロック源をクロックとして供給する場合はタイマ用の各チャネルのピン(TIMx-CHy: xタイマ番号、yチャネル番号)をクロック入力用に使用する外部クロックモード1とタイマの外部トリガ用専用ピン(TIMx-ETR)をクロック入力用に使用する外部クロックモード2があります。
高機能タイマと汎用タイマ
ここで、STM32マイコンでいう高機能タイマと汎用タイマの違いについて言及します。どちらもすでに十分な機能を持ち合わせているのですが、高機能タイマは通常のタイマ機能に加えてさらにデッドタイム付きPWMおよび相補出力を生成する機能などがあります。
この機能はモータ制御に使用するインバータなどパワーエレクトロニクスのスイッチングタイミングをコントロールするのに必要な高度な特化した機能ですので通常の用途では必要ありません。
また、高機能タイマを使用する場合、汎用タイマには必要のない設定を行わなければタイマ出力が無効となる場合がありますので使用しない機能でもすべて設定するのが無難です。
汎用タイマでも十分多機能ですので慣れるまでは少しでも設定の簡素化した汎用タイマ(TIM2-7:マイコンによります)を使用することをおすすめします。
註: NUCLEO-F103RB 搭載のSTM32F103RBではタイマはTIM1-4で選定します 。
高機能タイマはTIM1だけです。
タイマ・カウンタの動作
入力したクロックパルスのカウント数を格納するレジスタをカウンタレジスタといい、カウント上限回数を設定するレジスタを自動リロードレジスタ(ARR:Auto Reload Register)をいいます。
パルスのカウント方法は3つあり、カウント数0から設定値ARRまで加算する方式をアップカウント、設定値ARRから0まで減算する方式をダウンカウント、そして0と設定値ARRの間で加算、減算を繰り返す方式をセンターアラインカウントといいます。
アップカウントの場合はカウント数(カウンタレジスタ値)がARRに到達して次のカウントをしたときにカウンタオーバーフローになって、割り込みなどを発生させます。ダウンカウンタの場合はカウント数が0になって次のカウントをしたときにカウンタアンダーフローになって割り込みなどを発生させます。センターアラインカウントの場合は加算でARRに到達しオーバーフロー、減算で0に到達しアンダーフローで割り込みなどを発生させます。
各タイマにはプリスケーラと呼ばれる分周器が内蔵されています。プリスケーラは16ビットカウンタで構成されていますのでカウンタクロックを最大65535分の1(1/216)まで任意に遅くすることができます。
カウント動作中に加算時のオーバーフローや減算時のアンダーフローが発生すると割り込みなどの更新イベントが発生します。汎用タイマでは更新イベントを繰り返します。高機能タイマでは更新イベントの発生タイミングや繰り返しカウンタの回数などを設定できますがここでは言及しません。
タイマ機能の役割は
① カウント結果に応じて割り込みなどのイベントを発生させる
② カウント結果に応じてタイマ出力の状態を変化させる
③ 外部クロックの回数カウントやパルス幅の計測 の3種類があります。
①は前章のSisTickタイマと同じような使い方です。
②はカウンタを利用してパルス出力を発生させるもので例えばモータ制御の速度入力指令値などに利用されます。
③は①と②を組み合わせて使うことで信号の時間的な間隔を計測したりする使い方です。
例えばある外部信号の入力間隔を測りたい場合はタイマを利用すれば簡単です。
外部信号に対して十分細かい計測用内部クロックをカウントします。外部信号が入力されるとまず現状のカウント数を取得(カウント取得値A,B)してからカウント開始信号としてリセットします。そして次の外部信号入力まで計測用内部クロックをカウントするのを繰り返します。
カウント数は外部パルスの時間間隔(入力間隔A,B)つまり、外部パルス速度(時間間隔の逆数)を計測したことになります。後述しますが、外部信号は割り込み入力とするとより正確な計測ができます。
高機能・汎用タイマペリフェラルの使い方
タイマの機能を学ぶにはまず実際に使ってみることですが、ここでは基本的なものからはじめて利用方法を理解していきましょう。まず手ほどきに基本の出力比較モードをプログラムで解説していきます。
ここで出力比較モードとはカウント数を0と自動リロードレジスタ(ARR: Auto Reload Register)間に設定したキャプチャコンペアレジスタ(CCR: Capture Compare Register)と比較するモードです。カウント数がCCRと一致したときに出力が変化します。
実際のプログラムでの設定を解説していきます。
出力比較モードの設定例
目的:汎用タイマの出力比較モードで5kHzのパルスを出力する
■ タイマはTIM5の出力チャネルCH1-4のうちCH4(PA3)のみ使用
■ タイマへの供給クロックは36MHz
■ カンタ動作はアップカウンタモードで出力比較モード
註: NUCLEO-F103RB 搭載のSTM32F103RBではタイマはTIM1-4で選定します
① GPIOおよびタイマTIM5にクロック供給
GPIOポートAはAPB2に接続、TIM5はAPB1に接続しているのでそれぞれのペリフェラルにクロックを供給します。高機能タイマ(TIM1とTIM8:マイコンによります)の場合はAPB2に供給します。
② タイマに使うGPIOの設定
使用するタイマの出力ポートGPIOの設定を行います。PA3にタイマ出力のオルタネート出力を設定します。
③ タイマの初期化
次にタイマの初期化(TIM_TimeBaseInit関数)を行います。初期化項目は多岐にわたるので順次解説していきましょう。
タイマの初期化設定③ではタイマのプリスケールやカウンタ動作モードを指定します。初期化関数TIM_TimeBaseInit()を使用します。
タイマ初期化関数実行例:
TIM_TimeBaseInit(TIM5, &TIM_TimeBaseStructure);
関数の第1引数は設定対象タイマTIM5など指定します。第2引数は構造体メンバになっていて以下に示します。
設定例ではTIM_Prescalerを75-1に指定して36MHzのクロックを75分周してTIM5に480kHz(TIM5CLK)を供給します。
ARRはメンバTIM_Periodで設定します。
5kHzのタイマ出力が発生させるには10kHzごとに出力を反転(トグル)させればよいので48-1とします。
この例ではアップカウンタモードを設定しています。
④ タイマ出力の設定
タイマ出力の設定(TIM_OCxInit関数)を行います。この例ではCH4だけ有効にしています。
出力比較モードの詳細設定④ではタイマの出力仕様パラメータを指定します。
出力比較モード初期化関数実行例:
TIM_OC4Init(TIM5, &TIM_OCInitStructure);
TIM_OCxInit関数の第2引数はTIM_OCInitTypeDef型構造体のメンバとなっていて、下表にまとめますが、その中でTIM_OutputNState、TIM_OCNPolarityおよびTIM_OCNIdleStateは高機能タイマでのみ必要なものなので汎用タイマでは省略します。
⑤ タイマのカウントを開始:
すべての設定終了後、タイマのカウントを開始(TIM_Cmd関数)します。
この時点から出力が発生します。
ここまででタイマの設定ができましたのでTIM_Cmd関数を実行してタイマを有効化してカウントを始めます。
TIM_Cmd関数実行例:TIM_Cmd(TIM5, ENABLE);
TIM_ Cmd関数の第2引数にENBLEを指定するとカウントを開始し、DISABLEで停止します。
この時点でTIM5のCH4につながっているPA3に5kHzのプッシュプル出力が開始します。
このプログラム例では出力はCH4の1つのみですが、ほかのチャネルCH1-CH3を同時に使い、それぞれのチャネルに別々のCCR(キャプチャコンペアレジスタ値)を指定すると、同じパルスで位相をずらしたものが出力できます。
任意のタイミングで位相をずらす場合
任意のタイミングでCH4の位相をずらしたい場合は
TIM_SetCompare4関数でCH4の位相を変更できます。
TIM_SetCompare関数実行例:TIM_SetCompare4(TIM5, CCR_VALUE);
関数の第1引数は設定対象タイマTIM5など指定します。第2引数は出力比較モード設定内メンバのTIM_Pulseに相当するCRRの値を指定します。
任意の周波数に変更する場合
TIM5の周波数を変更したい場合はTIM_SetAutoreload関数で周波数を変更できます。
TIM_SetAutoreload関数実行例:TIM_SetAutoreload(TIM5, ARR_VALUE);
関数の第1引数は設定対象タイマTIM5など指定します。第2引数はタイマ初期化関数内メンバのTIM_ Periodに相当するARRの値を指定します。図ではアップカウンタでの例ですがカウント途中でARRを変更することになるのでダウンカウンタを使用するほうがよいです。
高機能タイマ出力の場合のみ
汎用タイマの場合はこれで設定は完了なのですが、高機能タイマの出力を有効化するにはさらにTIM_CtrlPWMOutputs(TIMx, ENABLE)を追加しておいてください。
タイマの応用
これまでのタイマの使い方としては、自動リロードレジスタ(ARR)の値を変更してカウントサイクル(周波数)を変え、キャプチャコンペアレジスタ(CCR)の値を変更することで、出力タイミング(位相)を変えることができました。
タイマの使い方としてはこの他にも、PWM出力や、外部からの高速クロック信号をカウントすることもできますので、一部紹介したいと思います。
PWMモード出力
アップカウント時、PWMモード1ではカウント数がCCRより小さいときにHigh,そうでないときにLowとしています。PWMモード2はその反転出力です。
ダウンカウント時、PWMモード1ではカウント数がCCRより大きいときにLow,そうでないときにHighとしています。PWMモード2はその反転出力です。
PWM出力の特徴は出力周期(サイクルカウント)は一定でCCRを変更することでON/OFFの比率(デューティ比)を変えることができることです。
設定は出力比較モードとほぼ同じで、TIM_OCxInit関数の第2引数のメンバのうちTIM_OCModeをTIM_ OCMode_PWM1かTIM_ OCMode_PWM2に指定するだけです。出力は出力比較モードのTIM_OCModeにTIM_OCMode_Toggleを指定したときの2倍の周波数になることに注意してください。
外部高速クロックカウント
タイマの応用として外部の高速クロックをカウントすることができるので紹介しましょう。
外部クロックモード
STM32のタイマには外部パルスを計測する機能があります。ロボット等のアクチュエータには位置、速度を検知するセンサとしてエンコーダがよく使われるのですが、外部パルスをカウントするには外部クロックモードとしてタイマを使用します。外部クロックモードを使用するには、タイマ初期化(TimeBaseInit関数実行)直後にTIM_TIxExternalClockConfig()関数を実行して設定します。
外部クロックモード関数実行例:
TIM_TIxExternalClockConfig(TIM3, TIM_TIxExternalCLKSource_TI1, TIM_ICPolarity_Rising, 0xf);
関数の第1引数は設定対象タイマTIM3など指定します。
第2引数は入力クロックパルス対象を指定します。
■ CH1のピン入力であるTI1のエッジを読み取るTIM_TIxExternalCLK1Source_TI1
■ CH2のピン入力であるTI2のエッジを読み取るTIM_TIxExternalCLK1Source_TI2
■ TI1とTI2のエッジ両方を読み取るTIM_TIxExternalCLK1Source_TI1ED
第3引数はTI1/TI2が検出するエッジを以下から指定
■ 立ち上がりエッジ検出のTIM_ICPolarity_Rising(極性はそのまま)
■ 立ち下がりエッジ検出のTIM_ICPolarity_Falling(極性は反転)
第4引数は以下に示したフィルタの種類を指定します。高速なエンコーダパルスを読み取る際のチャタリングなどのノイズを防止するためです。特別に高速なパルスを読み取るのでない場合はサンプリングの一番大きな"f"を指定しておいてよいのではないでしょうか。実際に動作させてみて調整するのがいいかと思います。
① GPIOおよびTIM4にクロックを供給
TIM3のCH1(PA6)を使用する例ではGPIOポートAはAPB2に接続、TIM3はAPB1に接続しているのでそれぞれのペリフェラルにクロックを供給します。
② GPIO初期化
外部入力の仕様に合わせて設定。ここでは無電圧接点入力のパルスを使用するためプルアップ入力としています。
③ TIM初期化
外部クロックのカウントではTIM3の初期化は省略できそうな気もしますが実行しておきましょう。
④⑤ TIMを外部クロックモードに設定しカウント開始
TIM3を外部クロックモードで使用するためにTIM_TIxExternalClockConfig関数を実行します。
フィルターはサンプリング回数が大きなものほど鈍くなりノイズに強いので 問題なければ"f"でいいと思います。
⑥ カウント値取得
入力パルスはアプリケーションプログラムの任意のタイミングで取得します。
エンコーダインターフェースモード
STM32タイマには外部信号として入力されたインクリメンタルエンコーダなどの高速パルスをタイマ機能だけでカウントすることができます。この場合はエンコーダインターフェースモードとしてタイマを使用します。エンコーダインターフェースモードを使用するには、タイマ初期化(TimeBaseInit関数実行)直後にTIM_EncoderInterfaceConfig関数を実行して設定します。
関数実行例:
TIM_EncoderInterfaceConfig(TIM4,TIM_EncoderMode_TI1,TIM_ICPolarity_Falling,TIM_ICPolarity_Rising);
関数の第1引数は設定対象タイマTIM4など指定します。
第2引数は
■ CH1のピン入力であるTI1のエッジを読み取るTIM_EncoderMode_TI1
■ CH2のピン入力であるTI2のエッジを読み取るTIM_EncoderMode_TI2
■ TI1とTI2の両方のエッジを読み取るTIM_EncoderMode_TI12
第3引数はTI1がエッジを検出したときのエッジ極性を以下から選択指定
■ エッジ検出のTIM_ICPolarity_Rising(極性そのまま)
■ エッジ検出のTIM_ICPolarity_Falling(極性反転)
第4引数はTI2がエッジを検出したときのエッジ極性を TI1と同様に選択指定
エンコーダインターフェースモードでは以下のパターンでカウントが行われます。
エッジの極性を反転に指定するとカウント方向が反転します。
エンコーダインターフェースモードではエンコーダのパルスエッジをカウントしますのでTI1またはTI2のみのエッジカウンタの場合は2逓倍、TI1およびTI2のエッジカウンタの場合は4逓倍の分解能になります。
① GPIOおよびTIM4にクロックを供給
GPIOポートDはAPB2に接続、TIM4はAPB1に接続しているのでそれぞれのペリフェラルにクロックを供給します。
② GPIO初期化後リマップ実行
必要に応じて、GPIO初期化後リマップを実行します。
③ TIM4の初期化
外部クロックのカウントではTIM4の初期化は省略できそうな気もしますが実行しておきましょう。
④ TIM4をエンコーダフェースモードに設定
TIM4をエンコーダインターフェースモードで使用するためにTIM_EncoderInterfaceConfig関数を実行します。
TI1とTI2の両方のエッジを検出するモードではカウント数が2倍になるためパルス分解能は4倍になります。いずれのモードでもTI1とTI2の位相によりアップカウント、ダウンカウントが切り替わります。
⑤ TIM4カウント開始
最後にTIM_Cmd関数を実行し、カウントを開始します。
⑥ カウント値取得
TIM4のカウンタ値はプログラム内で任意のタイミングで取得します。