HardwareTimer
名称
/icons/hr.icon
概要
このページでは、内蔵タイマーを制御する方法について説明します。 タイマがボード上でどのように動作するかは記述されていません。 詳細については、タイマーのリファレンスを参照してください。 Arduino STM32でタイマ資源利用を手軽に利用するために、HardwarerTimerクラスの定義済みインスタンス(タイマオブジェクト)Timer1、Timer2、Timer3、Timer4が用意されています(大容量ボードではさらにTimer5~Timer8が利用可能です)。Timer1、Timer8は高機能タイマ、Timer2~Timer5は汎用タイマ、Timer6、Timer7は基本タイマです。これらの高機能タイマ、汎用タイマ、基本タイマはHardwareTimerクラスでは全て同じタイマとして利用出来ます。
入門
最初に利用するタイマを選択する必要があります。ここでは定義済みのTimer1を使ってみましょう。
ここでTimer1はHardwareTimerクラスのインスタンス(オブジェクト)です。
もし、自分でタイマインスタンスを定義して利用したい場合は、次のように宣言します。
次の例ではグローバルなオブジェクトとして宣言しています。
code:sample1.ino
HardwareTimer MyTimer(1);
void setup() {
// Your setup code
}
void loop() {
// ...
}
プリスケーラ(分周値)とオーバーフローの設定
タイマはカウンタ、プリスケーラ(分周器)、オーバーフロー値(カウント上限値)を1つづつ持っています。
(さらに、4つのコンパレータ(比較器)を持っていますがこれについては後ほど解説します)。
プリスケーラはシステム・クロック(通常は72MHz)を分周することが出来ます。
タイマはその分周したパルスをカウントし、カウンタにその値を保持します。
カウンタの値はオーバーフロー値(カウント上限値)までカウントし、その値を超えると0に戻ります。
このプリスケーラとオーバーフロー値を設定することで、タイマは任意の周波数で任意の数までカウントすることが出来ます。
さらに、オーバーフロー値に達した時、割り込みを発生させることが出来ます。
このプリスケーラの設定、オーバーフロー値の設定は、
setPrescaleFactor()およびsetOverflow()関数を使用して行うことができます。
■ void setPrescaleFactor(uint32 factor)
タイマのプリスケール係数(分周値)を設定します。この値はシステムクロックの分周値となります。
ここで設定した新しい値は、次回カウンタがオーバーフローするまで有効になりません。HardwareTimer::refresh()を使用してカウンタを強制的にリセットすることができます。
引数
factor :1から65,536までの新しいプリスケール値を設定します
■ void setOverflow(uint16 val)
タイマーオーバーフロー(またはリロード)値を設定します。
新しい値は、次回カウンタがオーバーフローするまで有効になりません。HardwareTimer::refresh()を使用してカウンタを強制的にリセットすることができます。
引数
val :設定する新しいオーバーフロー値
利用例
code:sample2.ino
void setup() {
Timer1.setPrescaleFactor(7200); // システムクロック 72MHzを10kHzに分周
Timer1.setOverflow(2000); // 2000カウント(200ミリ秒)をオーバーフロー値に設定
}
void loop() {
// ...
}
上記の例では200ミリ秒でオーバーフロー値に達してカウンタが0になります。
同じ設定(時間を意識した設定)を行う、便利な関数としてsetPeriod()があります。
■ uint16 setPeriod(uint32 microseconds)
タイマーの周期をマイクロ秒単位で設定します。
プリスケーラおよびオーバーフロー値を設定して、指定されたマイクロ秒数に可能な限り近い周期でタイマリロードを生成します。
引数
microseconds :タイマーの希望期間、 これはゼロより大きくなければなりません
戻り値
新しいオーバーフロー値
setPeriod()関数は内部処理で、setPrescaleFactor()、setOverflow()を呼び出してプリスケーラおよびオーバーフロー値を設定しています。
利用例
code:sample3.ino
void setup() {
// 200ミリ秒の繰り返しカウントを行うタイマ設定
Timer1.setPeriod(200 * 1000);
}
void loop() {
// ...
}
タイマ割り込みの使用
タイマ割り込みとは、タイマのカウンタが指定した条件に達した場合に、あらかじめ登録した関数を呼び出す機能です。
この指定した条件には次のものがあります。
1. カウンタがオーバーフロー値に達して0に戻った(オーバーフロー発生)
2. カウンタがコンパレータ(比較器:チャンネル1~4の4つあります)の設定値と同じになった
上記は同時に指定可能ですので、1つのタイマで5つの割込み関数の登録できます。
次に上記1、2の割り込みの使い方を説明します。
1. オーバーフロー発生でのタイマ割り込み
タイマ割り込み使用の基本は次の手順となります。
タイマーを一時停止します(Timer1.pause())。
プリスケーラとオーバーフローを設定します(Timer1.setPrescaleFactor()、Timer1.setOverflow()、またはTimer1.setPeriod())。
割り込みハンドラを登録します(Timer1.attachInterrupt( ))。
タイマーをリフレッシュします(Timer1.refresh())。
タイマーを再開します(Timer1.resume())。
タイマ割込みをLEDを点滅させる例
code:sample4.ino
void handle_timer() {
static uint8_t sw = LOW;
sw = !sw;
digitalWrite(LED_PIN, sw);
}
void setup() {
pinMode(LED_PIN, OUTPUT);
Timer1.pause(); // タイマー停止
Timer1.setPeriod(200 * 1000); // 200ミリ秒でオバーフロー
Timer1.attachInterrupt( // 割り込みハンドラの登録
TIMER_UPDATE_INTERRUPT, // 呼び出し条件は、カウンターオーバーフロー更新時
handle_timer // 呼び出す関数
);
Timer1.refresh(); // タイマ更新
Timer1.resume(); // タイマースタート
}
void loop() { }
上記のスケッチでは割り込みハンドラを登録attachInterrupt()の第1引数にTIMER_UPDATE_INTERRUPTを指定して割込み条件を、オーバーフロー発生時としています。第2引数に、 割込みで呼び出す関数として handle_timerを指定しています。
2. コンパレータ(チャンネル)と一致でのタイマ割り込み
タイマには4つのコンパレータ(比較器:チャンネル1~4)を持っています。そのコンパレータの値を一致した条件での割り込みを発生することが出来ます。タイマのカウンタが0 ~ オーバーフロー値の値を繰り返す間、カウンタの値がコンパレータの値と一致した場合に割り込みを発生することが出来ます。
この場合のタイマ割り込み使用の基本は次の手順となります。
タイマを一時停止します(Timer1.pause())。
プリスケーラとオーバーフローを設定します(Timer1.setPrescaleFactor()、Timer1.setOverflow()、またはTimer1.setPeriod())。
割り込みを処理するタイマチャネル(コンパレータ)を選択し、チャネルのモードをTIMER_OUTPUT_COMPAREに設定します(Timer1.setMode())。
タイマチャネルのコンペア値を適切に設定します(Timer1.setCompare())。
割り込みハンドラを登録します(Timer1.attachInterrupt( ))。
タイマーをリフレッシュします(Timer1.refresh())。
タイマーを再開します(Timer1.resume())。
タイマ割込みをLEDを点滅させる例
code:sample5.ino
void handle_timer() {
static uint8_t sw = LOW;
sw = !sw;
digitalWrite(LED_PIN, sw);
}
void setup() {
pinMode(LED_PIN, OUTPUT);
Timer1.pause(); // タイマー停止
Timer1.setPrescaleFactor(7200); // システムクロック 72MHzを10kHzに分周
Timer1.setOverflow(20000); // 20000カウント(2000ミリ秒)をオーバーフロー値に設定
Timer1.setMode( // チャンネル1の割り込み発生条件の設定
TIMER_CH1, // チャンネル1を指定
TIMER_OUTPUT_COMPARE // 比較モードを指定
);
Timer1.setCompare(TIMER_CH1,10000); // 10000カウント(1000ミリ秒)と比較
Timer1.attachInterrupt( // 割り込みハンドラの登録
TIMER_CH1, // チャンネル1の値で割り込み発生
handle_timer // 呼び出す関数
);
Timer1.refresh(); // タイマ更新
Timer1.resume(); // タイマースタート
}
void loop() {
}
上記の例では、タイマが2000ミリ秒周期(カウンタの値が0~20000まで繰り返し変化)する間に、カウンタの値がチャンネル1で指定したコンパレータの値10000(1000ミリ秒)になった時に割り込みを発生します。LEDは2秒間隔で点滅します。
レーシングカウンター:この例は、複数のタイマーを同時に使用する方法を示しています。
code:sample6.ino
int count3 = 0;
int count4 = 0;
void setup() {
pinMode(BOARD_BUTTON_PIN, INPUT_PULLUP); // ボタン用ピンの設定
// タイマ割り込みの設定
Timer3.setMode(TIMER_CH1, TIMER_OUTPUT_COMPARE);
Timer4.setMode(TIMER_CH1, TIMER_OUTPUT_COMPARE);
Timer3.pause();
Timer4.pause();
Timer3.setCount(0);
Timer4.setCount(0);
Timer3.setOverflow(30000);
Timer4.setOverflow(30000);
Timer3.setCompare(TIMER_CH1, 1000);
Timer4.setCompare(TIMER_CH1, 1000);
Timer3.attachInterrupt(TIMER_CH1, handler3);
Timer4.attachInterrupt(TIMER_CH1, handler4);
Timer3.refresh();
Timer4.refresh();
Timer3.resume();
Timer4.resume();
}
void loop() {
Serial.print("Count 3: ");
Serial.print(count3);
Serial.print("\t\tCount 4: ");
Serial.println(count4);
// ボタンを押すと、Timer4が中断します
for (int i = 0; i < 1000; i++) {
if (digitalRead(BOARD_BUTTON_PIN)) {
Timer4.pause();
} else {
Timer4.resume();
}
delay(1);
}
}
void handler3(void) {
count3++;
}
void handler4(void) {
count4++;
}
PWMの制御
タイマが持っているコンパレータ(比較器:チャンネル1~4)には、外部にパルス出力するためのGPIOピンが割り当てられています。このピンを使ってPWMの出力を行うことが出来ます。
STM32F103C8T6のTimer1のチャンネル1~4に割り当てられているGPIOピンは次の通りです。
(他のタイマのチャンネル1~4にも別のGPIOピンが割り当てられています)。
table:GPIOピン
タイマ チャンネル GPIOピン
Timer1 1 PA8
Timer1 2 PA9
Timer1 3 PA10
Timer1 4 PA11
以下にTimer1のチャンネル1を使ったPWM出力の例を示します。
code:sample7.ino
#define LED_PIN PA8 // TIMER1 1CH用出力ピン void setup() {
pinMode(LED_PIN, PWM); // LED_PINをPWMに設定
Timer1.pause(); // タイマー停止
Timer1.setPrescaleFactor(7200); // システムクロック 72MHzを10kHzに分周
Timer1.setOverflow(10000); // 周期を1秒に設定
Timer1.setMode(TIMER_CH1,TIMER_PWM);
Timer1.setCompare(TIMER_CH1,1000); // 0.1秒間点灯
Timer1.setCount(0); // カウンタを0に設定
Timer1.refresh(); // タイマ更新
Timer1.resume(); // タイマースタート
}
void loop() {
}
上記の例では、周期1Hz(1秒)で0.1秒間、PA8ピンの出力をHIGHにしてLEDを点灯し、のこり0.9秒間LOWにして消灯します。PWM制御のオンの時間幅(デューティ)は0.1秒、デューティ比としては10%となります。
パルス周期は、タイマ割り込みよ同様にプリスケーラとオーバーフローをsetPrescaleFactor()、setOverflow()を使用して設定しています。
だだし、PEM出力の場合はその後のsetMode()の第2引数のモードにTIMER_PWMを指定します。setCompare()ではPWM制御のオンの時間幅(デューティ)のカウント値を指定します。この時のカウント値は0~オーバーフロー値の間の数値を指定し、オーバーフロー値 × デューティ比(0~100%)が指定するカウント値となります。
サンプルではカウント値 = オーバーフロー値 10000 × 0.1 (10%) = 1000 となります。
なお、上記スケッチのチャンネルのモード設定と、コンパレータ値(デューティ)の指定は、
code:c
Timer1.setMode(TIMER_CH1,TIMER_PWM);
Timer1.setCompare(TIMER_CH1,1000); // 0.1秒間点灯
次の1行でも同じ設定となります。
code:c
pwmWrite(LED_PIN, 1000); // PWMパルス幅を0.1秒に設定
上記のpwmWrite()では、LED_PINで指定したピンに割り付けられているタイマのチャンネルをTIMER_PWMモードにして、コンパレータ値に1000を設定しています。
HardwareTimerクラスリファレンス
/icons/hr.icon
このセクションでは、HardwareTimerの関数の完全なリストを示します。
class HardwareTimer
16ビットタイマーのインタフェース
メンバー関数
コンストラクタ
■ HardwareTimer(uint8 timerNum)
新しいHardwareTimerインスタンスを構築します。
引数
timerNum :制御するタイマの番号(1~4)or TIMER_CH1~TIMER_CH4
一般メンバ関数
■ void pause(void)
設定に影響を与えずにカウンタを停止します。
■ void resume(void)
構成に影響を与えずに、一時停止したタイマーを再開します。
タイマーは、必要に応じてカウントを再開し、割り込みを起動します。
このメソッドの使用に関連する関数呼び出しオーバーヘッドがあるので、HardwareTimer::pause()と同時に使用することは、複数のタイマーを同じカウント値に整列させる堅牢な方法ではありません。
■ int32 getPrescaleFactor()
タイマーのプリスケール係数(分周値)を取得します。
戻り値
タイマープリスケーラ(分周値)、1〜65,536
■ void setPrescaleFactor(uint32 factor)
タイマーのプリスケール係数を設定します。
新しい値は、次回カウンタがオーバーフローするまで有効になりません。HardwareTimer::refresh()を使用してカウンタを強制的にリセットすることができます。
引数
factor :1から65,536までの新しいプリスケール値(分周値)を設定します。
■ uint16 getOverflow()
タイマーオーバーフロー値を取得します。
戻り値
タイマーオーバーフロー値
■ void setOverflow(uint16 val)
タイマーオーバーフロー(またはリロード)値を設定します。
新しい値は、次回カウンタがオーバーフローするまで有効になりません。HardwareTimer::refresh()を使用してカウンタを強制的にリセットすることができます。
引数
val:設定する新しいオーバーフロー値
■ uint16 getCount(void)
現在のタイマーカウントを取得します。
戻り値
タイマーの現在のカウント値
■ void setCount(uint16 val)
現在のタイマーカウントを設定します。
引数
val:設定する新しいカウント値。この値がタイマーのオーバーフロー値を超えると、オーバーフロー値に切り捨てられます。
■ uint16 setPeriod(uint32 microseconds)
タイマーの周期をマイクロ秒単位で設定します。
プリスケーラおよびオーバーフロー値を設定して、指定されたマイクロ秒数に可能な限り近い周期でタイマリロードを生成します。
引数
microseconds :タイマーの希望期間、 これはゼロより大きくなければなりません
戻り値
新しいオーバーフロー値
■ void setMode(int channel, timer_mode mode)
タイマーチャネルのモードを設定します。
引数
channel :タイマーチャンネル、TIMER_CH1 〜 TIMER_CH4
mode :モード(timer_mode列挙型の定数)
timer_mode列挙型は、タイマーチャネルの動作を設定するために使用します。
すべてのタイマーをすべてのモードで設定できるとは限りません。
次の設定値があります。
TIMER_DISABLED :タイマはカウントを停止し、チャネル割り込みは切り離され、状態変化は出力されません。
TIMER_PWM :PWM出力モード。初期化後のピンのデフォルトモードです。
TIMER_OUTPUT_COMPARE :タイマーは0からリロード値まで繰り返しカウントします。 カウンタ値がチャネル比較値の1つに達するたびに、対応する割り込みが発生します。
■ uint16 getCompare(int channel)
指定されたチャネルの比較値を取得します。
引数
channel :タイマーチャンネル、TIMER_CH1 〜 TIMER_CH4
戻り値
指定されたチャネルの比較値
■ void setCompare(int channel, uint16 compare)
指定されたチャネルの比較値を設定します。
引数
channel :タイマーチャンネル、TIMER_CH1 〜 TIMER_CH4
compare :比較値、このタイマーのオーバーフロー値より大きい場合は、オーバーフロー値に切り捨てられます。
■ void attachInterrupt(int channel, voidFuncPtr handler)
指定されたチャネルに割り込みハンドラを接続します。
この割り込みハンドラは、タイマのカウンタが所定のチャネル比較値に達すると呼び出されます。
引数
channel :タイマーチャンネル; TIMER_UPDATE_INTERRUPT、TIMER_CH1 〜 TIMER_CH4
handler :ハンドラ(関数のポインタ)
channelにTIMER_UPDATE_INTERRUPTを指定した場合、チャンネルの比較ではなくオーバーフロー発生が割り込みのタイミングとなります。
■ void detachInterrupt(int channel)
指定されたチャネルに接続されている割り込みハンドラがあれば、それを削除します。
ハンドラはこのタイマーによって呼び出されなくなります。
引数
channel :タイマーチャンネル;TIMER_UPDATE_INTERRUPT、TIMER_CH1 〜 TIMER_CH4
■ void refresh(void)
カウンタをリセットし、プリスケーラとオーバフローの値を更新してください。
これにより、アップカウントモード(デフォルト)でカウンタが0にリセットされます。 また、HardwareTimer::setPrescaleFactor()またはHardwareTimer::setOverflow()を使用して変更するように設定している場合は、タイマのプリスケーラとオーバーフローも更新されます。
■ void setMasterModeTrGo(uint32_t mode)
マスターモードのTRGO信号を設定します。
これらのビットは、マスタモードで送信する情報を、同期用のスレーブタイマ(TRGO)に選択することを可能にします。
引数
mode: タイマーモード選択(タイマー制御レジスタ2 (CR2) のMMS設定値)
TIMER_CR2_MMS_RESET :Reset(TIMER1_EGRレジスタのUGビットがトリガ出力(TRGO)として利用)
TIMER_CR2_MMS_ENABLE :Enable(カウンタ・イネーブル信号CNT_ENがトリガ出力(TRGO)をして選択)
TIMER_CR2_MMS_UPDATE :Update(更新イベントがトリガ出力(TRGO)をして選択)
TIMER_CR2_MMS_COMPARE_PULSE :Compare Paluse(比較一致でCC1Fフラグのセット条件発生時トリガ出力(TRGO))
TIMER_CR2_MMS_COMPARE_OC1REF :Compare OC1REF(OC1REF信号がトリガ出力(TRGO)として利用)
TIMER_CR2_MMS_COMPARE_OC2REF :Compare OC2REF(OC2REF信号がトリガ出力(TRGO)として利用)
TIMER_CR2_MMS_COMPARE_OC3REF :Compare OC3REF(OC3REF信号がトリガ出力(TRGO)として利用)
TIMER_CR2_MMS_COMPARE_OC4REF :Compare OC4REF(OC4REF信号がトリガ出力(TRGO)として利用)
エンコーダー用関数
■ uint8 getDirection()
方向を取得します。
■ void setEdgeCounting(uint32 counting)
エンコーダ・インタフェース・モード(スレーブ・モード)の選択を行います。
エンコーダが1つのチャンネル(TI1またはTI2)、または両方のチャンネル(TI1とTI2)のエッジをカウントするかどうかを設定します。
引数
counting :カウント条件設定(スレーブ・モード条件)
TIMER_SMCR_SMS_DISABLED :スレーブ・モード禁止、内部クロックがプリスケーラの入力となります
TIMER_SMCR_SMS_ENCODER1 :エンコーダ・モード1、T1のエッジ入力でカウント
TIMER_SMCR_SMS_ENCODER2 :エンコーダ・モード2、T2のエッジ入力でカウント
TIMER_SMCR_SMS_ENCODER3 :エンコーダ・モード3、T1、T2のエッジ入力でカウント
TIMER_SMCR_SMS_RESET :リセット・モード、トリガ入力(TRGI)でカウンタリセット
TIMER_SMCR_SMS_GATED :ゲート・モード、トリガ入力(TRGI)がHIGHの場合、カウンタのクロック
TIMER_SMCR_SMS_TRIGGER :トリガ・モード、トリガ入力(TRGI)の立上りエッジでカウンタスタート
TIMER_SMCR_SMS_EXTERNAL :外部クロックモード、選択された信号(TRGI)の立上りエッジがカウンタのクロック
本関数の本来の目的はエンコーダ利用のため、TIMER_SMCR_SMS_DISABLED、TIMER_SMCR_SMS_ENCODER1、TIMER_SMCR_SMS_ENCODER2、TIMER_SMCR_SMS_ENCODER3を設定します。
■ uint8 getEdgeCounting()
エンコーダが1つのチャンネル、または両方のチャンネルのエッジをカウントするかどうかの値を取得します。
■ void setPolarity()
カウントの極性を設定します。
デバイス参照
このHardwareTimerインスタンスの基礎となるlibmaple timer_devへのポインタを取得します。
■ timer_dev* c_dev(void)
■ void enableDMA(int channel)
入力チャンネルのDMA要求を有効にします。
引数
channel :入力チャンネル(1~4)
■ void disableDMA(int channel)
channel :入力チャンネル(1~4)
引数
channel
非推奨の関数
以下の機能は今のところ存在しますが、廃止され、将来のMaple IDEリリースで削除されます。 あなたは新しいプログラムでそれを使うべきではなく、上記の最新の機能を使うためにあなたのプログラムを変更するべきです。
以前のリリースのTimerModeタイプは、timer_modeに変更されました。 モードTIMER_OUTPUTCOMPAREは引き続き存在しますが、将来のリリースでは削除されます。 代わりにTIMER_OUTPUT_COMPAREを使用してください。
■ void setChannelMode(int channel, timer_mode mode)
setMode(channel, mode) を使って下さい。
■ void setChannel1Mode(timer_mode mode)
setMode(TIMER_CH1, mode)を使って下さい。
■ void setChannel2Mode(timer_mode mode)
setMode(TIMER_CH2, mode)を使って下さい。
■ void setChannel3Mode(timer_mode mode)
setMode(TIMER_CH3, mode)を使って下さい。
■ void setChannel4Mode(timer_mode mode)
setMode(TIMER_CH4, mode)を使って下さい。
■ uint16 getCompare1()
getCompare(TIMER_CH1, mode)を使って下さい。
■ uint16 getCompare2()
getCompare(TIMER_CH2, mode)を使って下さい。
■ uint16 getCompare3()
getCompare(TIMER_CH3, mode)を使って下さい。
■ uint16 getCompare4()
getCompare(TIMER_CH4, mode)を使って下さい。
■ void setCompare1(uint16 compare)
setCompare(TIMER_CH1, compare)を使って下さい。
■ void setCompare2(uint16 compare)
setCompare(TIMER_CH2, compare)を使って下さい。
■ void setCompare3(uint16 compare)
setCompare(TIMER_CH3, compare)を使って下さい。
■ void setCompare4(uint16 compare)
setCompare(TIMER_CH4, compare)を使って下さい。
■ void attachCompare1Interrupt(voidFuncPtr handler)
attachInterrupt(TIMER_CH1, handler)を使って下さい。
■ void attachCompare2Interrupt(voidFuncPtr handler)
attachInterrupt(TIMER_CH2, handler)を使って下さい。
■ void attachCompare3Interrupt(voidFuncPtr handler)
attachInterrupt(TIMER_CH3, handler)を使って下さい。
■ void attachCompare4Interrupt(voidFuncPtr handler)
attachInterrupt(TIMER_CH4, handler)を使って下さい。
■ void detachCompare1Interrupt(void)
detachInterrupt(TIMER_CH1) を使って下さい。
■ void detachCompare2Interrupt(void)
detachInterrupt(TIMER_CH2) を使って下さい。
■ void detachCompare3Interrupt(void)
detachInterrupt(TIMER_CH3) を使って下さい。
■ void detachCompare4Interrupt(void)
detachInterrupt(TIMER_CH4) を使って下さい。
■ void generateUpdate(void)
refresh()を使って下さい。
関連項目
このドキュメントはleafLabs, LLC.が執筆し、たま吉が翻訳・一部加筆修正したものです。
NAVER、ヤフオク等の営利目的の記事転用、リンク貼りは禁止です。