ArduinoでPWMの周期に同期してAD変換する。

#はじめに
　導体をPWMで制御しているとき、電流を測定したい。(DCブラシモータの電流計測に使える?→知らん)
　→**PWMのON時間中にAD変換をしたい。**(シャント抵抗の電圧値を測定→電流計測)

　今回想定するような回路を以下に示す。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/558094/515fb9c9-6078-bc5c-7bfd-31d38aaf8ff6.png)
　右の回路はFETによる駆動回路とシャント抵抗による電流計測。
　右上の銅線まきまきは、導体。←この導体の電流や抵抗値が知りたい、制御したい。
　左の抵抗2つは、電源電圧測定用、導体の抵抗値や消費電力を測定したい場合必要。

まず結果
　PWMが1[kHz]、デューティ比0.05以上の場合、良好な結果が得られた。(Arduino Nanoを使用)
　位相/周波数PWM動作で、PWMは反転で出力がいい感じ
　電源電圧とアクチュエータの電流を同時には測定できませんでした。(PWMの周期ごとに交互に測定した{Arduinoの限界})


検索すべきワード
　Arduino PWM周波数変更　
　Arduino タイマ割り込み
　など
#なにをすれば実現できそうか
###1.　タイマでPWMを発生させる
　Arduino標準のPWM(analogWrite())の機能では、周波数の設定が機能がなく、どのみちタイマ割り込みの設定で、タイマを設定するため、**PWMはAVRの機能で実装**する。

###2.　タイマ割り込みを使う
　PWMを発生させているタイマを利用して、PWMがONになっているときに、AD変換を行う。

###3.　割り込み中にAD変換する
　今回は、Arduino標準のAD変換であるanalogRead()を利用する。
　　　　→ 分周を変更し、高速化してもよいが、最高の精度は得られないかも。

　このとき、割り込みで多くの時間を消費することになる。AD変換完了割込みを使うことで回避できるが、今回はほっとく。


#実験環境
###使用器具
- Arduino Nano
- シャント抵抗(カーボン抵抗1/4[W]) 
- 電流を計測したいもの
- [Nch MOSFET 2SK4017](http://akizukidenshi.com/catalog/g/gI-07597/)
- その他FETゲート電流制限抵抗など

###補足
　今回はArduino Nanoを使用しました。これにはATmega328pが搭載されており、Arduino Unoも同じMCUで構成されているため、同じプログラムが実行できます。(AVRの機能を利用するためMCUが変わると利用できない)
　Arduino Nanoは16[Mhz]だよ
 今回はPWMの出力に9番ピンをつかったよ
　

#具体的に方法の検討
##タイマは何を使うか→Timer1
　**ArduinoでPWMを利用できるPINは6本存在**しています。それらは3, 5, 6, 9, 10, 11ピンです。
　**今回はPWM出力ピンを9または10を前提に開設**します。以下に理由を書きます。

　Arduino Nanoに搭載されているATmega328pには、**タイマとよばれるカウンタが存在します。**
　これらはクロックごとに自動的に加算されていきます。このカウンタは1クロックに1加算ということも可能ですが、分周によって、2クロックごと、8クロックごとなど、加算の仕方を変更することができます。

　ATmega328pには**タイマが3つ(Timer0,Timer1,Timer2)搭載**されており、個別に数値を加算させていくことが可能です。また、**それぞれカウンタの上限値、分周を個別に設定**することが可能です。**それぞれのタイマが対応したピンにPWMを出力可能**です。

　各タイマとPWM出力ピンの関係、Arduino内で使用されている関数を以下の表に示します。

| タイマ      |ピン番号 |bit数[bit]|すでにArduinoで使用されている機能|
|:-----------------|:------------------|:------------------|---|
| Timer0     |5,　6|8|delay(),millis(),micros()|
| **Timer1**|**9,　10**|16|Servoライブラリ|
| Timer2     |3,　11|8|タイマ割込みライブラリ|

　ここでTimer0の設定を変更すると、delay()など、Arduinoで利用されている関数の時間がくるいます。
　Timer2でもいいけど、まあ今回はTimer1で説明します。

　これらの理由から今回は、設定を変更しても影響の少ない**Timer1を利用**してPWMを発生させます。
 　

##PWM周波数の決定
→今回は1[kHz]以下で、最小デューティ比とのバランス
###AD変換の実行時間について
　ATmega328pのAD変換機は1つ内臓されており、ArduinoのA0～A7までのピンの電圧を10[bit]で測定することができる。なお、同時に測定することはできない。(ADCが1つしか内臓されていないため)
　なお、あるピンを測定時、104[us]よりも後に、他のピンを変換することはできる。

　Arduino NanoのAD変換は、**変換開始から理論値で104[us]で完了**する。また、測定には104[us]の時間がかかるが、AD変換開始直後に、測定するピンの電圧を保持する機構が内蔵されているため、電圧を保持した後は、電圧が変換しても、AD変換開始直後の電圧値を測定できる。

　また、AD変換の電圧保持にはAD変換クロックの約2クロックの時間が必要であり、これは(1/16[MHz])×128[分周]×2[クロック]=**16[us]の時間、電圧が変化しなければ、イイ感じに電圧を測定できる。**（ハズ　　ここは詳しく知らない
　　　(AD変換クロックは、Arduinoでは標準で128分周されている)
　

###PWMとAD変換のタイミング
　PWMのON時に、AD変換をするために、Timer1により、PWMの発生とタイマ割り込みを行う。
　また、PWMには高速PWM動作や位相/周波数基準PWM動作など存在するが、**今回は位相/周波数基準PWM動作、出力を反転**で構成した。以下説明
　

####タイマの動作の種類
ここでは、比較AなどをPWMしきい値と表しています。
#####高速PWM動作
　高速PWM動作ではカウンタが加算されていき、TOP値になると0に戻る。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/558094/3bd725b5-d181-7d5b-ee1e-2b95812eac84.png)


