4 デジタル出力

今回の講義の目的は、マイコンの使い方を理解することと、デジタルピンの使い方をマスターすることです。

  • 4.1 マイクロコントローラの基礎
  • 4.2 デジタル信号・アナログ信号
  • 4.3 GPIO
  • 4.4 デジタル出力
  • 4.5 実習

4.1 マイクロコントローラの基礎

まず、マイコンについて基本的なことを説明しておきます。マイコンは、一般に以下の写真のような見た目をしています。

写真のマイコンは、細長い形をしていて両側に足が出ていますが、形状はいろいろなものが存在します。多くの場合、黒いプラスティックのパッケージになっています。このプラスティックのパッケージの中に、マイコン本体が封入されています。よって、マイコン本体は、もっと小さなものです。

マイコンからは、ゲジゲジの足のようなものが出ています。これをピン(pin)、あるいは端子と呼びます。マイコンは、このピンを通じて電源供給を受けたり、他の部品と連携したりします。

Raspberry Piに搭載されているマイコンは、以下の写真に示す場所にあります。

マイコンからは小さなピンが出ていますが、ものすごく小さくてプロトタイプを作る際には使いづらいことが多いです。そのため、マイコンボードは、ピンから配線を延ばして回路を作りやすい形状にしているのです。実際の製品にするときには、マイコンボードは使わずに、よりコンパクトな基盤にすることが多いです。

Raspberry Pi Zeroに搭載されているマイコンは、Broadcom社のBCM2835です。

  • メモリ: 512MB
  • CPUクロック: 最高1GHz (通常は700MHz)

BCM2835はシングルコア/シングルスレッドです。Raspberry Pi 3+やRaspberry Pi 4になると、もっと高性能のマイコンが搭載されていて、クアッド(4)コア/クアッドスレッドになります。

マイクロコントローラには、メモリ、CPU、GPU、通信モジュールなどがセットになって入っています。こういうLSIをSoC(System On Chip)と呼びます。

4.2 デジタル信号・アナログ信号

電気の世界における信号とは、時間とともに変化する電圧によって情報を表すことです。デジタル信号とは、2つの電圧によって0と1を表現したもので、例えば、0Vが0で3.3Vが1のように表現します。

実際の電子回路では、0を1V未満、1を2.5V以上のように、ある閾値(しきいち)を定めて判断しています。閾値とは境界となる値のことです。しかし、今は、そこまで意識する必要はありません。

アナログ信号の場合には、電圧の大きさで何かの大きさや量を表現します。例えば、部屋の明るさの場合には、光量を知りたいですよね。陽が昇れば徐々に明るくなり、陽が暮れれば徐々に暗くなります。夜中に電気をつければ突然明るくなったりもします。部屋の明るさを光センサを使って連続的に計測して電圧の変化に変換したもの、それがアナログ信号です。

4.3 GPIO

前回、Raspberry PiのGPIOについて簡単に説明しました。ここでは、入出力という観点からもう少し詳しく説明します。以下にGPIOのピン配置図を再掲します。

GPIOのピンうち、3.3V、5V、GROUNDは電源です。そのほかのピンにはGPIO 0のように、GPIOに続いて番号が記されています。これらGPIO 0からGPIO 27までは、入出力に使える汎用的なピンになります。これらの汎用的なピンは、プログラム中でデジタル出力にするかデジタル入力にするか、アナログ入出力にするかを決めることができます。ここではアナログ入出力は説明しません。

GPIOのピンをデジタル出力にすると、そのピンに3.3Vかけるか0Vにするのかを決めることができます。デジタル入力にすると、3.3Vであれば1、0Vであれば0という入力を受け付けることができます。

ピン配置図をみると、GPIO 2(SDA)のように番号の後ろにカッコ書きでキーワードが記されているピンがあります。これらは、汎用の入出力ピンであるとともに、特殊な役割を持ったピンということになります。今日はこれら特殊な役割については説明しません。

4.4 デジタル出力

GPIOを使ってLEDをプログラムから点灯させたり消灯させたりしてみましょう(点灯制御)。

回路図は以下のようになります。

