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


概要

第5回目です。前回は、Arduino(アルディーノ)の デジタル入力機能 について勉強しました。今回は、PWM出力(疑似アナログ出力)について、Tinkercad Circuit(Arduino&電子回路シミュレータ)を利用して勉強していきます。

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

アナログ出力とは

デジタル出力は、第2回 で学習したように「$1$(=HIGH)」または「$0$(=LOW)」の 2値 に対応する電圧を出力する機能でした。Arudino UNO の場合は「$1$(HIGH)が $5\mathrm{V}$ 電圧」に、「$0$(LOW)が$0\mathrm{V}$(GNDレベル)電圧」に対応していました。

ちなみに、RaspberryPimbedArduino 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値の出力 が短い時間で切り替わっていることが分かります。ただし、ランダムに切り替わるわけではなく、規則性をもって HIGHLOW が切り替わっています。具体的には、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出力 は「3561011 番のデジタル出力ポート(ピン)」でしか使用できないという制約があります。ボードには 「」 が印字されています。

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に移動します)。

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

演習10:電子ホタル(1匹)~準備~

次のような回路を構成し、PWM出力を利用して、LEDが ゆっくりとした明暗の変化を繰り返す ような電子ホタルをつくりたいと思います。

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

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

演習11:電子ホタル(1匹)~プログラムの読解~

演習10 で複製した回路のコードを確認してみましょう。プログラムには、新たな要素として「変数」と「繰り返し」が登場しています。しかし、「繰り返し」のブロックの表記(和訳)が残念なことになっていて、正直、英語に切り替えたほうが分かりやすいです。以下は、C言語での while(a<=10){ ... } に対応するブロックですが(高校生以上なら)英語のほうが分かりやすいですよね。

言語は、Tinkercad のダッシュボードの最下部から変更可能です。必要に応じて英語に切り替えてください(英語モードにしたとき、ブラウザの翻訳はオフにしておきましょう)。

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

プログラム内のパラメータ(10015 などの数値)を変更して、動作がどのように変わるか予測・確認することも理解の助けになります。

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

電子ホタル(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$ になります。

これを次のようにプログラム上に実装すれば、同様に電子ホタルが作成できます(ブロックでは $\pi$ が定数として利用できないので、$2\pi\simeq 6.28$ としています)。

演習12:電子ホタル(1匹)~作成~

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

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

  1. 変数のグループのなかにある「新しい変数」をクリックします。
  2. ダイアログが表示されるので、変数名を入力します。日本語は避けましょう。
  3. 作成した変数を削除したい場合は、右クリックして「変数 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が点灯するクリスマスイルミネーションの回路とプログラムをつくってみましょう。

次回