2015年09月06日

Make Electronics - ソフトウェアだけじゃあ楽しくない! L チカで学ぶ電子工作の基礎

僕は IT に興味と可能性を感じていて、ソフトウェアという論理的なものを中心に「おもしれー」って思うことを学んだり、作ったりしてきました。 でも、最近ちょっと退屈です。 だって、僕らの生活において目の前で直接的な現象を起こすラスト・ワンマイルを作るには物理的な知識と技術が必要で、それが僕には足りないからです。 だから、少しずつだけど、より下層レイヤーについて学び直そうって考えたんです。 あ、L チカってのは LED をチカチカと点滅させることを指していて、電子工作において基礎を学ぶ大変大事で簡単な作業のことです。

LED

L チカを構成する電子部品とその基礎


L チカっていうぐらいですから LED は必須。 僕は、購入した電子工作キットに含まれていた、多分 OSNG3133A(http://akizukidenshi.com/catalog/g/gI-00625/)であろう 5 mm 黄緑色 LED を使います。

IMG_3859


よく見ると、LED の二本の足(リード線)はそれぞれの長さが違っていると思います。 長い足はアノードと呼びプラス、短い足をカソードと呼びマイナス側につなげるという約束があります(方向を気にしなきゃいけないことを「極性がある」っていいます)。

データ・シートも読んでおきましょう。 極性に基づき正しい配線を行った時、LED の両端に生じる電圧を順方向電圧(VF/DC Forward Voltage)、電位差が生じて流れる電流を順方向電流(IF/DC Forward Current)と呼び、OSNG3133A は最大で 30 mA の電流を流すことができ、標準的には VF を 2 V にして 20 mA の IF を流すのが良いみたいです。

次に電源。 今回ボクが使う電源は単三電池を 2 本収納することができる電池ケース。

IMG_3825


このケース内部では 1.5 V の単3電池が直列につながっていて、(僕の古めかしい)マルチメーターを使うと約 3.05 V の直流電圧を計測できました。

IMG_3795


これでモノの手配はおしまい。 極性に気をつけて LED と電源をつなげれば点灯するかっていうと順方向電圧の最大値を超える 3 V では生じる大きな電流とその熱で LED 内部は溶け、もう二度と使えなくなってしまいます。

この問題を回避するにはどうしたらよいでしょうか? そう LED の電圧を降下させる抵抗が必要です。

万が一にも大き過ぎる電流が流れないように安全を取って LED の両端に 1.5 V を与えるとすると(低い電圧であれば LED は比較的暗く輝くだけです)、電源電圧の 3 V のうち 1.5 V を抵抗で消費する必要がありますよね。 それに、今僕が作ろうとしている単純な閉じた回路では LED と 抵抗に流れる電流は同じになるはずで、回路に 15 mA の電流を流すのであればオームの法則から抵抗 R には R = V/I = 1.5 V / 0.015 A = 100 Ω が必要なことが分かります。

さあ、100 Ω の抵抗を見つけましょう。 抵抗には複数の帯があって金又は銀色の帯が右側になるよう横にします。 この時、左側の2つの帯はその色で数値を、3つ目の帯は先頭の2つの帯のスケールとなる10のべき乗 n を意味しています。

抵抗カラーコード_600x451

これで L チカに必要な部品 3 つ全てが揃いました。

IMG_3868


ブレッドボードを使った簡単な L ピカ(点灯)


ミノ虫クリップ(ワニ口クリップ)」を使って直感的に部品を配線するって方法もありますが、僕はこの先も見据えてブレッドボードを使うことにします。

IMG_3748


ブレッドボードには一定間隔で(IC チップと互換性のある)穴が空いています。 また、その内部では両サイド 2 列が縦方向、中央部分が横方向に電気的につながるよう金属の板が埋め込まれていて、抜き差しするだけで試験的な回路を組むことができる大変便利なツールです。

ブレッドボードの構造


先ほど考えた回路をブレッドボード上に再現するとこうなります(端にある 2 つの列を電源に使い、この列からジャンパーワイヤで中央の回路に電源を供給しています)。

IMG_3808


電源スイッチをオンにすると・・・ L ピカ。

IMG_3813


Raspberry Pi を使った LED チカ(点滅)


ピカってのは電源がオン(電圧が HIGH)の状態であってチカらせる為には HIGH、LOW を一定周期で繰り返す必要があります。 それで、ここからはプログラミング言語と Raspberry Pi のチカラを借りようと思います。

Raspberry Pi には GPIO(General Purpose Input Output)と呼ばれる汎用入出力が用意されていて、HIGH(3.3v)を 1、LOW(0v)を 0 で表すデジタル入出力や、限られたピンで複数の周辺機器(ペリフェラル・デバイス)と通信を行うことができる I2C(Inter-Integrated Circuit) や SPI(Serial Peripheral Interface)と呼ばれるシリアルバス機能をピン設定で切り替えられるようになっています。

まずは Raspberry Pi のピン・レイアウトを見てみましょうか。

RPi2 GPIO pins


あの飛び出した金具(ピン)の呼び方は少しややこしいです。 基盤そのもの(BOARD)をベースにしたピン・ヘッダー番号ってのと SoC(システム・オン・チップ)を製造している BROADCOM(BCM)のポート名をベースにした BCM 名の2つがあって一般的には BCM 名を使うことが多いみたいです。

僕はデジタル入出力以外に特に機能の無い GPIO 24 ポート(BCM)と 20 番ヘッダー・ピン(BOARD)の位置にある GND をブレッドボードに配線することにします(GND は HIGH/3.3v の基準電位になるポートです)。

/sys/class/gpio/ 下にあるファイルをコマンドや Bash スクリプトで操作するっていう簡単な方法もありますが、少し高度な制御を求めて Python で書くならこんなコードになります。

#!/usr/bin/python
# -*- coding:utf-8 -*-
import RPi.GPIO as GPIO
import time

bcm_gpio = 24
interval_in_sec = 1

GPIO.setmode(GPIO.BCM) # use broadcom name.
GPIO.setup(bcm_gpio, GPIO.OUT) # set pin to output.

try:
   for i in xrange(0, 10):
       if i % 2 == 0: # is even?
            print "HIGH (GPIO %d)" % bcm_gpio
            GPIO.output(bcm_gpio, GPIO.HIGH)
       else:
            print "LOW  (GPIO %d)" % bcm_gpio
            GPIO.output(bcm_gpio, GPIO.LOW)
       # keep voltage level for interval_in_sec.
       time.sleep(interval_in_sec)
except KeyboardInterrupt as ki:
    print "shutting down..."
finally:
    # release resource.
    GPIO.cleanup()


このコードは RPi.GPIO というライブラリを使っていて xrange で作り出した 0 から 9 の数字 i が偶数なら出力モードに設定した GPIO 24 番ポートを HIGH に、奇数なら LOW にしていて都度 1 秒間その状態をキープしています(あまり短い間隔だと人の目では点灯と点滅の見分けがつかないからです)。

GPIO.cleanup() は GPIO リソースの解放に必要な処理で、これを忘れたり例外が発生してコードの実行が中断されてしまうと GPIO は使用中のまま再び利用できなるので厄介です。

こいつを実行すると・・・

pi@raspberrypi ~ $ sudo python blinkLED.py


L チカ成功。



Raspberry Pi を使った LED ジワジワ(パルス幅変調による輝度制御)


残念ながら Raspberry Pi の GPIO にアナログ入出力はついてません。 でも僕や貴方が制御したいと考えるペリフェラル・デバイスには DC モーターやサーボのように僅かな電圧の変化に応じてその回転速度(角度)を変えるデバイスもあります。 このようなアナログ制御をデジタル入出力を使って擬似的に行うのが PWM(Pulse Width Modulation)です。

で、PWM って一体何なんでしょうか? HIGH と LOW が周期的に繰り返されるとき、ある 1 つの周期(HIGH, LOW の組み合わせ)に要する時間を T(サイクル・タイム)、その中で HIGH の占める期間(時間)を PW(パルス幅)っていいます。

PWM_600x273


周期 T における PW の割合はデューティ比(英語ではデューティ・サイクルって呼ぶのが一般的です)といって、例えばデューティ比が 50 %、HIGH 電圧 3.3 V の場合を考えると周期 T における平均電圧は 3.3 v * 0.5 duty = 1.65 v です。

「結局、ただの L チカじゃねーの?」って思うかもしれません。 でも、人には短い周期で繰り返される点滅が点灯に見えてしまうのと同じで、パルス幅変調を使うと中間電圧(LED の輝度)を作り出すことができるんです。

PWM をまとめるとこうなります。

「十分に短い周期であれば、デジタル入出力であっても PWM を使って擬似的に中間電圧を作ることが可能」

もう1つ PWM にはソフトウェア PWM とハードウェア PWM があることを覚えておきましょう。

ソフトウェア PWM は CPU を使ってソフトウェア・ベースで PWM を行うことで、これまでと同じ RPi.GPIO で書くならこんなコードになります。

#!/usr/bin/python
# -*- coding:utf-8 -*-
import RPi.GPIO as GPIO
import time

bcm_num = 24
freq = 100
interval_in_sec = 0.1

GPIO.setmode(GPIO.BCM) # use broadcom name.
GPIO.setup(bcm_num, GPIO.OUT) # set output.

mod = GPIO.PWM(bcm_num, freq)

try:
    current_duty = 0 # start with zero duty cycle.
    mod.start(current_duty)
    for i in xrange(0, 3): # main loop
        print "fade-in..."
        # fade-in loop
        while current_duty < 100:
            current_duty += 5
            mod.ChangeDutyCycle(current_duty) # where 0.0 <= dc <= 100.0
            # keep duty cycle for interval_in_sec.
            time.sleep(interval_in_sec)

        print "fade-out..."
        # fade-out loop
        while current_duty > 0:
            current_duty -= 5
            mod.ChangeDutyCycle(current_duty)
            # keep duty cycle for interval_in_sec.
            time.sleep(interval_in_sec)
except KeyboardInterrupt as ki:
    print "shutting down..."
finally:
    # release resources.
    mod.stop()
    GPIO.cleanup()


このコードは周波数 100 Hz(1 周期 0.01 秒)の PWM を生成し、ChangeDutyCycle(dc) で 5 % ずつデューティ比を変化させることで LED 輝度を最大、最小の間でフラップしています。

実行してみると・・・

pi@raspberrypi ~ $ sudo python softwarePWM.py


(ソフトウェア)L ジワー。



L ジワぐらいなら問題無いんですが、より高い精度でペリフェラル・デバイスを制御したいって考えたとき、CPU ベースのいつ誰に割り込まれるかも分からない不安定なものでは無く、専用チップを使った安定的な PWM が欲しいって思うかもしれません。 そんな時にハードウェア PWM 使います。

もう一度、あのピン・レイアウトを見て下さい。 PWM0、PWM1 って機能が書いてあるピンがあるはずです。 このピンなら 19.2 Mhz のシステム・クロックで PWM できます(RPi.GPIO では無く、より Low Level な WiringPi ライブラリも必要)。

WiringPi は Raspbian OS にパッケージングされておらず、まず、次の手順で C 言語で記述された WiringPi コアとその Python ラッパーである WiringPi2 をインストールする必要があります(もちろん、wiringPi だけでコードを書いたって構いません!)。

# Install WiringPi 
pi@raspberrypi ~ $ git clone git://git.drogon.net/wiringPi
pi@raspberrypi ~ $ cd wiringPi/
pi@raspberrypi ~/wiringPi $ ./build

# install WiringPi2-Python
pi@raspberrypi ~/wiringPi $ cd ~/
pi@raspberrypi ~ $ sudo apt-get install python-setuptools python-dev
pi@raspberrypi ~ $ git clone https://github.com/Gadgetoid/WiringPi2-Python.git
pi@raspberrypi ~ $ cd WiringPi2-Python/
pi@raspberrypi ~/WiringPi2-Python $ sudo python setup.py install


WiringPi による PWM を理解したいのであれば次の関係式を頭に入れておくべきです。

PWM 周波数 = ハードウェア・ベース周波数 / (分周比 * 分解能)

ベース周波数は Raspberry Pi のチップが生成する 19.2 Mhz と静的に決まっていて、PWM のパルス周波数は制御するペリフェラル・デバイスのデータ・シートに書いてあるでしょう。 だから多分、僕らが決めきゃいけないのはデューティ比の設定精度(分解能)だけです。

例えば、50 Mhz の PWM 信号を作り出したいとします(「じゃなきゃ、俺は命令を受け付けねぇ」ってデバイスのデータ・シートに書いてあったとします)。 1 周期の分解能を 1024 段だとすると、静的に与えられたベース周波数を 1024 回使って PWM の 1 周期を表現することになるので 19,200,000 Hz を 1 / 1024 する必要があります。 また、このままでは所望の周波数では出力されないので、50 Mhz になるように周波数を落とす、つまり分周(1 / 375)するってことになります。

これを WiringPi で書くとこうなります。

#!/usr/bin/python
# -*- coding:utf-8 -*-
import wiringpi2 as wiringpi
import time

bcm_gpio = 18 # GPIO 18 (PWM0)
interval_in_sec = 0.05

wiringpi.wiringPiSetupGpio() # use broadcom name.
wiringpi.pinMode(bcm_gpio, 2)  # (HARD) PWM_OUTPUT(2), SOFT_PWM_OUTPUT(4)

# The PWM generator can run in 2 modes – “balanced” and “mark:space”.
# The mark:space mode is traditional, however the default mode in the Pi is “balanced”.
# You can switch modes by supplying the parameter: PWM_MODE_BAL(1) or PWM_MODE_MS(0).
wiringpi.pwmSetMode(0) # PWM_MODE_MS(0)

# PWM Freq = PI_BASE_FREQ / (divisor * range)
PI_BASE_FREQ = 19.2 * 1000000 # RPi constant variable (19.2 Mhz).

# This sets the range register in the PWM generator.
max_duty = 1024
wiringpi.pwmSetRange(1024) # The default is 1024.

# This sets the divisor for the PWM clock.
wiringpi.pwmSetClock(375) # I need 50 Hz output. 

current_duty = 0
wiringpi.pwmWrite(bcm_gpio, current_duty) # duty cycle between 0 and 1024


# starting
try:
    for i in xrange(0, 3): # 0..2
        print "fade-in..."
        while current_duty < max_duty:
            current_duty += 32
            wiringpi.pwmWrite(bcm_gpio, current_duty)
            time.sleep(interval_in_sec)
        print "fade-out..."
        while current_duty > 0:
            current_duty -= 32
            wiringpi.pwmWrite(bcm_gpio, current_duty)
            time.sleep(interval_in_sec)
except KeyboardInterrupt as ki:
    print "shutting down..."
finally:
    # release resources.
    print "releasing resources..."
    wiringpi.digitalWrite(bcm_gpio, 0) # set bcm_gpio port to 0 voltage (off).
    wiringpi.pinMode(bcm_gpio, 0) # set bcm_gpio mode to input.

じゃあ、また今度。

Make: Electronics ―作ってわかる電気と電子回路の基礎 ((Make:PROJECTS))
Charles Platt
オライリージャパン
売り上げランキング: 69,439

電子工作入門以前
電子工作入門以前
posted with amazlet at 15.09.05
後閑 哲也
技術評論社
売り上げランキング: 10,628

Posted by netbuffalo at 04:17│Comments(0)TrackBack(0) 電子工作 | Raspberry Pi


この記事へのトラックバックURL

コメントする

名前
 
  絵文字