はじめに
色々調べた結果の自分用メモ。
人が見てもわかりやすいようにする。
使えるピン
Pin | Timer | CMPn |
---|---|---|
D9 | A0 | 0 |
D10 | A0 | 1 |
D5 | A0 | 2 |
D6 | B0 | - |
D3 | B1 | - |
Timerの特徴
- Timer A
- 周波数を変更すると、Delay、Millis、Microsが狂う
- 厳密には上記はTCB3で制御されているが、TCA0を参照しているため影響を受ける
- 周波数を変更すると、Delay、Millis、Microsが狂う
- Timer B
- 周波数に柔軟性がない
- Aとは制御の仕方がちょっと違う
ざっくりとした使い方
- 決められた択から周波数を選ぶ形でよい場合
- 周波数だけ高速化して、analogWriteで動かす
- 同上、だがMillisとかが狂うと困る場合
- 周波数の択は狭まるが、Timer B でやる
- 周波数を自分で作りたい
- 細かいところまで調整する
- ただしanalogWriteだとDuty比の設定範囲に制限がでてしまうので、違う動かし方を使う
- 入れたいDuty比が制限範囲内に収まるならanalogWriteも可
Timer A (TCA0)
Split Modeという分割モード(16bitの枠を8bit x2にするモード)もあるが、デフォルトのNormal Modeの話で進める。
周波数の計算式は以下のとおり。
fPWM_SS [Hz] = fCLK_PER / N(PER+1)
全部デフォルトだと下記のとおり。
fCLK_PER = クロック = 16MHz
N = TCAプリスケーラー = 64
PER = カウンター分解能 = 255
クロックジェネレータの選択
特別な事情がなければ変更不要。後の計算で使うので把握しておくだけ。
- CLKCTRL.MCLKCTRLA
- CPUクロック(デフォルト)
- 弄ってなければ 16MHz。弄ったら20MHz
- 内蔵水晶発振子
- 32 KHz
- 外部水晶発振子
- 32.768 KHz
- CPUクロック(デフォルト)
TCA 周波数選択(プリスケーラー選択)
択から周波数を選ぶだけでよいなら、これだけ変更すればOK
- TCA0_SINGLE_CTRLA
- 0b1011(デフォルト)
- DIV64, enable
- = 16,000,000 / (64 * (255 + 1)
- = 976 Hz
- 一番速いDIV1の場合で 62,500 Hz
- 最下位ビットは有効フラグなので1にしておく
- 以後のビットからの数値
- 詳細はデータシートの 20.5.1 Control A を参照
- 0b1011(デフォルト)
TCA 周波数カスタマイズ(カウンター分解能調整)
択からの選択では物足りない場合に使用する。
analogWriteにも反映される。
- TCA0.SINGLE.PER
- 255(デフォルト)
- 16bitなので、上限は 65535
- 例えば 25000 Hz を作りたい場合は、DIV4の31250Hzをベースに以下の計算で作成する
- 倍率 = 元になる周波数 / 作りたい周波数
- 31250 / 25000 = 1.25
- 255 * 1.25 = 318.75
- ≓ 319
TCA Duty比の制限範囲を考える
- analogWriteで入力できる値は255まで
- 分解能調整でPERを255を超える値にした場合でも、変わらず255まで
- 255を超えた分のDuty比を指定できなくなる
- 例えば25000Hzを作るために 319 に変更した場合、analogWriteで設定できるDuty比の上限は以下のとおり
- 255 / 319 ≓ 80%
- この枠内を超えるDuty比を入れたい場合は次の項目に進む
- 分解能調整でPERを255を超える値にした場合でも、変わらず255まで
TCA analogWriteとは違う形でDuty比設定&出力
- Duty比設定
- TCA0.SINGLE.CMPnBUF
- CMP n BUF のnの部分は冒頭の表を参照
- TCA0.SINGLE.PERを上限とした数値で指定する
- 倍率指定で入れてOK。50%を指定するならこんな感じ
TCA0.SINGLE.CMP0BUF = TCA0.SINGLE.PER * 0.5;
- loop内で随時変更可
- TCA0.SINGLE.CMPnBUF
- pin出力開始
- TCA0.SINGLE.CTRLB
- 下位8bitで波形モード選択
- PWMを作るならデフォルトの0x3をそのまま使用する
- 上位8bitのうちの下位3bitでピンの有効化を行う
- 0x40 D5
- 0x20 D10
- 0x10 D9
- 厳密には有効化というよりはピン操作のオーバーライド
- 詳細はデータシートの20.5.2 Control B - Normal Mode を参照
- 下位8bitで波形モード選択
- pinMode(pin, OUTPUT/INPUT)
- CTRLBでピン有効化した場合は、この時点でPWM出力が始まる
- TCA0.SINGLE.CTRLB
TCA サンプルコード(pin9)
void setup() {
Serial.begin(9600);
delay(100);
////TCAプリスケーラーを64(0b1011)から2(0b11)に ⇒ fPWM_SS 16000000 / (2 * 256) = 31250 Hz
TCA0.SINGLE.CTRLA = 0b011;
//目標値の25000 Hz にするためには、分解能(TOP)を調整する
// 31250 / 25000 = 1.25
// 255 * 1.25 = 318.75 ≓ 319
TCA0.SINGLE.PER = 319;
//Duty初期値設定(50%)
TCA0.SINGLE.CMP0BUF = 158;
//PWM出力設定(これて常時WriteされるためloopでのWriteは不要)
//処理中にDutyを変更したい場合はCMPnBUFを 0~PER の範囲で変更する
TCA0.SINGLE.CTRLB = 0x13; //Bit4 = CMP0 = pin9
pinMode(9, OUTPUT);
}
void loop() {
}
Timer B (TCB0 / TCB1)
TCAとの違い
- 周波数指定の範囲が狭い
- クロック分解能(CLK_PER)の選択肢が以下の3つのみ
- DIV1
- DIV2
- TCA0と同じ値
- ピンの指定の仕方が異なる
- TCAではTCA0のCMPnBUFの数値だったが、TCBではTCB0かTCB1かで指定
- 続くレジスタ名はTCB0で記載する
- bitモード選択がない
- 元々使ってないからいいとして、レジスタの指定の仕方が異なる
- TCBではSINGLE/SPLITがいらないので名前が短い
- 生成できる波形の種類が少ない
- 代わりに他の機能が設けられている
- いずれにしても、PWMが欲しい場合には気にする必要なし
- カウンター分解能が低い
- TCAの16bit(65535)に対し、こちらは8bit(255)
- カウンター分解能調整による周波数調整の仕方が
- TCAの16bit(65535)に対し、こちらは8bit(255)
- クロック分解能(CLK_PER)の選択肢が以下の3つのみ
TCB 周波数選択(周波数の取得元選択)
択から周波数を選ぶだけでよいなら、これだけ変更すればOK。
- TCB0_CTRLA
- 先述のとおり以下の三択
- DIV1
- DIV2
- TCA0_SINGLE_CTRLA で指定したのと同じ値
- 最下位ビットは有効フラグなので1にしておく
- 詳細はデータシートの 21.5.1 Control A を参照
- 先述のとおり以下の三択
TCB 周波数カスタマイズ(カウンター分解能調整)
択からの選択では物足りない場合に使用する。
analogWriteには反映されない。
- TCB0.CCMPL
- 255(デフォルト)
- 8bitなので、上限は 255
- 倍率 = 元になる周波数 / 作りたい周波数
- デフォルトが上限値なので、倍率調整は下げる方(周波数が速くなるほう)しかできない
TCB analogWriteとは違う形でDuty比設定&出力
- Duty比設定
- TCB0.CCMPH
- TCB0.CCMPLを上限とした数値で指定する
- TCB0.CCMPH
- pin出力開始
- TCB0.CTRLB
- 下位4bitで波形モード選択
- PWMを作るならデフォルトの0x3(0b111)をそのまま使用する
- 上位4bitのうちの下位1bitでピンの有効化を行う
TCB0.CTRLB = 0b10111;
- 厳密には有効化というよりはピン操作のオーバーライド
- 詳細はデータシートの21.5.2 Control B を参照
- CTRLBでピン有効化した場合は、この時点でPWM出力が始まる
- pinModeはいらない
- 下位4bitで波形モード選択
- TCB0.CTRLB
TCB サンプルコード(pin6)
void setup() {
Serial.begin(9600);
delay(100);
//TCB0周波数を CLK_PER/2(31250Hz) に
TCB0.CTRLA = 0b11;
//目標値を40000 Hzとして、分解能(TOP)を調整する
// 31250 / 40000 = 0.78125
// 255 * 0.78 = 199
TCB0.CCMPL = 199;
//Duty初期値設定(50%)
TCB0.CCMPH = 100;
//PWM出力設定(これて常時WriteされるためloopでのWriteは不要)
//処理中にDutyを変更したい場合はCMPnBUFを 0~PER の範囲で変更する
TCB0.CTRLB = 0b10111;
//pinModeはいらない
}
void loop() {
}
参考文献