GROUND記号になっているところはRaspberry PiのGROUND(例えば6番ピン)に繋ぎますが、回路図ではこのように書きます。GPIOは11番ピンを使っています。できるだけ、特殊な役割を割り当てられていないGPIOを使います。

実際にブレッドボードに回路を作った様子は以下のようになります。この写真では、Raspberry Pi ZeroにGrove Base Hatを付けていますが、まだ付けなくて良いです。

PythonからGPIOを制御するためには、GPIO Zeroというライブラリを使います。以下のようにして、インストールしてください。これは当然Raspberry Piにログインして行います。

GPIO Zeroのドキュメントは以下の場所にあります。

https://gpiozero.readthedocs.io/en/stable/

適当な場所に、LEDchikaというディレクトリを作成して、以下の内容をled_chika01.pyという名前で保存します。このプログラムは、1秒間隔でLEDを点けたり消したりします。いわゆる「Lチカ」プログラムというやつです。

led = LED(17)17GPIO 1717に対応します。GPIO 17は11番ピンです。LEDクラスのインスタンスであるledは、onoffというメソッドを持っています。

https://gpiozero.readthedocs.io/en/stable/api_output.html

LEDを使うと、GPIO 17はデジタル出力用に設定されます。onで3.3V、offで0VがGPIO 17にかかります。

メインループ

組込みシステムのプログラムは、組込みソフトウェアと呼ばれます。組込みシステム開発といったらハードウェア込みの開発ですが、組込みソフトウェア開発と言ったら組込みシステムのうちのソフトウェアの開発だけを指します。

組込みシステムは多くの場合、電源を入れるとすぐにプログラムが起動して、そのままずっと動き続けます。電子レンジは、電源を入れるとすぐに操作受付が可能になり、調理が終わってもユーザからの入力を受け付け続けています。このように、組込みソフトウェアは無限ループの構造になっているのです。

組込みソフトウェアは、通常、メインループと呼ばれる無限ループを持っています。このメインループの中で、ユーザからの入力を受付たり、それに対する反応を返したりします。

先ほどのプログラムでは、main_loop()という関数がメインループになっています。

この講義では、メインループの名前としてmain_loop()を使うことにします。この書き方を守ってください。main_loop()の中には、無限ループであるwhile True:を書きます。

main_loop()を呼び出しているのは、以下の部分です。

__name__という変数は特別な変数で、このプログラムがシェルから実行された時には__main__が自動的に代入されています。led_chika01.pyがモジュールとして他のプログラムに輸入(import)された時には、__name__にはled_chika01が代入されます。つまり、このif文は、モジュールとして輸入されたのかシェルから実行されたのかを判別しているわけです。この書き方は、Pythonのお約束のようなものなので、従っておきましょう。

ソース電流・シンク電流

先ほどの例では、GPIO 17をオン、すなわち3.3VにするとLEDが点灯しました。これを、ソース電流(source current)を使った接続と言います。LEDを点灯させるには、もう1つ方法があります。

3.3Vの電源は、Raspberry Piの1番ピンからとります。

こちらは、シンク電流(sink current)を使った接続と言います。電流の流れる向きに注意してください。この回路は、3.3V→抵抗→LED→GPIO 17という向きに電気が流れます。

LEDを点灯させるには、以下のようなプログラムを書きます。ファイル名は、led_chika02.pyにしましょう。

先ほどのled_chika01.pyとの違いが分かりづらいですが、以下の部分が違います。

LED(17)としたときには、active_high=Trueとなっています。このときにはonでピンに3.3Vかかります。active_high=Falseを指定すると、onで0V、offで3.3Vかかるようになります。

GPIO 17に3.3Vかかっているときには、LEDの両端は0Vを基準として同じ3.3Vになります。このとき、この3.3Vを0Vを基準とした電位といいます。電位が同じ場所には電流は流れません。GPIO 17が0Vになると片方の電位は3.3V、もう片方の電位は0Vとなります。電位に差が生まれて電流が流れます。この差のことを電位差と呼びます。要は電圧の差のことだと思えば良いです。

LEDと電流

