Arduino Nano Every (ATmega4809) PWM周波数変更メモ

## はじめに
色々調べた結果の自分用メモ。
人が見てもわかりやすいようにする。

## 使えるピン
|Pin|Timer|CMPn|
|---|---|---|
|D9 | A0 | 0 |
|D10| A0 | 1 |
|D5 | A0 | 2 |
|D6 | B0 | - |
|D3 | B1 | - |

## Timerの特徴
- Timer A
  - 周波数を変更すると、Delay、Millis、Microsが狂う
    - 厳密には上記はTCB3で制御されているが、TCA0を参照しているため影響を受ける
- 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

### 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 を参照

### 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比を入れたい場合は次の項目に進む

### 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内で随時変更可
- pin出力開始
  - TCA0.SINGLE.CTRLB
    - 下位8bitで波形モード選択
      - PWMを作るならデフォルトの0x3をそのまま使用する
    - 上位8bitのうちの下位3bitでピンの有効化を行う
      - 0x40 D5
      - 0x20 D10
      - 0x10 D9
    - 厳密には有効化というよりはピン操作のオーバーライド
    - 詳細はデータシートの20.5.2 Control B - Normal Mode を参照
  - pinMode(pin, OUTPUT/INPUT)
    - CTRLBでピン有効化した場合は、この時点でPWM出力が始まる

### 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）
      - カウンター分解能調整による周波数調整の仕方が 

### 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を上限とした数値で指定する
- pin出力開始
  - TCB0.CTRLB
    - 下位4bitで波形モード選択
      - PWMを作るならデフォルトの0x3（0b111）をそのまま使用する
    - 上位4bitのうちの下位1bitでピンの有効化を行う
      - ```TCB0.CTRLB = 0b10111;```
    - 厳密には有効化というよりはピン操作のオーバーライド
    - 詳細はデータシートの21.5.2 Control B を参照
    - CTRLBでピン有効化した場合は、この時点でPWM出力が始まる
      - pinModeはいらない

### 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() {
}

```

## 参考文献
https://ww1.microchip.com/downloads/en/DeviceDoc/ATmega4808-09-DataSheet-DS40002173B.pdf

https://qiita.com/ShunHattori/items/1085de3d283b68683f9b

https://forum.arduino.cc/t/nano-every-pwm-frequency/602016/14

https://rtmrw.parallel.jp/laboratory6/lab-report-152/lab-152.html