概要
第5回目です。前回は、Arduino(アルディーノ)の デジタル入力機能 について勉強しました。今回は、PWM出力(疑似アナログ出力)について、Tinkercad Circuit(Arduino&電子回路シミュレータ)を利用して勉強していきます。
今回のArduinoプログラムには、新しい要素として 変数 や 繰り返し が登場します。
- Tinkercad @ Autodesk
アナログ出力とは
デジタル出力は、第2回 で学習したように「$1$(=HIGH)」または「$0$(=LOW)」の 2値 に対応する電圧を出力する機能でした。Arudino UNO の場合は「$1$(HIGH)が $5\mathrm{V}$ 電圧」に、「$0$(LOW)が$0\mathrm{V}$(GNDレベル)電圧」に対応していました。
ちなみに、RaspberryPi や mbed、Arduino Due のデジタル入出力(GPIO:General-Purpose Input/Output)は「$1$(HIGH)が $3.3\mathrm{V}$ 電圧」に、「$0$(LOW)が $0\mathrm{V}$(GNDレベル)電圧」に対応します。電子工作用(ホビー用途)のマイコンの多くでは、だいたいは $5\mathrm{V}$ 系か、$3.3\mathrm{V}$ 系となっています。一方で、$1.8\mathrm{V}$ 系を採用している低消費電力タイプのマイコンも存在します。これらのデジタル入出力に用いる電圧レベルを ロジックレベル といいます。
- ロジックレベルが異なるモジュール(マイコンやセンサなど)を組み合わせる場合、レベルの変換のための回路(ロジックレベルコンバータ や レベルシフタ とよばれます)が、必要になります。
- ロジックレベル変換モジュール @ スイッチサイエンス
さて、デジタル出力に対して アナログ出力 とは、一定の範囲内で連続した電圧を出力できる機能 になります。例えば、$0\mathrm{V}$ から $5\mathrm{V}$ の範囲内で、$2.3\mathrm{V}$ や $4.5\mathrm{V}$、$0.2\mathrm{V}$ などの任意の電圧を出力する機能になります。アナログ出力 を使えば、LED に対して 点灯/消灯 だけではなく、調光(明るさの調節)ができるようになります。
とても便利な機能なのですが、一般的なマイコン(Arduino、RaspberryPi、mbed、PICなど)には、通常、この「アナログ出力機能」は備わっていません。
PWM出力(疑似アナログ出力)
PWMとは、Pulse Width Modulation の略称で、日本語では「パルス幅変調」と呼ばれます。これを利用することで、**疑似的なアナログ出力が可能**になります。
次に示すのは、周期が $1000 \mu\mathrm{s}$、デューティ比が $60 %$ の PWM信号 です。
HIGH(=$5\mathrm{V}$)と LOW(=$0\mathrm{V}$)の 2値の出力 が短い時間で切り替わっていることが分かります。ただし、ランダムに切り替わるわけではなく、規則性をもって HIGH と LOW が切り替わっています。具体的には、HIGH出力の時間 $T_{H}$ が $600 \mu\mathrm{s}$、LOW出力の時間 $T_{L}$ が $400 \mu\mathrm{s}$ で繰り返されています。
このとき、次式で計算される値を デューティ比 $D$ といいます。
$$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} $ になることが分かります。このことから、デューティ比 $60%$ のPWM出力は、疑似的な $3\mathrm{V}$ のアナログ出力として扱うことができます。実際に、この 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信号 を出力してみます。
通常のデジタル出力と比較するために、デジタル8番ポート(D8)からは固定で HIGH を出力して、デジタル6番ポート(D6)からはデューティ比 $30%$ のPWM を出力して、それぞれで、LED を光らせてみましょう。
Tinkercad にログインして、次のように回路を構成します。回路を構成する際は、LED の極性に注意してください。また、電流制限抵抗には $330\Omega$ を使用してください。
次にコードエディタ(ショートカットキーは [E] )を起動し、ブロックモードでロジックを構成して、シミュレーションを実行(ショートカットキーは [S] )してみましょう。ロジックでは「$0.3\mathrm{ms}$($=300\mu \mathrm{s}$)間のHIGH出力」と「$0.7\mathrm{ms}$間のLOW出力」を交互に与えるために、無限ループを構成しています。
シミュレーションを実行してみると、少し分かりずらいですが、PWM出力(デューティ比$30%$)の6番ポートに接続している LED のほうが やや暗い こと が確認できたと思います。
- 回路例はこちら(Tinkercadに移動します)。
しかし、この方法では、PWM出力をつくる部分でプログラムがループしてしまうために(そこから先にプログラミングが進まなくなってしまうために)、他の処理(例えば、デジタル入力をチェックするなど)を組み合わせることが難しくなってしまいます。そのため、通常、この方法は使われません。
ArduinoによるPWM出力(方法2)
Arduinoでは、analogWrite(...)
という関数を使って、より簡単に(ループを構成せずに)**PWM出力(=疑似アナログ出力)**が可能です。ブロックプログラムの場合は、次のように「ピン X を Y に設定」というブロックを使用します。
このブロックはデジタル出力ブロックに似ていますが、高(HIGH)/低(LOW) でなはなく、デューティ比(数値)を設定します。
ただし、設定する数値は、0 から 255 までの整数 で与えます。0 を設定するとデューティ比 $0%$、255 を設定するとデューティ比 $100%$、128を設定するとデューティ比が約 $50%$ の PWM信号が出力されます。
逆に、デューティ比 $30%$ のPWM信号を出力したければ、$255\times 0.3 = 76.5$ なので、$76$ か $77$ を指定します(誤差の範疇なのでどちらでもOKです)。
また「ピン X を 0 に設定」を実行すれば(デューティ比$0%$になるので) PWM 出力は停止します。
このように便利な機能なのですが、analogWrite(...)
による PWM出力 は「3、5、6、10、11 番のデジタル出力ポート(ピン)」でしか使用できないという制約があります。ボードには 「~」 が印字されています。
analogWrite(...)
の機能を使って、さきほどと同じようにLEDを点灯させてみましょう。プログラミングを以下のように書き換えてシミュレーションをしてみてください。回路例はこちら(TinkerCadに移動します)。
補足:オーバーフロー
analogWrite(...)
つまり「ピン X を XXX に設定」で設定可能な数値は、「0」~「255」まで整数値( $0$ を含めての $255$ なので、$256$ 段階=$2$の$8$乗段階)になります。もし、$256$ 以上の値、例えば「$270$」とかをアナログ出力に与えると、それは $270-256$ で「$14$」を与えたことになってしまいます($14$ になると PWM出力が約 $5%$ となるので、LED出力は相当に暗いです)。このような現象を**オーバーフロー(桁あふれ)**といいます。詳しく知りたい場合は「オーバーフロー 数値 コンピュータ
」などで検索してみてください。
補足:analogWrite(...) によるPWM出力の周波数
analogWrite(...)
による PWM出力の周波数は基本的に固定です。5番と6番ポートは $980\mathrm{Hz}$、その他は $490\mathrm{Hz}$ になります。これは、Tinkercad の オシロスコープモジュール 使って確認することもできます。
9番ポートからデューティ比 $50%$ で PWM 出力して、それをオシロスコープモジュールで観測してみます。シミュレーションを実行すると、次のように約 $500\mathrm{Hz}$($490\mathrm{Hz}$)になっていることが確認できます。回路例はこちら(Tinkercadに移動します)。
なお、周波数を変更することも可能ですが、初心者向けではありません。気になる方は以下が参考になると思います。
- PWM周波数を”自由に”変更する @ デジモノ覚書
- analogWrite( ) @ Arduinoで遊ぶページ
演習10:電子ホタル(1匹)~準備~
次のような回路を構成し、PWM出力を利用して、LEDが ゆっくりとした明暗の変化を繰り返す ような電子ホタルをつくりたいと思います。
まずは、作成例をコピーして動作を確認しましょう。こちら をクリックすると、サンプルに飛ぶので、その画面内の「コピーして編集」をクリックします。自分の領域に複製が作成されるので、シミュレーションを実行して動作を確認しましょう。
この「複製して動作確認まで」が演習10です。
演習11:電子ホタル(1匹)~プログラムの読解~
演習10 で複製した回路のコードを確認してみましょう。プログラムには、新たな要素として「変数」と「繰り返し」が登場しています。しかし、**「繰り返し」のブロックの表記(和訳)が残念なことになっていて、正直、英語に切り替えたほうが分かりやすい**です。以下は、C言語での while(a<=10){ ... }
に対応するブロックですが(高校生以上なら)英語のほうが分かりやすいですよね。
言語は、Tinkercad のダッシュボードの最下部から変更可能です。必要に応じて英語に切り替えてください(英語モードにしたとき、ブラウザの翻訳はオフにしておきましょう)。
コードエディタを開いてブロックあるいはコードを読み解いてみてください。また、次の課題12ではゼロから作成してもらうので、眺めるだけではなくで、1ブロックずつ意味を考えながら読み解きましょう。
プログラム内のパラメータ(100
や 15
などの数値)を変更して、動作がどのように変わるか予測・確認することも理解の助けになります。
ソースコードを、直接、見たほうが分かりやすいかもしれません。
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$ になります。
これを次のようにプログラム上に実装すれば、同様に電子ホタルが作成できます(ブロックでは $\pi$ が定数として利用できないので、$2\pi\simeq 6.28$ としています)。
演習12:電子ホタル(1匹)~作成~
演習11と同じものを、ゼロから(回路を含めて最初から)構成してみましょう。見本のコードを眺めて理解したつもりでも、実際に手を動かしてみると「??」となることは多々あります。必ず実際に作成してみましょう。
なお、この課題では変数を利用する必要があります。次の手順で変数を組み込みます。
- 変数のグループのなかにある「新しい変数」をクリックします。
- ダイアログが表示されるので、変数名を入力します。日本語は避けましょう。
- 作成した変数を削除したい場合は、右クリックして「変数 x を削除」を選択します。
演習13:電子ホタル(2匹以上)~作成~
LED を 2つ以上 に増やし、なおかつ、それらの点灯タイミングがズレるようにしてみましょう。
-
解答例はこちら(TinkerCadに移動します)。
- $\sin(\omega t) $ と $\sin(\omega t+\theta) $ の関係を利用して変数1つで対応する方法。
-
解答例(別解)はこちら(TinkerCadに移動します)。
- 三角関数を使わずに変数4つで対応する方法(お勧めしない)。
-
3Hの学生さんは、最低でも 30分 は自分で粘ってから解答例を見るようにしてください。数分で諦めるのは早過ぎできす。また、解答例をみて理解したあとも、必ず自分で動かして同じようにロジックを構成してみましょう(「檸檬」という漢字が「読めること」と「書けること」は違うのと同じ理屈です)。
補足:LEDを直列接続して同時点灯させる回路
演習13 とは直接的には関係ありませんが、LEDを直列接続して同時点灯させる回路 を説明しておきます(第3回 で学習した 単体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$ としています)。
また、直列接続した $n$ 個の LED を点灯させる場合、電流制限抵抗 $R$ は次のように決めます。なお、電源電圧 $V_{\mathrm{CC}}$ は、少なくとも $nV_{F}$ 以上にする必要があります。
$$ 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$ としています)。
ところで、Arduino の デジタル出力は $5,\mathrm{V}$ なので上記のように、5つものLEDを直接接続することはできません。その場合、次のように トランジスタ(NPN) を使って回路を構成します。
なお、ベース抵抗(図内の $10k\Omega$ の抵抗)には、ちゃんとした設計法があるのですが、それについては省略します(電子回路の授業や、実験実習で習うハズです)。気になる人は「トランジスタ スイッチング ベース抵抗 設計
」をキーワードにググってください。
- 回路例はこちら(TinkerCadに移動します)。
なお、デジタル $6$ 番ポートから PWM出力 をさせていても、問題なく動作します。
さらに、次のように並列化することで、さらに多くの LED を点灯させることができます(電源の電流負荷が2倍になっていることに注意してください)。
EX演習2:クリスマスイルミネーション
トランジスタを使ったスイッチング回路も組み合わせて多数のLEDが点灯するクリスマスイルミネーションの回路とプログラムをつくってみましょう。