LEDの使い方は、意外と簡単だと思いませんか?しかし、それは、LEDの数が少ない場合です。複数のLEDを光らせるのは、結構難しいことなのです。マイコンのピンは、ソース電流にしてもシンク電流にしても、流せる電流の最大値が決まっています。この講義では、以下の制限を設けることにします。これを超えてLEDを繋いだ場合には、マイコンが壊れる可能性があります。

  • 1つのデジタルピンで制御できるLEDは1つだけ(5mA)
  • マイコン全体に接続できるLEDは8個まで(5mA x 8個 = 合計40mA)
  • 同時に点灯できるLEDは6個まで(5mA x 6個 = 合計30mA)
    ※LEDを8個接続していても同時にONにできるのはその内の6個まで

8個以上のLEDを制御したい場合には、LED用の外部電源(電池など)を用意する必要があります。これに関しては、途端に難しくなりますので、別の機会に説明します。

ただし、この制限は、LEDだけをRaspberry Piにつないでいるときの話です。他のデバイスが繋がっている場合には、それを含めて考えなければいけません。

※本当はもう少し余裕があります。ここでの制限はかなり安全よりにしてあります。

4.5 実習

実験1. ソース電流を使う

ソース電流を使って、1つのLEDを点滅させてみましょう。「点滅」とは光ったり消えたりを繰り返すという意味です。点滅間隔は自由に決めて色々と変えて見ましょう。

実験2. シンク電流を使う

シンク電流を使って、1つのLEDを点滅させてみましょう。1番ピンを電源とします。

実験3. 3つのLEDを光らせる

3つのGPIOピンを使って、3つのLEDを光らせてみましょう。3つのLEDのうち、点灯しているのは1つだけで、その点灯しているLEDを0.5秒間隔で順番に切り替えていきます。ソース電流でもシンク電流でも良いです。LEDは、できるだけまっすぐに並べて配置してください。どのGPIOピンを使うかは各自で決めてください。

以下の回路図はシンク電流を使っています。

3個以上のLEDがある場合には、リストを使って管理すると便利でしょう。

実験4. LEDを使って0から7までの数を表現する

実験3の回路をそのまま使います。変数xに正の整数が代入されているとしましょう。xを2進数で表現した際のビット列をLEDの点灯消灯で表してみましょう。今、LEDは3つしかありませんから、整数は0から7までしか表現できません。xに代入されている値が8以上の場合には、下位3ビットだけ使うことにします。

x = 5の場合を考えてみましょう。5を2進表現すると101です。101が2進表現であることを明示するために0b101と書くことにしましょう。この時は、向かって一番右側のLEDと向かって一番左側のLEDを点灯させ、真ん中のLEDは消灯させます。

x = 9の場合を考えてみましょう。9を2進表現すると0b1001です。この場合には、下位3ビットを使いますから、001の部分だけをLEDで表現します。向かって一番右のLEDだけを点灯させ、他の2つは消灯させます。

xのnビット目を取り出すためのプログラムは、いくつかの書き方ができます。一番簡単で直感的なのは、除算と剰余演算を使うことでしょう。xを2で割った余りは、下から1ビット目の値です。0b101の下から3ビット目を得たければ、2で2回割った答えに対して2で割った余りを求めれば良いです。ただし、注意しなければならないのはPythonの/の結果は実数になるということです。整数の割り算は//という演算子を使います。

0b101を2で割った答えは0b10になります。10進で考えると5を2で割ると2になるということですね。0b10を2で割ると0b1となります。10進で考えると2を2で割ると1になるということです。最後に0b1を2で割った余りを求めると1になります。10進で考えると1を2で割った余りは1になるということです。

for文を使って3回繰り返すことによって実現してください。

実験5. LEDを使って0から7までの数を表現する2

実験4で、整数の下位3ビットを使ってLEDを光らせました。まず、これを関数にしてみましょう。関数名はbin_led()とします。

このbin_led()は引数として、正の整数numと、LEDのリストをとります。


この関数を呼び出す時には、以下のようにするということですね。

3つ全てのLEDを消灯させる関数も作っておくと便利だと思います。

bin_led()を使って、0から7までの整数を順にLEDで表現します。0.5秒おきに、数字が増えていくようにしてください。7まで表示したら、また0に戻って表示を繰り返します。

LEDの光り方は以下の動画を参考にしてください。

実験8の参考動画