GPIO
Raspberry Pi の表面を見ると、剣山のような細い針金が40本あります。これはGPIOと呼ばれるインターフェイスです。GPIOは General Perpose Input Output の略です。このGPIOにLEDやセンサなどをつなげることができます。
Raspberry Piの公式ページにGPIOについての説明があります。
https://www.raspberrypi.com/documentation/computers/os.html#gpio-and-the-40-pin-header
https://www.raspberrypi.com/documentation/computers/os.html#gpio-pinout
今後、このGPIOピンに接続して回路を作成していきますので、ピンのどこがどこであるかを調べられるようにしてください。開発論でも資料で説明があったと思いますが、pinoutコマンドで簡易的に調べることもできます。
$ pinout
LED
LEDは、光る部品です。日本語では発光ダイオード、英語では Light Emitting Diode といいます。ダイオードという電子部品の一種です。線が2本伸びていますが、どちらをプラスにして、どちらをマイナスにするか決まっています。これを、極性があるといいます。足の長い方の線をアノードと呼び、足の短い方の線をカソードと呼びます。アノードがプラスで、カソードがマイナスです。間違えると光りませんので、うまく光らない場合は、電流が流れる向きを確かめてみるとよいでしょう。
赤色LED、緑色LED、青色LEDがあります。ちなみに青色LEDは、3つのうちで一番最後に発明されました。発明者は日本人で、赤崎 勇・天野 浩・中村 修二の各氏です。この発明の対価をめぐって訴訟に発展したことは、みなさんも聞いたことがあるかもしれません。
また、LEDは流せる電流の上限値が決まっています。このような上限値のことを、絶対最大定格と言います。LEDに、絶対最大定格で決められている以上の電流を流すと、LEDは壊れます。
多くのLEDでは、20mAくらいの電流を流すと一番明るく光るようになっているようです。しかし、20mAを超えると急激に危険な領域に入ります。そこで、この講義では、5mAを流すことを標準とします。5mAでも十分に明るく光ります。
使用する抵抗の計算
3.3Vの電源を使ってLEDを光らせることを考えます。
それでは、抵抗を使う方法から説明しましょう。適切な抵抗を入れて、電流を制御するわけです。抵抗値を計算するにはオームの法則を使います。
V = I × R
これを変形させると、以下のような式を得ます。
R = V / I
今、電流 I は5mAと決めましたから、あとは電圧 V がわかればよいことになります。これは電源の電圧でよいのでしょうか?
それを知るには、LEDの性質を知らなければいけません。LEDは、電流が流れていれば光り、流れている電流が大きいほど明るく光る部品なのですが、ある一定以上の電圧がかかって初めて電流が流れ始めます。つまり、それまでは光らないということです。また、電流が流れ始めて光り出すと、ほんの少しの電圧の増加で大きく電流が増加します。これらのことから、LEDが光っている時には、光の強さによらず、ほとんど一定の電圧の低下が起こります。これを順方向電圧降下と呼びます。どのくらい電圧が降下するかはLEDによって異なり、正確にはデータシートを見なければわかりません。データシートには、順電圧(Vf)という名前で記載されています。ここではLEDの順方向電圧降下は2.0Vであるとして、その値を例にして説明をします。
LEDを光らせる時の抵抗値を計算するには、LEDにかかる電圧がほとんど一定であることを考慮に入れて、以下のように行います。
抵抗値 = (電源電圧 – 順方向電圧降下) / LEDに流したい電流
実際に3.3Vの電源電圧で計算してみましょう。電流の単位はAですから、5mAは0.005Aとなります。
抵抗値 = (3.3V – 2.0V) / 0.005A = 260Ω
回路に260Ωの抵抗を入れれば、5mA流れてくれるわけです。しかし、授業中に配布した部品の中には260Ωの抵抗は入っておらず、代わりに330Ωの抵抗が入っています。これでは、ダメなのでしょうか。330Ωでどのくらいの電流が流れるかを計算してみましょう。
電流 = (3.3V – 2.0V) / 330Ω = 約3.9mA
これでも全く問題ありません。330Ωの抵抗を使ったとしても3.9mA流れますので、まだ光ります。LEDを光らせるときには、5mA前後であれば、ある程度適当で良いです。多くの場合、抵抗の値は近ければなんとかなります。
それでは、10KΩにしたらどうでしょうか。
電流 = (3.3V – 2.0V) / 10000Ω = 0.13mA
0.13mAだと光らないか、もしくは光ったとしても本当にわずかです。
注意!: 抵抗をはさまずに、LEDだけを3.3Vとグラウンドの間に接続すると、一瞬でLEDはこわれます。今回使うLEDは順方向電圧降下が2.0Vであるので、3.3Vの電圧がかかると大電流が流れてしまうからです。LEDを使うときは抵抗をセットで使うと覚えておきましょう。
LEDを光らせる
それでは、実際にLEDを光らせてみましょう。電源の+側としては Rasperry Pi のGPIOに出ている3.3Vを使います。また、電源の-側としてはグラウンドを(GROUND)を使います。グラウンドは0Vを意味するきまりなので、覚えてください。
もう1度、GPIOピンの配置を確認して、3.3VがGPIOの何番ピンなのかを確認しましょう。1番ピンと17番ピンですね。今回は1番ピンを使います。グラウンドは6番、9番、14番、20番、25番、30番、34番、39番です。今回は6番ピンを使います。
ブレッドボードを使って、3.3Vから、330Ωの抵抗につながり、LEDに行き、固いジャンパワイヤ(青)を伝って黒いワイヤにたどり着くように回路を組みます。回路図どおりであることを確認しましょう。
注意!: 大事なことなのでもう1度書きます。抵抗をはさまずに、LEDだけを3.3Vとグラウンドの間に接続すると、一瞬でLEDはこわれます。LEDを使うときは必ず間に抵抗をはさんでください。
Raspberry Pi を起動すると、GPIOピンにも電圧が出力されます。正しく回路を接続していると、写真のようにLEDは点灯するはずです。
プログラムから制御する
GPIOのピンうち、3.3V、5V、GROUNDは電源ですが、そのほかのピンには GPIO 0 のように、GPIOに続いて番号が記されています。これら GPIO 0 から GPIO 27 までは、入出力に使える汎用的なピンになります。
GPIOのピンをデジタル出力のために使うように設定にすると、そのピンの電圧を3.3Vにするか0Vにするかを決めることができます。このことを使ってLEDをプログラムから点灯させたり消灯させたりしてみましょう(点灯制御)。
回路図は以下のようになります。
上の回路図と比べると、電源の3.3Vの部分が「GPIO 17」と書かれた五角形に置き換わっています。この五角形はマイコンボードの入出力ピンを意味するきまりです。
ここで、GPIOのピンの電圧を3.3Vにするか0Vにするかを決めることができることを思い出してください。3.3Vにすればグラウンドとの間に電圧がかかるので電流が流れてLEDが点灯します。0Vにすればグラウンドとの間に電圧はかからないので電流は流れずLEDは消灯します。
実際にブレッドボードに回路を作った様子は以下のようになります。ややこしいですが、 GPIO 17 は11番ピンなので、11番ピンから抵抗にジャンパワイヤをのばします。電源の+側でも-側でもないワイヤは赤と黒以外(ここでは黄色)を使うことが慣例です。
PythonからGPIOを制御するためには、 GPIO Zero というライブラリを使います。以下のようにして、インストールしてください。(すでにインストールされている(そういうメッセージが出る)かもしれません。)これは当然 Raspberry Pi にログインして行います。
$ sudo apt install python3-gpiozero
適当な場所に、以下のプログラムを作成して保存します。保存したらシェルから実行してください。このプログラムは、1秒間隔でLEDを点けたり消したりします。いわゆる「Lチカ」プログラムというやつです。プログラムを終了するには、Ctrl-Cを押します。
from gpiozero import LED
from time import sleep
led = LED(17)
def main_loop():
while True:
led.off()
sleep(1)
led.on()
sleep(1)
if __name__ == '__main__':
main_loop()
詳細は開発論の資料を参照してください。
アナログというのは「連続的な」という意味ですが、ここでは 0 (0V) と 1 (3.3V) の中間の電圧を出力するという意味です。マイコン、もしくは、 Raspberry Pi では、 0 (0V) か 1 (3.3V) のデジタル信号を扱うのが基本であるため、アナログ出力を扱うにはPWMという追加の知識が必要になります。これはデジタル出力を使って擬似的にアナログ出力を実現する方法だと思ってください。
PWMの理論
マイコンのデジタルピンからの出力は、原理的にはHIGH(例えば3.3V)かLOW(例えば0V)のどちらかだけです。しかし、0Vから3.3Vまでの間で電圧を自由に変えたいという場合があります。例えば、LEDの明るさを調整したい場合などです。こういう時には、 PWM (Pulse Width Modulation) という方法を用います。
PWMの仕組みを一言で説明すると、HIGHとLOWを素早く切り替えることによって、HIGHとLOWの間の電圧を擬似的に作り出す方法です。以下の図をみてください。
このようにHIGHとLOWを規則的に繰り返す信号を、パルス波と言います。上記の図で示しているパルス波は、ある一定時間で同じパターンを繰り返していますね。このような繰り返しの間隔を周期と呼び、1秒間に何周期あるかを周波数と呼びます。周期の始まりは、LOWからHIGHに変わる瞬間で、周期の終わりは、次にLOWからHIGHに変わる瞬間です。周波数1kHzと言ったら、1秒間に1000回の周期が起こるような波形を意味します。
図で示したパルス波は、1周期におけるHIGH(3.3V)とLOW(0V)の間隔が、ちょうど1/2ずつになっています。周波数が十分に高い場合,このデジタルピンにかかる電圧は、3.3V / 2 = 1.65Vがずっとかかっているのと同じ意味を持ちます。これがPWMです。実際に電圧を変えるのではなくて、擬似的に電圧を変えているのです。
PWMを使った場合にデジタルピンにかかる電圧は、デューティ比(duty ratio)によって決まります。デューティ比とは,1周期の間にHIGHになっている割合のことです。HIGHが3.3Vの場合、デューティ比50%ならば1.65V、10%ならば0.33Vとなります。
PWMを使用する場合、どのくらいの周波数が必要かは難しい問題なので、あまり深入りしないことにしますが,1Hzではだめなことは明らかです。1Hzということは、HIGHとLOWが同じ時間であるとすると1周期内で0.5秒ずつです。LEDで実験すると単に点滅するだけです。
PWMの周波数は、デジタルピンの用途によって変わるとされています。例えばLEDを使う際に必要な周波数,モータを使う際に必要な周波数という具合です。モータの場合には10kHzから20kH位が適当でしょう。LEDだと、100Hzくらいでも大丈夫だと思います。
例えば、10kHzのPWMをデューティ比20%で実現したいという場合には、1周期は100μsですから、20μsの間HIGHを出力して、80μsの間LOWを出力して、というのを繰り返す必要があります。これを行うにはプログラム上のタイマーで20μsや80μsを計測するということに原理的にはなります。
PWMの実際
以上がPWMを使ってアナログ出力を(擬似的に)実現するための理論です。しかし、実際にPWMを使うときはもっと簡単で、 GPIO Zero には、PWMのためのライブラリが用意されています。このライブラリを使えば、プログラムする際にタイマーを設定するなどの面倒なことは必要なくなります。
GPIO Zero に、PWMLEDとPWMOutputDeviceという2つのクラスがありますが、ここではPWMLEDの方を使います。この2つのクラスの詳細は、 GPIO Zero のリファレンスの"API – Output Devices"を見てください。
https://gpiozero.readthedocs.io/en/stable/api_output.html
下記のプログラムは、異なるデューティ比でLEDを交互に光らせるものです。デューティ比80%の時には明るく光り、デューティ比10%の時には暗く光るはずです。
from gpiozero import PWMLED
from time import sleep
led_pin = 17
led_pwm = PWMLED(led_pin)
def main_loop():
while True:
led_pwm.value = 0.8 # デューティ比80%
sleep(0.5)
led_pwm.value = 0.1 # デューティ比10%
sleep(0.5)
if __name__ == '__main__':
main_loop()
PWMLEDはデフォルトでPWMの周波数を100Hzにしています。これを変えるには以下のようにします。
led_pwm = PWMLED(led_pin, frequency=500)
もう少し複雑な例を上げておきます。これは、デューティ比を徐々に増やしながらLEDを光らせています。LEDの明るさが滑らかに変わることが観測されるはずです。
from gpiozero import PWMLED
from time import sleep
led_pin = 17
led_pwm = PWMLED(led_pin)
def main_loop():
duty_ratio = 0.0 # デューティー比の初期値
while True:
led_pwm.value = duty_ratio
duty_ratio += 0.05 # 5%ずつ増やしていく
sleep(0.1)
if duty_ratio >= 1.0:
duty_ratio = 0 # 100%以上になったら0に戻す
if __name__ == '__main__':
main_loop()
デューティ比の変化が速すぎると、それを目で確認できなくなるので、上記の例ではsleep(0.1)で、デューティ比の変化させる際に100msの間隔を空けています。これを入れないと、LEDは高速で点滅しているような感じになってしまいます。
スイッチ
スイッチというのは、電線を連結したり切断したりするためのデバイスです。ボタンということもあります。スイッチには様々な種類が存在しますが、よく使うものはタクトスイッチと、トグルスイッチです。
タクトスイッチ(tactile switch)とは、ボタンの形状をしていて、押すと電線が連結され、離すと切断されます。写真のタクトスイッチは、足が4つ付いていますね。これらの足は、2つずつの2グループに分かれていて、同一のグループに属する足は、常に通電するようになっています。どの足とどの足が繋がっているかは、背面にマークがあることが多いです。写真の製品の場合には、背面に線が書いてあって、繋がっている足がわかります。マルチメータを使ってどの足とどの足が通電しているか確認しておきましょう。
タクトスイッチをブレッドボードに挿す時には以下のように真ん中の仕切りをまたぐようにするとよいでしょう。
トグルスイッチ(toggle switch)とは、電線の連結と切断を切り替えられるようにしてあるスイッチです。どちらかを選択すると、他を選択し直すまで同じ状態を保ちます。
スイッチの状態を感知するというのは、マイコンにとってはデジタル信号の入力を行うことです。ピンにかかる電圧は3.3Vか0Vのどちらかになり、3.3Vならば1、0Vならば0という数値で認識します。
スイッチは、直感的にわかりやすいデバイスですが、マイコンへの入力として使うときには少し注意が必要です。回路を誤ると、タクトスイッチが押されたかどうかをマイコンで検知できなかったり、スイッチを押したときに3.3Vとグランドがショートしてしまい、火花が飛んだり導線が焼き切れて焦げた匂いがしたりマイコンボードが破損したりして危険です。
正しくは以下のような回路を組みます。回路に使われる抵抗は、10kΩがよいでしょう。
この例では、10KΩの抵抗と3MΩの抵抗は並列に接続されていますね。並列に接続されているということは、どちらにも同じ電圧がかかるということです。タクトスイッチが押されると、並列回路の部分に3.3Vの全てがかかるはずです。よって、 GPIO 17 では3.3Vが計測されます。タクトスイッチが離されている時には、抵抗に電流は流れませんので両端で電圧差はなく、 GPIO 17 はGROUNDと同じ電圧になりますから0Vになるわけです。
このような回路をプルダウン回路と呼び、10KΩの抵抗をプルダウン抵抗と呼びます。
プルダウン回路とは逆に、タクトスイッチが離されている時に3.3Vが計測され、押されている時に0Vとなる回路も作れます。
これをプルアップ回路と呼び、10KΩの抵抗をプルアップ抵抗と呼びます。この場合、スイッチが離されている時に3.3Vが計測されます。
プログラムから読みとる
それでは実際に回路を作って試してみましょう。プルダウン回路にしてみます。以下のようなプログラムを作成して保存してください。
from gpiozero import Button
from time import sleep
button = Button(17, pull_up=None, active_state=True)
def main_loop():
while True:
if button.is_pressed:
print('pressed')
else:
print('not pressed')
sleep(0.1)
if __name__ == '__main__':
main_loop()
実行すると、not pressedが連続して出力されます。タクトスイッチを押すとpressedと出力されるはずです。このプログラムは GPIO Zero のButtonクラスを使っています。ボタンからの入力を監視しているのは以下のオブジェクトです。
button = Button(17, pull_up=None, active_state=True)
17はGPIO 17の17です。pull_up=Noneは Raspberry Pi のGPIOに内蔵されているプルアップ・プルダウン抵抗を無効にするための指定です。button.is_pressedはbool型の変数で、ボタンが押されているときはTrueになり、押されていないときにはFalseになります。より詳細は、以下の GPIO Zero のドキュメントを参照してください。
https://gpiozero.readthedocs.io/en/stable/api_input.html#button
チャタリング
先ほどの回路にLEDを追加して、スイッチを押すたびにLEDが光るか消えるかを変えることを考えます。タクトスイッチの読み取りは GPIO 17 で行い、 GPIO 27 にLEDをつないだものです。プログラムは、以下のように作ったとしましょう。
from gpiozero import Button
from gpiozero import LED
button = Button(17, pull_up=None, active_state=True)
led = LED(27)
def main_loop():
while True:
if button.is_pressed:
led.toggle()
if __name__ == '__main__':
main_loop()
このプログラムは、タクトスイッチが押されるたびにLEDの状態が変わることを意図して作ったものです。led.toggle()というメソッドは、LEDの状態を反転させます。LEDが消灯している時に呼び出されると点灯し、点灯している時に呼び出されると消灯します。
しかし、このプログラムは思ったように動かないはずです。このプログラムは、タクトスイッチを押しっぱなしになっているときに、何度もled.toggle()が呼び出されてしまうからです。プログラムは高速に動作しているので、普通にボタンを押しただけでも何度もループしてLEDのオンオフが反転してしまいます。
LEDがコロコロ反転してしまうのは困るので、タクトスイッチが押された瞬間を検知して、そのときだけ反転させることにします。考えかたとしては、 GPIO 17 の値を読んだ時にそれがTrueで前回読んだ時にはFalseだった場合が、タクトスイッチが押された瞬間と考えられそうです。これをもとにプログラムで表現してみます。
from gpiozero import Button
from gpiozero import LED
button = Button(17, pull_up=None, active_state=True)
led = LED(27)
def main_loop():
previous = False
current = False
while True:
previous = current
current = button.is_pressed
if previous == False and current == True:
led.toggle()
if __name__ == '__main__':
main_loop()
このようにすると、previousには前回ボタンの状態を読んだときの値が、currentには今回ボタンの状態を読んだときの値が記憶されることがわかるでしょうか。if文では、前回がFalseで今回がTrueのときにLEDを反転させています。
しかし、実際に実験してみると、これも思ったようには動かない場合があります。タクトスイッチを押してもLEDの状態が変わらない場合です。これは、スイッチを使うときに起きるチャタリング(chattering)という現象が原因です。英語だとchatteringよりもbouncingと表現することが多いです。
スイッチは、金属板に金属板を押し付けることによって電線を連結します。この時、非常に短い時間で金属板同士が弾きあいます。結果として、以下の図で示すような現象が起こります。この図はプルダウン回路ではスイッチが押されたとき、プルアップ回路ではスイッチが離された時の様子です。
閾値(しきいち)は、この値以上あるいは以下でHIGHやLOWを認識するという境界です。この図で示すとおり、人間が1回だけスイッチを押したつもりでも、複数回のHIGHとLOWが起こります。この結果、人が意図しないスイッチのオンオフが起こってしまいます。そのために、先ほどのプログラムは、意図通りに動かないときがあるのです。
チャタリングはスイッチを押した時にも、離した時も起こります。スイッチによっても違いますが、授業で配布したタクトスイッチの場合、離した時に起こることの方が多いように思います。※まったく起こらない場合もあります。
チャタリングは、ハードウェアの工夫でも、ソフトウェアの工夫でも防ぐことができます。実際の工業製品の場合には、ハードとソフトの工夫を組み合わせることが多いと思います。この講義では、ソフトウェアの工夫のみを行います。ただし、ソフトウェアの工夫のみでチャタリングを完全に除去することはなかなか難しいです。
Pythonプログラムでタイマーを使う
チャタリングに対するソフトウェアの対策にも使えるのですが、Pythonプログラムでタイマーを使いたい場合は pref_counter() という関数呼び出しを使うことができます。 pref_counter() は、このプログラムが起動された時からの経過時間を秒で返します。秒よりも小さい単位は小数点以下の数字で表されます(ミリ秒やマイクロ秒)。
実際には、以下のライブラリの読み込みをプログラムの冒頭に追加する必要があります。
from time import perf_counter
デジタル入力は、端子に一定以上の電圧がかかっているか、かかっていないかという2状態を入力として識別することでした。アナログ入力は、端子にかかる電圧の変化を連続的な値として識別します。
例えば部屋の明るさは、太陽の位置等によって滑らかに変化していきます。光センサーは、その変化を電圧の変化に変換してマイコンに伝えます。
しかし、マイコンの中では、受け取った電圧の値をデジタルデータとして保持することになります。音楽データも、写真データも、動画データも、コンピュータの中では全てデジタルデータですよね。それと同じことです。つまり、アナログからデジタルへの変換が必要になるということです。マイコンを使用するときにそれを行ってくれるのが、 Analog to Digital Convertor (ADC) と呼ばれるデバイスです。一般的には、A/Dコンバータと呼ばれます。
MCP3008
しかし、 Raspberry Pi に搭載されているマイコンにはA/Dコンバータが内蔵されていません。そこで登場するのがMCP3008というICです。
今回配付するICの表面をよく読むと、MCP3008と書いてあると思います。MCP3008は10ビット8チャンネルのA/Dコンバータです。10ビットということは、2の10乗の解像度を持ちますので、1024段階の大きさの分類ができるということです。8チャンネルとは、8つの別々の入力を受け付けることができるということです。
2列合計16本の足がついていますが、この2列の足をブレッドボードの中央の溝をはさんで両側にくるようにブレッドボードに差し込むと、使い勝手がよいです。ちょうど下図のような具合です。
MCP3008のピンの役割は下図のようになっています。CH0-CH7と印字されているピンがアナログ入力に使用できます。これらのピンはセンサの回路に組み込んで使います。今回はCH0のピンだけを使います。それ以外のピンは、 Raspberry Pi のGPIOにそれぞれつなぎます。
MCP3008のVDDとVREFは Raspberry Pi の3.3Vに、
MCP3008のDGNDとAGNDは Raspberry Pi のGNDに、
MCP3008のCLKは Raspberry Pi のSCLK(23番)に、
MCP3008のCS/SHDNは Raspberry Pi のCE0(24番)に、
MCP3008のDoutは Raspberry Pi のMISO(21番)に、
MCP3008のDinは Raspberry Pi のMOSI(19番)に、
それぞれつないでください。ブレッドボードをうまく使います。 Raspberry Pi の SCLK, CE0, MISO, MOSI のピンについては、GPIOピンの図を参考にしてください。
なんだかつなぎかたが複雑ですが、これはSPI通信という規格を使うときに毎度出てくるつなぎかたです。 SCLK, CE0, MISO, MOSI の4つの線でマイコンと外部デバイス(今回はA/Dコンバータ)の通信を行います。
MCP3008を使うためのライブラリ
以下の場所にライブラリの説明があります。MCP3008は GPIO Zero ライブラリの標準で使えるデバイスで、その名もMCP3008クラスというのがあります。
https://gpiozero.readthedocs.io/en/stable/api_spi.html#mcp3008
以下のサンプルプログラムを動かすと、MCP3008のCH0の電圧の値を読むことができます。読んだ値は、コンソールに出力しています。
from gpiozero import MCP3008
from time import sleep
sensor = MCP3008(0)
def main_loop():
while True:
value = sensor.value
print(value)
sleep(0.1)
if __name__ == '__main__':
main_loop()
MCP3008(0)の引数の0がチャンネルの指定で、1と指定するとCH1のピン、2と指定するとCH2のピン、3と指定するとCH3のピンの電圧を読むことができます。SPI通信は、GPIOの特殊なピン(いつも固定のピン)を使うので、GPIOのピン番号を指定する必要はありません。
MCP3008のオブジェクト(ここでは変数sensorに代入されている)のvalueの値に、現在のピンの電圧に応じた値が入ります。プログラム中では、sensor.valueというようにすると、値が取得できるということです。値は、0Vから3.3Vのスケールに応じて、0.0から1.0の値で取得されます。実際の電圧値を知るには、もうひと計算必要だということです。
SPI通信を使うための準備
MCP3008は、 Raspberry Pi のマイコンとSPIという規格を使って通信します。 Raspberry Pi でSPIを利用可能とするには、1度だけ以下の設定を行う必要があります。
raspi-configというシステム設定ツールを立ち上げる。
$ sudo raspi-config
矢印キーで 3 Interfacing Options を選び、エンターキーを押す。
P4 SPI を選びエンターキーを押す。
Yesを選びエンターキーを押す。
Finishを選びエンターキーを押す。
Raspberry Piを再起動する。
光センサ
光を感知するセンサは、光センサ、あるいはフォトレジスタ(Photoresistor)と呼ばれます。
光センサには色々なタイプがありますが、ここではCdS(Cadmium Sulfide)セルを使った光センサを扱います。CdSセルとは、硫化カドミウムと呼ばれる物質を応用したセンサで、光の強弱によって抵抗値が変わります。
ここではCdSセルを使った光センサを単にCdSと訳して呼びます。CdSは以下に示すような見た目をしています。CdSは抵抗器の一種で、極性はありません。
CdSは以下に示す回路図のような使い方をします。抵抗の分圧の法則が成り立つ回路になっているのがわかりますでしょうか。
CdSの抵抗値は受ける光の強さによって変化しますから、もう1つ抵抗を使って可変抵抗の時と同じ状況を作ります。CdSは、強い光を受けるほどに抵抗値が小さくなっていきます。つまり、この回路ではCdSが受ける光が強くなると、アナログピンにかかる電圧が高くなります。CdSと抵抗の位置を逆にすると、光が強くなるほどアナログピンにかかる電圧は低くなります。
実習1 いろいろな抵抗の実際の値を測定する
手持ちの抵抗器のうち、値の違う3種類を用意して、マルチメータで実際に抵抗の値を測りましょう。手持ちの抵抗器がない人は、配りますので教員に聞いてください。
測った結果を実験レポートで報告してください。カラーコードの値とは少し違った値になることもあります。誤差があるということです。近い値(正確にはカラーコードの誤差の範囲)であればOKです。
実習2 いろいろな抵抗を使ってLEDを光らせる
GPIOの2番から5Vがとれます。これを使ってLEDを光らせましょう。この実習でも抵抗は実習1と同じ3種類を試してください。
それぞれの抵抗のときに、LEDに何mAの電流が流れているのかを計算してください。結果には、回路図(手書きでよい・ただし読みやすいように太字ペンや照明の調整を使いましょう)、電流の計算、光らせたときの写真を含めてください。
実習3 スイッチでLEDをオンオフ
タクトスイッチを押すたびに、LEDが点いたり消えたりオンオフが変化するようにします。動作させてみると、チャタリングが発生するかもしれません。よく観察して、発生しているのかしていないのかをレポートしてください。
発生した場合は、どのようにすれば解決できるのかも考えてみてください。チャタリングはスイッチのハードウェアに依存して発生するものですが、ソフトウェアによって対策をとることも可能です。もし実装することができたり、実装できなくても解決策を考えることができたら、レポートでアピールしてください。
実習4 スイッチで点滅パターンを最初から
まず、自分が好きだと感じる点滅パターンを自由に作成してください。単純な点滅の繰り返しだけでなくより複雑な光りかたを、「生き物みたいな感じ」「金属探知機みたいな感じ」「R2-D2みたいな感じ」など、何かテーマを決めて作成してください。細かい調整を少しするだけで、大きく心地よさが変化する場合もあるので、時間が許すならばこだわって作成してください。
次に、タクトスイッチが押されたら、LEDの点滅パターンは最初からやり直すというプログラムを作成してください。スイッチが押されたらすぐに最初からやり直すようにできるとよいです。(ヒント: delay() の呪縛から逃れよう / sleep() の呪縛から逃れよう)
実習5 光りかたを予測する
3.1節のサンプルプログラムを少し変更した以下のプログラムを実行するとどうなるでしょうか。光りかたの予想と結果をまとめたうえで、なぜそうなるのかを考察してください。
from gpiozero import LED
from time import sleep
led = LED(17)
def main_loop():
while True:
led.off()
sleep(0.01)
led.on()
sleep(0.01)
if __name__ == '__main__':
main_loop()
なぜそうなるのかについては、そうなるということの証拠がつかめる場合は証拠を示してレポートで論じ、証拠がつかめない場合は推測でよいので必ず考えたことを書いてください。グループメンバーなどと相談しあってもOKですが、証拠や推測は必ずそれぞれがつかんでください。
実習6 タクトスイッチで調光する
タクトスイッチ1個とLED1個を使います。タクトスイッチを押すたびにLEDの明るさが5段階に変化するようにします。LEDの明るさの変更はPWMのデューティ比を変更させることで行います。
5段階の変化は、最も単純には「スイッチを押すたびに明るくなっていき、最も明るいときにスイッチを押すと最も暗い状態に戻る。」というようにすればOKですが、「明るくなっていく→暗くなっていく→明るくなっていく→暗くなっていく→...を繰り返す」など、条件にオリジナリティを出せるとよりよいです。