Arduinoで和音を出力する方法 その1

# 初めに
<font color="#00979c">**Arduino**</font>で音を出そうとしたとき、<font color="#ff8000">**Tone()**</font>関数を使うと思います。
しかし、この関数は1つの音しか出すことができません。
そこで数回に分けて、ライブラリ等を使用しないで複数の音を出力する方法を示したいと思います。

# 原理
<font color="#ff8000">**Tone()**</font>関数は、<font color="#00979c">**Arduino**</font>デバイスのタイマを使用し音を出しています。
タイマのカウント数を変更することで、オーバーフローする周期を変更しています。

>Arduinoでの実装の詳細はこちらを参考にしています
[Arduino 日本語リファレンス　Tone(pin,freaquency)](http://www.musashinodenpa.com/arduino/ref/index.php?f=0&pos=2484)
[Arduino 内部構造　tone()](https://garretlab.web.fc2.com/arduino/inside/hardware/arduino/avr/cores/arduino/Tone.cpp/tone.html)

周波数の計算方法

```math
Freaq[Hz]=\frac{f_{clk_{I/O}}}{2 \times Count\times prescalar}=\frac{16[MHz]}{2\times (0～255)\times (2,8,32,64,128,256,1024)}\\
```

実際には

```math

Count = OCRXA (X=0,1,2)\\
```
となります。

試しに、<font color="green">TIMER2</font>を使って<font color="#ff8000">**Tone()**</font>もどきを作ってみましょう


```C:tone
#define DIV_1 1 //分周無し
#define DIV_8 2 //8分周
#define DIV_32 3 //32分周
#define DIV_64 4 //64分周
#define DIV_128 5 //128分周
#define DIV_256 6 //256分周
#define DIV_1024 7 //1024分周
#define WGM21 0b10 //タイマ2 動作モード設定
#define TOIE2 0b10 //割り込み許可

uint8_t output_pin = 13;

void setup() {
  //初期化関数
  pinMode(output_pin, OUTPUT); //13を出力に設定

  //タイマの設定
  TCCR2A = WGM21;//CTCモード　比較一致OCR2A
  TCCR2B = DIV_128;
  OCR2A = 141;//440Hz
  TIMSK2 = TOIE2;

  sei();//割り込み許可
}

void loop() {
}

ISR(TIMER2_COMPA_vect) {
  //割り込み時に実行される関数
  digitalWrite(output_pin, !digitalRead(output_pin));
}
```

さて、こちらのコードを<font color="#00979c">**Arduino(UNO,MEGA)**</font>に書き込むと<font color="red">D13</font>ピンから矩形波が440Hz(A4:ラ)で出力されます。　　

オシロスコープがある方は、波形を確認してみてください。
ない方は、1kΩくらいの抵抗を付けてスピーカーにつなげてみてください。
絶対音感がある方ならそこで、音階がわかると思います。
ない方は、スマートフォンのチューニングアプリで確認してみてください。<font color="red">(おすすめ)<font>


###計算式から結果を検討する
式に今回の条件を代入すると、

```math
Freaq[Hz]=\frac{f_{clk_{I/O}}}
{2 \times Count\times prescalar}=\frac{16[MHz]}{2 \times (141+1)\times 128}=440.14[Hz] \\
```

となります。出力結果は計算通りですね。

:::note warn
注記
141に1を足しているのは、0もカウントとされるためです。
配列の1番目が[0]になるようなものです
:::

任意の周波数fを出力するOCR2A出したい場合は、式を変形して

```math
\begin{align}
Freaq[Hz]&=\frac{f_{clk_{I/O}}}
{2 \times Count\times (OCR2A+1)} \\

Freaq[Hz]\times (OCR2A+1)&=\frac{f_{clk_{I/O}}  }
{2 \times Count\times (OCR2A+1)}\times (OCR2A+1) \\

Freaq[Hz]\times (OCR2A+1)&=\frac{f_{clk_{I/O}}  }
{2 \times Count}\\

Freaq[Hz]\times (OCR2A+1) \div Freaq[Hz]&=\frac{f_{clk_{I/O}}  }
{2 \times Count}\div Freaq[Hz]\\

OCR2A+1&=\frac{f_{clk_{I/O}}  }
{2 \times Count \times Freaq[Hz]}\\

OCR2A+1&=\frac{f_{clk_{I/O}}  }
{2 \times Count \times Freaq[Hz]}-1\\
\end{align}

```

となります。

:::note info
余談
マークダウン書式の練習で細かく書いています。
:::

先ほどの、440Hzの時は

```math
\begin{align}

OCR2A&=\frac{f_{clk_{I/O}}  }
{2 \times Count \times Freaq[Hz]}-1\\

OCR2A&=\frac{{16 \times 10^6 }  }
{2 \times 128 \times 440[Hz]}-1\\

OCR2A&=142-1\\
&=141\\
\end{align}

```

となります。ちなみに、このタイマの使い方を<font color="red">CTCモード</font>といいます

>
>大変参考になるページ
>始める電子回路様
>http://startelc.com/AVR/Avr_100timrMemo.html
>　　

本記事では、AVRのレジスタの説明は行わない予定ですので他サイト様をご参照ください

:::note info
余談
タイマ割り込みで有名な
MsTimer2もこの方法で割り込み周期を計算しています。
:::

### ISR内

ISR(ベクタ名)は、各割り込みが起きたときに実行される関数です。

今回は、タイマ2のOCR2Aコンペアマッチなので、TIMER2_COMPA_vectとしています。


```C:tone詳細
digitalWrite(output_pin, !digitalRead(output_pin));
```
ここでは、D13の状態を確認しそれを反転(！)しています。
省略せずに書くと

```C:digital
int pin_state = digitalRead(output_pin);
pin_state = !pin_state;
digitalWrite(output_pin,pin_state);
```

となります。

# まとめ
その1では、<font color="#ff8000">**Tone()**</font>関数を自前で作ってみるところでおしまいにしようとおもいます。
OCR2Aや分周数を変更して、出力される周波数を変更するなど遊んでみてください。

その2では、和音の出力に入りたいと思います。


https://qiita.com/electronic1155work/items/dca619a3cd3d291936c7