#####位相/周波数基準PWM動作
　位相/周波数基準PWM動作ではカウンタが加算されていき、TOP値になると、減算されていく。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/558094/47769581-dbba-eaf7-b365-c21adc72324e.png)

#####なぜ位相/周波数基準PWM動作を選択するのか
　今回タイマ割り込みを利用するばあい、割り込み要求ができるのが、比較一致した場合か、TOP値と一致した場合です。

　ここで、**位相/周波数基準PWM動作の反転出力(黄)に注目すると、PWM出力がONのとき、かつON時間の1/2の時に、カウンタがTOP値と一致し、割り込みが発生**することが分かる。

　では、高速PWM動作ではどうなのかというと、高速PWM動作の非転出力では、カウンタがTOP値での割り込みの時、同時に出力が立ち上がり、電圧が上昇する。

　もしFETの立ち上がりが遅く、AD変換の電圧保持時、電圧が安定していない場合などを考え、位相/周波数基準PWM動作を選択した。
　　　→もしかしたら、そこまで考えなくてもいいのかもしれない。実際これをすると必要なON時間が2倍になる（ちょっと損かも）。

###デューティ比とPWM周波数の決定
　まず、必要なON時間を求め、その後最小デューティ比と、PWM周波数を求める。
　
　必要ON時間は32[us]である。
　→AD変換では開始後16[us]は、測定したい電圧である必要があり、位相/周波数基準PWM動作では、ON時間の半分しか使えないことがわかったため。
　
　最小デューティ比を決める。これはアクチュエータの動作にさほど影響を与えない程度に決定する。（物による)例えばデューティ比0.05(5[%])
　
　必要なON時間と最小デューティ比からPWM周波数を決定する。
　→必要なON時間32[us]、最小デューティ比0.05から、32[us]/0.05=640[us]→1562[Hz]以下となる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/558094/78eb03f1-a978-0d73-c503-d3af992618f4.png)

　もし、最小デューティ比を倍の0.1にすると、PWM周波数は約3[kHz]以下となる。

#実際にやっていく
　やること

- タイマの設定
- タイマ割り込みの設定
- AD変換



##タイマの設定
###指定した周波数でPWMを出力できるようにする
　今回は9番ピンをPWMの出力に割り当てる。
　Timer1のTOP値を自由に指定できるレジスタICR1(捕獲レジスタ)に設定する。

```cpp
  pinMode(9, OUTPUT);
```

```cpp
//Timer1　変更
TCCR1A &= B11111100;
TCCR1A |= B00000000;              //WGM11,10, TOP値ICR1:00  
```

位相/周波数基準PWM動作に設定する

```cpp
TCCR1B &= B11100111;
TCCR1B |= B00010000;              //位相,周波数基準  WGM13,12 位相/周波数基準:10
```
分周なしに設定

```cpp
TCCR1B &= B11111000;
TCCR1B |= B00000001;              //1分周:001 
```

PWMの周波数を設定する。

```cpp
top = (muc_frq / frq/2);        //マイコンの周波数/PWMの設定周波数/2(←この2は位相/周波数基準PWM動作であるため)
ICR1 = top;                     //レジスタに代入
```

デューティ比を設定する。

```cpp
OCR1A = (unsigned int)(top * duty);
```

ここでdutyは0～1

最後にPWMを出力させる(反転動作)

```cpp 
TCCR1A &= B00111111;
TCCR1A |= B11000000;              //PWM動作 pin9出力
```

この時点で9番ピンから指定したPWMが出力される。

##タイマ割り込みの設定　

```cpp
//カウンタがTOP値になったときこの関数が呼ばれる。　この関数はloopやsetuoではない、外に記述する。
ISR(TIMER1_CAPT_vect) {
    //AD変換
}
```

setup関数内にTimer1の捕獲割込みを有効化する。＋全割込み許可

```cpp
//タイマ1　捕獲割込み　有効
TIMSK1 &= B11011111;
TIMSK1 |= B00100000;
sei();                            //割込み　許可
``` 

#おわりに
　電流値を測定するには、シャント抵抗の電圧値と抵抗値から、電流を導出する。
　電源電圧を利用することで、導体の抵抗値がわかる。(ﾊｽﾞ


#参考サイト
- [analogWrite()](https://gar![uploading-0]()
retlab.web.fc2.com/arduino_reference/language/functions/analogio/analogWrite.html
)
- [Timer1で任意の周期の割り込みを発生させるためのレジスタ設定
](https://sites.google.com/site/arduinostart/registers)
- [ArduinoのTimerを初心者が1からなんとなくわかるためのメモ
](https://qiita.com/suzukinori/items/939cc9f49e535c4eadd7)