ラズパイで初めてデバイス制御を学習するときに使用されるのがLEDです。LEDの基本的な説明(端子、VF、電流制限抵抗)をしたあと、7セグメントLEDやドットマトリクスLEDを制御するためのプログラミング方法について紹介します。
発光ダイオード(LED)
現在では、LEDは私たちの日常生活に欠かせないものになりました。1960年代にはじめての製品が発売されて以来、輝度(明るさ)とエネルギー効率は改良改善が続けられています。
LEDは基本的に電気回路上での振る舞いは「光らない」通常のダイオード(整流ダイオード)と同じような特性を持ちます。ダイオードを製造するときどんな材料を使うかによって光るか光らないか、光るとしてどのような色に光るかが決まります。
LEDを使った回路を設計する際には、LEDに印可する電圧がLEDの仕様に合った範囲に設定されているか確認する必要があります。(「順方向電圧降下」「VF」と表記されます)
通常は1.5~3.7V程度の範囲になります。この値は使用するLEDのデータシートから読み取って使用します。
厳密には部品によって異なりますが、一般的におおむね次のような値になります。
- 赤外線LED: 1.5V
- 赤色LED: 1.6V
- 黄色LED: 2.2V
- 緑色LED: 2.1V
- 青色LED: 2.9V
- 白色LED: 4V
LEDには流してよい最大の電流値「定格電流」が決まっていて、これを超える電流を流すとLEDは簡単に壊れてしまいます。
LEDを点灯させる際には、流れる電流がこの「定格電流」以下になるように直列に抵抗を配線する必要があります。この「定格電流」も、データシートに記載されています。
抵抗値を決める数式は、
$$R_{v}=\frac{V-V_{F}}{I}$$
$R_{v}$が直列に入れる抵抗の値(単位は[Ω])、$V$は電源の電圧、$V_{F}$はLEDに印可する電圧、$I$はLEDに流したい電流(単位は[A])を表します。
例えば、Raspberry PiのGPIOピン(電圧3.3V)を電源として、定格2.1Vの緑色のLEDを20mAの電流で点灯させたい場合、次のような計算から抵抗値を求めます。
$$R_{v}=\frac{V-V_{F}}{I}=\frac{3.3[V]-2.1[V]}{0.02[A]}=60[\Omega]$$
この計算から、60Ωの抵抗器を直列に入れる必要があるとわかりました。
しかし「60Ωの抵抗器」というものは、おそらく手に入らないはずです。お店などで探しても、「56Ω」の次が「68Ω」など、とびとびの数値しか作られていないからです。
このような場合には、計算で出てきた抵抗値より次に大きな抵抗器を使用します。
上の例で言えば「68Ω」ですね。
この抵抗器はLEDと直列につながってさえいればLEDのアノード側[1]につないでも、カソード側につないでも、どちらでも構いません。
アノードについて補足します。
LEDには2つの電極がありますが、電球などと異なりプラスマイナスが決まっています。アノードと呼ばれる電極をプラス極(電源やGPIOピン)に、カソードと呼ばれる電極をマイナス極(GND)に接続すると、LEDは光ります。逆につなぐと光らないばかりでなく、LEDが壊れてしまう場合もあります。
この抵抗値が低すぎるとLEDに大きな電流が流れてLEDが壊れてしまう危険性があります。逆に高すぎるとLEDに十分な電流が流れず、光が弱くなってしまいます。
フルカラーLED(RGB LED)
光の三原色である「赤」「緑」「青」の3つのLEDを内蔵したフルカラーLEDというものもあり、それぞれの明るさを調整することで様々な色の光を作ることができます。フルカラーLEDの中には直列抵抗や制御用の小さなICが内蔵されており回路の作成や制御をより簡単に行えるモジュールもあります。
7セグメントLED
7セグメントLEDは、複数のLEDを内蔵して1桁の数字を表示するコンポーネントです。
同じような表示装置としてアルファベットも表示できる14セグメントLEDや7セグメントLEDを複数個連結して2~4桁の数値を表示できるもの、4桁でも時計表示に特化したものなどがあります。
このコンポーネントは、内蔵したLEDのアノードを共通にする「アノードコモン」か、カソードを共通にする「カソードコモン」かによって大きく2種類に分けられます。いずれも背面に10本のピンがあり、うち2本が共通のアノード/カソード極、7本が数字を構成するそれぞれのLEDの電極、残り1本が小数点用の電極です。
単体のLEDと同様に、LEDに印可する電圧と流れる電流の調整のための抵抗を直列に取り付ける必要があります。
特に大型の7セグメントLEDの場合、小数点がほかのセグメントよりも小さいことから、明るさをそろえるために小数点とそれ以外とで異なる抵抗値を使用する場合もあります。
図4の例では、カソードコモンの7セグメントLEDを使用しています。
共通となっているカソードをGNDピンに接続、各セグメントのアノードは抵抗を通して各GPIOピンに接続します。
次のpythonコードを実行すると、0から9までの数字が0.5秒間ずつ表示されます。簡単にするために小数点は含んでいません。
import RPi.GPIO as GPIO import time GPIO.setmode(GPIO.BCM) GPIO.setup(23, GPIO.OUT) GPIO.setup(24, GPIO.OUT) GPIO.setup(25, GPIO.OUT) GPIO.setup(12, GPIO.OUT) GPIO.setup(16, GPIO.OUT) GPIO.setup(20, GPIO.OUT) GPIO.setup(21, GPIO.OUT) z_0 = [1,1,1,0,1,1,1] z_1 = [1,0,0,0,1,0,0] z_2 = [1,1,0,1,0,1,1] z_3 = [1,1,0,1,1,1,0] z_4 = [1,0,1,1,1,0,0] z_5 = [0,1,1,1,1,1,0] z_6 = [0,1,1,1,1,1,1] z_7 = [1,1,0,0,1,0,0] z_8 = [1,1,1,1,1,1,1] z_9 = [1,1,1,1,1,0,0] numbers = [z_0,z_1,z_2,z_3,z_4,z_5,z_6,z_7,z_8,z_9] def set_segment(s_or,s_om,s_ol,s_mm,s_ur,s_um,s_ul): GPIO.output(23, GPIO.HIGH if s_or==1 else GPIO.LOW) GPIO.output(24, GPIO.HIGH if s_om==1 else GPIO.LOW) GPIO.output(25, GPIO.HIGH if s_ol==1 else GPIO.LOW) GPIO.output(12, GPIO.HIGH if s_mm==1 else GPIO.LOW) GPIO.output(16, GPIO.HIGH if s_ur==1 else GPIO.LOW) GPIO.output(20, GPIO.HIGH if s_um==1 else GPIO.LOW) GPIO.output(21, GPIO.HIGH if s_ul==1 else GPIO.LOW) for number in numbers: set_segment(*number) time.sleep(0.5) GPIO.cleanup()
プログラムの核となるのはset_segment()関数で、7セグメントLEDの各LEDに対応した7つの引数があり、引数によって対応するGPIOのHIGH/LOWを切り替えます。
リストz_0からz_9には、表示したい各数字に対してどのセグメントを点灯させるか・消灯させるかという情報が含まれています。これにより、z_0などのリストを読みだすことで簡単に点灯パターンを設定することができます。
ループ内でのset.segments(*number)の呼び出しについて、注意点があります。引数numberの前の「*」を忘れないでください。アスタリスクをつけることで、リスト内の要素の一つ一つを引数として渡せるようになります。
ループ終了後、GPIO.cleanup()命令を実行してGPIO端子を開放して終了します。
ドットマトリックスLED
7セグメントLEDと同様にドットマトリックスLEDも、多くのLEDで構成されています。図5に写真を示します。背面にピンは8本ずつ2列しかありません。
表面には8×8=64個のLEDが内蔵されているので、LEDを個別に制御するのは不可能に思えます。どのような仕組みになっているかというと、LEDがマトリックス(格子)状に配置されていて、2つのピンの列がマトリックスの行と列に対応している、というものです。
例えば、上から2列目のピンを電源プラス端子に、左から3列目のピンを電源マイナス端子に接続すると、それらの交点にあるLEDが点灯する、という具合です。
しかし、これではすべてのLEDを個別のON/OFFすることはできません。
いくつかのピンをプラスやマイナスに接続すると、「すべての交差点」のLEDが点灯してしまいます。点灯してほしくない場所も点灯してしまうわけです。
これを防ぐために一度に点灯させる行を1行だけに限定して、その行を目的のLEDだけが点灯するように順番に切り替えていくという制御を行います。この制御をダイナミック制御といいます。一見正しく表示できない気がしますが、行の切り替えをある程度高速で行えば人間の目の錯覚(残像)ですべての行が点灯しているように見えます。
Raspberry Piなどで直接制御するほかにも、「シフトレジスタ」と呼ばれるICを使うことで実装することができます。
ドットマトリックスLEDもLEDであるため、動作電圧・電流に注意が必要です。データシートを見て適切な抵抗器を挟む必要があります。抵抗器は各列(タテのライン)に対して設置します。行に対して設置してしまうと、同じ行のLEDが複数点灯したときに、LEDが暗くなってしまいます。
実際に試してみましょう。
各行と列を個別にRaspberry PiのGPIOにつないでも制御できますが、多くのGPIOピンが必要になります。そこで、先に述べたようなICを使用することにします。
ここで使用するLEDドライバIC「MAX7219」は最大64個[2]のLEDを個別に制御できるICで、ドットマトリックスLEDと一体になったモジュールも市販されています。
ここではMAX7219と一体化したドットマトリックスLEDモジュールを使います。図6のように配線します。
図8中の赤い部品は「論理レベル変換器」というモジュールです。一方向のみ変換できるものと、双方向に変換できるものがあります。この例では一方向のみのもので良いですが、その時々で適切なものを選ぶ必要があります。
Raspberry PiのGPIOは最大3.3Vの電圧で動作します。一方で、MAX7219は5Vが入力されたときにHIGHと判断します。これらを直接接続した場合、Raspberry PiがGPIOにHIGHを出力しているのに、電圧が低すぎてMAX7219がHIGHとして認識できない、という事態が発生します。また、今回のケースでは起こりませんが、Raspberry PiのGPIOピンに5Vの信号を直接入力してしまうと過電圧で壊れてしまうこともあります。
こういった場合に、論理レベル変換器を間に挟むことで信号の電圧を上げたり下げたりして、お互いに問題なく読み取ることのできるレベルに変換してくれます。
各信号の接続リストを作成しました。次の表のように接続してください。
名前 | Raspberry Pi | 論理レベル変換器 | ドットマトリックスLED |
5V | 5V | – | VCC |
GND | GND | – | GND |
Data | GPIO 10/MOSI | 3.3V ⇔ 5V | Din |
Chip Select | GPIO 8/SPI CE0 | 3.3V ⇔ 5V | CS |
Clock | GPIO 11/SCLK | 3.3V ⇔ 5V | CLK |
この後のサンプルコードを実行するための準備が必要です。
まず、Raspberry Pi のSPIインターフェースを使えるようにします。
設定は「raspi-config」ツールを使用して行います。コンソールで「sudo raspi-config」を実行すると起動できます。
「5 Interface Options」>「P4 SPI」を選択し、「Yes」を選択します。これによってSPIインターフェースが有効になります。他に使用する場合でSPIインターフェースをオフにしたい場合は「No」を選択すれば無効になります。
次に、必要なpythonライブラリ「spidev」と「Luma.LED_Matrix」をコンソール画面からインストールします。
pip3 install spidev
pip3 install Luma.LED_Matrix
ライブラリがすでにインストールされている場合はコンソール画面にそのように表示されます。そのまま進めてください。
これらの準備ができたら、次のコードを使ってドットマトリックスLEDに文字を出力します。
from luma.core.interface.serial import spi, noop from luma.core.render import canvas from luma.led_matrix.device import max7219 from luma.core.legacy import text from luma.core.legacy.font import proportional,CP437_FONT,LCD_FONT serial = spi(port=0,device=0,gpio=noop()) device = max7219(serial) with canvas(device) as draw: text(draw,(0,0),"B",fill="white",font=proportional(CP437_FONT))
コメントを残す