Tinkercad Circuits で Arduino 入門5～PWM出力～

# 概要
第5回目です。[前回](https://qiita.com/code0327/items/35b2c0a7532e094ddd8b)は、Arduino（アルディーノ）の **デジタル入力機能** について勉強しました。今回は、**PWM出力**（疑似アナログ出力）について、Tinkercad Circuit（Arduino＆電子回路シミュレータ）を利用して勉強していきます。

今回のArduinoプログラムには、新しい要素として **変数** や **繰り返し** が登場します。

- [Tinkercad](https://www.tinkercad.com/) @ Autodesk 

# アナログ出力とは

**デジタル出力**は、[第2回](https://qiita.com/code0327/items/b2922d7f325fa1b1de30) で学習したように「$1$（=**HIGH**）」または「$0$（=**LOW**）」の **2値** に対応する**電圧を出力**する機能でした。Arudino UNO の場合は「<font color='red'>$1$（**HIGH**）が $5\mathrm{V}$ 電圧</font>」に、「<font color='blue'>$0$（**LOW**）が$0\mathrm{V}$（GNDレベル）電圧</font>」に対応していました。

ちなみに、[RaspberryPi](https://www.switch-science.com/catalog/5681/) や [mbed](https://www.switch-science.com/catalog/250/)、[Arduino Due](https://www.switch-science.com/catalog/1096/) のデジタル入出力（GPIO：General-Purpose Input/Output）は「<font color='red'>$1$（**HIGH**）が $3.3\mathrm{V}$ 電圧</font>」に、「<font color='blue'>$0$（**LOW**）が $0\mathrm{V}$（GNDレベル）電圧</font>」に対応します。電子工作用（ホビー用途）のマイコンの多くでは、だいたいは $5\mathrm{V}$ 系か、$3.3\mathrm{V}$ 系となっています。一方で、$1.8\mathrm{V}$ 系を採用している低消費電力タイプのマイコンも存在します。これらのデジタル入出力に用いる電圧レベルを **ロジックレベル** といいます。

- ロジックレベルが異なるモジュール（マイコンやセンサなど）を組み合わせる場合、レベルの変換のための回路（**ロジックレベルコンバータ** や **レベルシフタ** とよばれます）が、必要になります。
  - [ロジックレベル変換モジュール](https://www.switch-science.com/catalog/list/224/) @ スイッチサイエンス

さて、デジタル出力に対して **アナログ出力** とは、**一定の範囲内で連続した電圧を出力できる機能** になります。例えば、$0\mathrm{V}$ から $5\mathrm{V}$ の範囲内で、$2.3\mathrm{V}$ や $4.5\mathrm{V}$、$0.2\mathrm{V}$ などの任意の電圧を出力する機能になります。アナログ出力 を使えば、LED に対して 点灯／消灯 だけではなく、調光（明るさの調節）ができるようになります。

とても便利な機能なのですが、**<font color='red'>一般的なマイコン（Arduino、RaspberryPi、mbed、PICなど）には、通常、この「アナログ出力機能」は備わっていません</font>**。

# PWM出力（疑似アナログ出力）

**PWM**とは、**Pulse Width Modulation** の略称で、日本語では「**パルス幅変調**」と呼ばれます。これを利用することで、**<font color='red'>疑似的なアナログ出力が可能</font>**になります。

次に示すのは、周期が $1000 \mu\mathrm{s}$、デューティ比が $60 \%$ の **PWM信号** です。
![2020-05-01_19h43_46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152805/de33d11a-77cd-3c3e-6c88-e7e4d4ee6182.png)

**HIGH**（=$5\mathrm{V}$）と **LOW**（=$0\mathrm{V}$）の **2値の出力** が短い時間で切り替わっていることが分かります。ただし、ランダムに切り替わるわけではなく、<font color='red'>規則性をもって **HIGH** と **LOW** が切り替わっています</font>。具体的には、**HIGH出力の時間** $T_{H}$ が $600 \mu\mathrm{s}$、**LOW出力の時間** $T_{L}$ が $400 \mu\mathrm{s}$ で繰り返されています。

このとき、次式で計算される値を <font color='red'>**デューティ比** $D$</font> といいます。

$$D = \frac{T_{H}}{T_{H}+T_{L}}\times 100=\frac{600\times10^{-6}}{600\times10^{-6}+400\times10^{-6}} = 60 \%$$

また、$T_{H}+T_{L} = 600\mu + 400\mu = 1000\mu\mathrm{s}$（$=1\mathrm{ms}$）が**周期** $T$、その逆数が **周波数** $f = 1/T = 1000\mathrm{Hz}$（$=1\,\mathrm{kHz}$）になります。

ところで、上記の PWM出力 について**平均電圧**を計算してみると、$5\mathrm{V} \times 60\%  =3\mathrm{V} $ になることが分かります。このことから、<font color='red'>デューティ比 $60\%$ のPWM出力は、**疑似的な** $3\mathrm{V}$ **のアナログ出力**として扱うことができます</font>。実際に、この PWM出力 を LED に与えると、**（人間の目には）$3V$ を与えたときに相当する明るさで点灯しているように見えます**。

ただし、注意しなければならないのは、**PWM出力の周波数** です。もし、周波数が $0.1\mathrm{Hz}$ だと、周期は $T=1/f=1/0.1= 10\,\mathrm{s}$ となり、もはや $6$ 秒点灯と $4$ 秒消灯 の単なる繰り返しです。

なお、$D=0\%$ のとき、PWM出力は **デジタルLOW出力**（=$0\mathrm{V}$）と同じです。また、$D=100\%$ のときは、**デジタルHIGH出力**（=$5\mathrm{V}$）と同じです。

# ArduinoによるPWM出力（方法1）

①ポート出力を**HIGH**に設定、②$T_{H}$ 秒だけ待機、③ポート出力を**LOW**に設定、④$T_{L}$ 秒だけ待機、という処理を繰り返す方法で、Arduino から PWM信号 を出力してみます。<br>![2020-05-01_23h12_54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152805/2c7f8216-32c1-e869-aa18-f1db21616126.png)

通常のデジタル出力と比較するために、**<font color='red'>デジタル8番ポート（D8）からは固定で HIGH を出力して</font>**、**<font color='blue'>デジタル6番ポート（D6）からはデューティ比 $30\%$ のPWM を出力して</font>**、それぞれで、LED を光らせてみましょう。

[Tinkercad](https://www.tinkercad.com/) にログインして、次のように回路を構成します。回路を構成する際は、LED の**極性**に注意してください。また、**電流制限抵抗**には $330\Omega$ を使用してください。

次にコードエディタ（ショートカットキーは **[E]** ）を起動し、ブロックモードでロジックを構成して、シミュレーションを実行（ショートカットキーは **[S]** ）してみましょう。ロジックでは「$0.3\mathrm{ms}$（$=300\mu \mathrm{s}$）間の**HIGH出力**」と「$0.7\mathrm{ms}$間の**LOW出力**」を交互に与えるために、無限ループを構成しています。<br>![2020-05-01_23h07_39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152805/e1037473-c51f-f679-8bf3-c6ebf107f49a.png)

シミュレーションを実行してみると、少し分かりずらいですが、**PWM出力（デューティ比$30\%$）の6番ポートに接続している LED のほうが <font color='red'>やや暗い</font> こと** が確認できたと思います。

- 回路例は[こちら](https://www.tinkercad.com/things/0IeobjNPRiM)（Tinkercadに移動します）。

しかし、この方法では、PWM出力をつくる部分でプログラムがループしてしまうために（そこから先にプログラミングが進まなくなってしまうために）、他の処理（例えば、デジタル入力をチェックするなど）を組み合わせることが難しくなってしまいます。そのため、通常、この方法は使われません。

# ArduinoによるPWM出力（方法2）

Arduinoでは、`analogWrite(...)` という関数を使って、より簡単に（ループを構成せずに）**PWM出力（=疑似アナログ出力）**が可能です。ブロックプログラムの場合は、次のように「**ピン X を Y に設定**」というブロックを使用します。<br>![2020-05-01_23h52_59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152805/7f687588-36cf-cd47-7ad9-5561191ab312.png)

このブロックはデジタル出力ブロックに似ていますが、<font color='red'>高(HIGH)／低(LOW) でなはなく、**デューティ比（数値）を設定**します</font>。<br>![2020-05-01_23h14_42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152805/9bb2be38-bc3b-7713-89de-c74831d6bf4f.png)

ただし、設定する数値は、**<font color='red'>0 から 255 までの整数</font>** で与えます。**0** を設定するとデューティ比 $0\%$、**255** を設定するとデューティ比 $100\%$、**128**を設定するとデューティ比が約 $50\%$ の PWM信号が出力されます。

逆に、デューティ比 $30\%$ のPWM信号を出力したければ、$255\times 0.3 = 76.5$ なので、$76$ か $77$ を指定します（誤差の範疇なのでどちらでもOKです）。<br>![2020-05-03_12h02_46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152805/8a3ed9da-09d2-ac36-9d78-625dcd9678e5.png)

また「**ピン X を 0 に設定**」を実行すれば（デューティ比$0\%$になるので） PWM 出力は停止します。

このように便利な機能なのですが、`analogWrite(...)` による PWM出力 は「**3**、**5**、**6**、**10**、**11** 番のデジタル出力ポート（ピン）」でしか使用できないという制約があります。ボードには 「**～**」 が印字されています。<br>![2020-05-01_23h36_49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152805/66d4b822-3354-b804-c7b6-e606bf9aad97.png)

`analogWrite(...)` の機能を使って、さきほどと同じようにLEDを点灯させてみましょう。プログラミングを以下のように書き換えてシミュレーションをしてみてください。回路例は[こちら](https://www.tinkercad.com/things/cPlR2h21jQ3)（TinkerCadに移動します）。<br>![2020-05-01_23h41_23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152805/a0ed68a4-aa12-3b46-a5eb-413132d82185.png)

## 補足：オーバーフロー

`analogWrite(...)` つまり「**ピン X を XXX に設定**」で設定可能な数値は、「**0**」～「**255**」まで整数値（ $0$ を含めての $255$ なので、$256$ 段階＝$2$の$8$乗段階）になります。もし、$256$ 以上の値、**<font color='red'>例えば「$270$」とかをアナログ出力に与えると、それは $270-256$ で「$14$」を与えたことになってしまいます</font>**（$14$ になると PWM出力が約 $5\%$ となるので、LED出力は相当に暗いです）。このような現象を**オーバーフロー（桁あふれ）**といいます。詳しく知りたい場合は「`オーバーフロー 数値　コンピュータ`」などで検索してみてください。

## 補足：analogWrite(...) によるPWM出力の周波数

`analogWrite(...)` による PWM出力の**周波数は基本的に固定**です。5番と6番ポートは $980\mathrm{Hz}$、その他は $490\mathrm{Hz}$ になります。これは、[Tinkercad](https://www.tinkercad.com/) の **オシロスコープ**モジュール 使って確認することもできます。<br>![2020-05-02_11h07_00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152805/86603515-17e2-8dc1-e546-c15bb6c3996d.png)

9番ポートからデューティ比 $50\%$ で PWM 出力して、それをオシロスコープモジュールで観測してみます。シミュレーションを実行すると、次のように約 $500\mathrm{Hz}$（$490\mathrm{Hz}$）になっていることが確認できます。回路例は[こちら](https://www.tinkercad.com/things/2RwWmXYU2wY)（Tinkercadに移動します）。<br>![2020-05-02_11h14_49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152805/1f52dbaa-e088-2657-42b9-89cfb3e01a9a.png)

なお、周波数を変更することも可能ですが、初心者向けではありません。気になる方は以下が参考になると思います。

- [PWM周波数を”自由に”変更する](http://blog.kts.jp.net/arduino-pwm-change-freq/) @ デジモノ覚書
- [analogWrite( )](http://garretlab.web.fc2.com/arduino/inside/hardware/arduino/avr/cores/arduino/wiring_analog.c/analogWrite.html) @ Arduinoで遊ぶページ

# 演習10：電子ホタル（1匹）～準備～

次のような回路を構成し、PWM出力を利用して、LEDが **ゆっくりとした明暗の変化を繰り返す** ような電子ホタルをつくりたいと思います。<br>![2020-05-02_11h54_04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152805/de643891-938f-bbd4-f637-9a12b412d965.png)

まずは、作成例をコピーして動作を確認しましょう。[こちら](https://www.tinkercad.com/things/ay6h7hQEFtG) をクリックすると、サンプルに飛ぶので、その画面内の「**コピーして編集**」をクリックします。自分の領域に複製が作成されるので、シミュレーションを実行して動作を確認しましょう。

この「複製して動作確認まで」が**演習10**です。

# 演習11：電子ホタル（1匹）～プログラムの読解～

**演習10** で複製した回路のコードを確認してみましょう。プログラムには、新たな要素として「**変数**」と「**繰り返し**」が登場しています。しかし、**「繰り返し」のブロックの表記（和訳）**が残念なことになっていて、**<font color='red'>正直、英語に切り替えたほうが分かりやすい</font>**です。以下は、C言語での `while(a<=10){ ... }` に対応するブロックですが（高校生以上なら）英語のほうが分かりやすいですよね。<br>![2020-05-03_08h32_00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152805/ceb8b475-1713-5363-2a5a-08f4ee908df7.png)


言語は、Tinkercad の**ダッシュボードの最下部から変更可能**です。必要に応じて**英語**に切り替えてください（英語モードにしたとき、ブラウザの翻訳はオフにしておきましょう）。<br>![2020-05-02_12h20_33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152805/7d0d6045-18bb-935d-4d13-78b39360cdf9.png)

コードエディタを開いて**ブロック**あるいは**コード**を読み解いてみてください。また、次の**課題12**では**ゼロ**から作成してもらうので、眺めるだけではなくで、1ブロックずつ意味を考えながら読み解きましょう。

プログラム内のパラメータ（`100` や `15` などの数値）を変更して、動作がどのように変わるか予測・確認することも理解の助けになります。<br>![2020-05-02_11h48_17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152805/b16f63e3-af45-7291-84ff-2c156afef6f0.png)

ソースコードを、直接、見たほうが分かりやすいかもしれません。

```cpp:電子ホタル（1匹）.ino
int i = 0;

void setup() {
  pinMode(9, OUTPUT);
}

void loop() {
  i = 0;
  while (i < 255) {
    analogWrite(9, i);
    delay(100); // Wait for 100 millisecond(s)
    i = (i + 15);
  }
  while (i > 0) {
    analogWrite(9, i);
    delay(100); // Wait for 100 millisecond(s)
    i = (i - 15);
  }
}
```

## （別解）

上記のプログラムは、次式のように三角関数（$\sin$）を使ってもっとスマートに構成することができます。

$$ d =\big(\, \sin(2\pi\times\frac{i}{36}) + 1.0 \, \big) \times 127.5 $$ 

ここで、$i$ を $0,1,2,\cdots\,34,35$ と変えていけば、$d$ は $0$ から $255$ の範囲で次のように変化します。例えば、$i=9$ のとき、$d=(\sin(\frac{\pi}{2})+1)\times 127.5 = 255$ になります。<br>![ダウンロード.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152805/0eadc0ad-c654-0859-9d58-68318d2c58fc.png)

これを次のようにプログラム上に実装すれば、同様に電子ホタルが作成できます（ブロックでは $\pi$ が定数として利用できないので、$2\pi\simeq 6.28$ としています）。<br>![2020-05-03_09h16_29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152805/e42d7790-4228-c8c0-a750-2adb226df858.png)

# 演習12：電子ホタル（1匹）～作成～

**演習11**と同じものを、ゼロから（回路を含めて最初から）構成してみましょう。見本のコードを眺めて理解したつもりでも、実際に手を動かしてみると「？？」となることは多々あります。必ず実際に作成してみましょう。

なお、この課題では**変数**を利用する必要があります。次の手順で変数を組み込みます。

1. 変数のグループのなかにある「**新しい変数**」をクリックします。<br>![2020-05-02_18h09_48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152805/0287783b-596f-9e3f-8b2c-4348c4a919f4.png)
2. ダイアログが表示されるので、**変数名を入力**します。日本語は避けましょう。<br>
![2020-05-02_18h12_17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152805/156257e1-f648-db8c-cbcf-c4c57b1a4db9.png)
3. 作成した変数を削除したい場合は、右クリックして「**変数 x を削除**」を選択します。<br>![2020-05-02_18h13_49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152805/057518dc-a3e2-4ccc-fc1c-f12f7da18d65.png)

# 演習13：電子ホタル（2匹以上）～作成～

LED を **2つ以上** に増やし、なおかつ、それらの点灯タイミングがズレるようにしてみましょう。<br>![2020-05-03_10h24_51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152805/8dd33500-0a0c-5662-fe55-e5a69e559a34.png)

- 解答例は[こちら](https://www.tinkercad.com/things/dHkq5YghlSU)（TinkerCadに移動します）。
  - $\sin(\omega t) $ と $\sin(\omega t+\theta) $ の関係を利用して変数1つで対応する方法。
- 解答例（別解）は[こちら](https://www.tinkercad.com/things/8mmZFlU1LGg)（TinkerCadに移動します）。
  - 三角関数を使わずに変数4つで対応する方法（お勧めしない）。

- **<font color='blue'>3Hの学生さんは、最低でも 30分 は自分で粘ってから解答例を見るようにしてください。数分で諦めるのは早過ぎできす。また、解答例をみて理解したあとも、必ず自分で動かして同じようにロジックを構成してみましょう</font>**（「檸檬」という漢字が「読めること」と「書けること」は違うのと同じ理屈です）。

## 補足：LEDを直列接続して同時点灯させる回路

**演習13** とは直接的には関係ありませんが、**LEDを直列接続して同時点灯させる回路** を説明しておきます（[第3回](https://qiita.com/code0327/items/b2922d7f325fa1b1de30) で学習した 単体LED を点灯させる方法を理解している前提の説明です）。

直列接続した $2$ 個の LED を点灯させる場合、電流制限抵抗 $R$ は次のように決めます。

$$ R \ge \frac{V_{\mathrm{CC}}-2\times V_{F}}{I_F} \,\,[\Omega]$$ 

例えば、LEDの特性が $V_{F}=2.0\,\mathrm{V}$、$I_{F}=20\,\mathrm{mA}$ で、電源電圧$V_{\mathrm{CC}}=5\,\mathrm{V}$ であれば、$ R \ge (5-2\times 2)/0.02=50\,\Omega$（下記では余裕を持たせて $100\Omega$ としています）。<br>![2020-05-03_10h46_25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152805/b5675ef5-f6e3-6ea9-4d66-de2d6bc7f7e4.png)

また、直列接続した $n$ 個の LED を点灯させる場合、電流制限抵抗 $R$ は次のように決めます。なお、<font color='red'>電源電圧 $V_{\mathrm{CC}}$ は、少なくとも $nV_{F}$ 以上にする必要があります</font>。 

$$ R \ge \frac{V_{\mathrm{CC}}-nV_{F}}{I_F} \,\,[\Omega]$$ 

例えば、$V_{F}=2.0\,\mathrm{V}$、$I_{F}=20\,\mathrm{mA}$、$V_{\mathrm{CC}}=12\,\mathrm{V}$ であれば、$ R \ge (12-5\times 2)/0.02=100\,\Omega$（下記では余裕を持たせて $200\Omega$ としています）。<br>![2020-05-03_10h46_25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152805/d7e53371-5708-c0bc-95ff-9d7ef553b196.png)

ところで、Arduino の デジタル出力は $5\,\mathrm{V}$ なので上記のように、**5つものLEDを直接接続することはできません**。その場合、次のように **トランジスタ(NPN)** を使って回路を構成します。<br>![2020-05-03_11h16_30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152805/d6050d27-9db9-c70c-abd9-6eaca43f7b14.png)

なお、**ベース抵抗**（図内の $10k\Omega$ の抵抗）には、ちゃんとした設計法があるのですが、それについては省略します（電子回路の授業や、実験実習で習うハズです）。気になる人は「`トランジスタ　スイッチング　ベース抵抗　設計`」をキーワードにググってください。

- 回路例は[こちら](https://www.tinkercad.com/things/e8fzKcmhwHp)（TinkerCadに移動します）。

なお、デジタル $6$ 番ポートから **PWM出力** をさせていても、問題なく動作します。

さらに、次のように並列化することで、さらに多くの LED を点灯させることができます（電源の電流負荷が2倍になっていることに注意してください）。<br>![2020-05-03_11h48_10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152805/e9628971-47e1-be47-a56b-6063bf4c3648.png)

# EX演習2：クリスマスイルミネーション

トランジスタを使ったスイッチング回路も組み合わせて多数のLEDが点灯するクリスマスイルミネーションの回路とプログラムをつくってみましょう。

# 次回

- [Tinkercad Circuits で Arduino 入門6～アナログ入力～](https://qiita.com/code0327/items/d99ba47d149c6ce9997e)