概要
Raspberry Piで8×8のドットマトリックスLEDを表示させてみた。
- 2つのシフトレジスターを使って、任意のパターンを表示させるようにした
- パターンを周期的に流れるように表示させた
ドットマトリックスLEDの仕様
キットに同梱されていたドットマトリックスLEDはピン配置や極性の手掛かりになる表示などがなかったので、1つずつ通電しながらピン配置と点灯の確認を行った。
内部の接続は以下のようになっている。RmをHIGHに、CnをLOWにすると、m行目n列目のLEDが点灯する。
回路図
キットのマニュアルの回路は2つのシフトレジスター74HC595を使っていたが、それらをカスケードに接続しているため、行と列のパターンを同時にしか設定できない。
そこで、2つのシフトレジスターを切り離して独立して制御するようにした。1行ずつスキャンしながら行ごとの点灯パターンを設定し、8×8の64個のLEDを任意のパターンで点灯させることができる。
ブレッドボードで回路を組んだら、かなり配線がごちゃごちゃになった。LEDのRとCから8本ずつのケーブルが出て、保護抵抗も8本使っているので仕方がないか。LEDのサイズがそれなりに大きいので、ブレッドボードの穴が覆われてしまう。
表示は平仮名の「お」で、後述のサイネージコードで「おはよう」と流して表示しているときの一部。
コード例
試験点灯
まず、8×8のドットパターンを2秒間だけ表示させてみる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 |
import RPi.GPIO as GPIO import time import threading import lib.led_dotmatrix_data as data # Set GPIO pin number in BCM SER_R = 12 SER_C = 23 SRCLK_R = 20 SRCLK_C = 24 RCLK = 16 SRCLR = 21 ROWS = [0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01] # Initialize all pins def initialize(): GPIO.setmode(GPIO.BCM) GPIO.setup(SER_R, GPIO.OUT, initial=GPIO.LOW) GPIO.setup(SER_C, GPIO.OUT, initial=GPIO.LOW) GPIO.setup(SRCLK_R, GPIO.OUT, initial=GPIO.LOW) GPIO.setup(SRCLK_C, GPIO.OUT, initial=GPIO.LOW) GPIO.setup(RCLK, GPIO.OUT, initial=GPIO.LOW) GPIO.setup(SRCLR, GPIO.OUT, initial=GPIO.HIGH) # Input a bit to shift register def shift(ser, serclk, level): GPIO.output(ser, level) GPIO.output(serclk, GPIO.HIGH) GPIO.output(serclk, GPIO.LOW) # Read value in the storage register def read(): GPIO.output(RCLK, GPIO.HIGH) GPIO.output(RCLK, GPIO.LOW) # Clear all the LEDs of dotmatrix LED def clear(): for i in range(8): shift(SER_R, SRCLK_R, GPIO.LOW) shift(SER_C, SRCLK_C, GPIO.LOW) read() # Display pattern to dotmatrix LED during sleeptime def display(pattern, sleeptime): for row, colpattern in zip(ROWS, pattern): for i in range(8): shift(SER_R, SRCLK_R, (row >> i) & 1) for i in range(8): shift(SER_C, SRCLK_C, not((colpattern >> i) & 1)) read() time.sleep(sleeptime) # Ending execution def destroy(): clear() GPIO.cleanup() # Class inherited from Thread to display dotmatrix LED class DisplayThread(threading.Thread): def __init__(self,bit_pattern): threading.Thread.__init__(self) self.is_running = True self.bit_pattern = bit_pattern def run(self): while self.is_running: display(self.bit_pattern, 0.001) def stop(self): self.is_running = False # Main process initialize() clear() t = DisplayThread(data.get_bit_pattern(data.SMILE)) t.start() time.sleep(2.0) t.stop() time.sleep(0.01) # <- Without this line, runtime error occurs requiring setmode destroy() |
ドットパターンはサブディレクトリーlib
の下にled_dotmatrix_data.py
として以下のモジュールを準備した。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
# This module is imported by led_dotmatrix control programs. # Function to convert 8x8 string pattern to bit pattern def get_bit_pattern(string_array): pattern = [] for str in string_array: bin = 0 for n in range(8): digit = 0 if str[n] == ' ' else 1 bin = bin | (digit << (7 - n)) pattern.append(bin) return pattern SMILE = [ ' **** ', ' * * ', '* * * *', '* *', '* * * *', '* ** *', ' * * ', ' **** ', ] |
パターンをスペースとスペース以外の文字(ここでは*
)を使って準備し、get_bit_pattern
関数で8ビット×8行のリストとする。
最初のコードで、まずGPIOピンなどの定数を定義している。ROWS
はドットマトリックスの各行を単独で指定する際に、シフトレジスターに与えるビットパターン。
1 2 3 4 5 6 7 8 |
# Set GPIO pin number in BCM SER_R = 12 SER_C = 23 SRCLK_R = 20 SRCLK_C = 24 RCLK = 16 SRCLR = 21 ROWS = [0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01] |
shift
関数は、SERとSRCLK、HIGH/LOWのレベルを指定してシフトレジスターを1回シフトする。
1 2 3 4 5 |
# Input a bit to shift register def shift(ser, serclk, level): GPIO.output(ser, level) GPIO.output(serclk, GPIO.HIGH) GPIO.output(serclk, GPIO.LOW) |
read
関数は、RCLKをHIGHにしてストレージレジスタ―の出力を取り出し、LOWに戻す。
1 2 3 4 |
# Read value in the storage register def read(): GPIO.output(RCLK, GPIO.HIGH) GPIO.output(RCLK, GPIO.LOW) |
clear
関数はドットマトリックスのR1~R8をLOWにしてLEDを消灯させる。念のためC1~C8もLOWにしている。
1 2 3 4 5 6 |
# Clear all the LEDs of dotmatrix LED def clear(): for i in range(8): shift(SER_R, SRCLK_R, GPIO.LOW) shift(SER_C, SRCLK_C, GPIO.LOW) read() |
display
関数は、8ビット×8行のビットパターンを与えてsleepの秒数だけLEDを点灯させる。
1 2 3 4 5 6 7 8 9 |
# Display pattern to dotmatrix LED during sleeptime def display(pattern, sleeptime): for row, colpattern in zip(ROWS, pattern): for i in range(8): shift(SER_R, SRCLK_R, (row >> i) & 1) for i in range(8): shift(SER_C, SRCLK_C, not((colpattern >> i) & 1)) read() time.sleep(sleeptime) |
DisplayThread
はビットパターンを与えて生成し、start
メソッドを呼ぶと内部でrun
メソッドが実行される。stop
メソッドによりスレッドが停止する。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
# Class inherited from Thread to display dotmatrix LED class DisplayThread(threading.Thread): def __init__(self, bit_pattern): threading.Thread.__init__(self) self.is_running = True self.bit_pattern = bit_pattern def run(self): while self.is_running: display(self.bit_pattern, 0.001) def stop(self): self.is_running = False |
試験点灯用のコードは以下の通りで、表示パターンを与えてスレッドを生成し、2秒間点灯させた後にスレッドを停止させている。
ここで、stop
メソッドの後に僅かな時間の間をおかないと、「setmode
が必要」というランタイムエラーが出る。destroy
メソッドでcleanup
を実行するタイミングがstop
で表示が停止するよりも早いからかもしれない。
1 2 3 4 5 6 7 8 9 |
initialize() clear() t = DisplayThread(data.get_bit_pattern(data.SMILE)) t.start() time.sleep(2.0) t.stop() time.sleep(0.01) # <- Without this line, runtime error occurs requiring setmode destroy() |
CTRL-Cでの表示停止
次に、実行後表示を続けてCTRL-Cのキーボード入力で停止するようにした。
このとき、キーボード割込を待ち受けるwhile
ループの内容を単にpass
とすると目に見えるくらいのちらつきが生じたため、sleep
を入れている。
1 2 3 4 5 6 7 8 9 10 |
try: t = DisplayThread(data.get_bit_pattern(data.SMILE)) t.start() while True: time.sleep(1) # <- Flicks when this line is 'pass' rather than this except KeyboardInterrupt: print('STOP') t.stop() time.sleep(0.1) destroy() |
アニメーション表示
2つのパターンを準備して、一定時間で交互に表示を切り替えてみた。
まず、led_dotmatrix_data
モジュールで準備した表示パターンは以下の通り。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
# Define two patterns lists to be displayed alternately SMILE = [ ' **** ', ' * * ', '* * * *', '* *', '* * * *', '* ** *', ' * * ', ' **** ', ] WINK = [ ' **** ', ' * * ', '* * *', '* *', '* * * *', '* ** *', ' * * ', ' **** ', ] |
2つのパターンを交互に切り替える実行部分は以下の通り。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
try: # Continue to display SMILE pattern and WINK pattern alternately while True: t = DisplayThread(data.get_bit_pattern(data.SMILE)) t.start() time.sleep(1.0) t.stop() t = DisplayThread(data.get_bit_pattern(data.WINK)) t.start() time.sleep(1.0) t.stop() except KeyboardInterrupt: # Before cleanup, it's needed to (1)stop the thread, (2)wait a bit. # Otherwise, runtime error occurs requiring setmode. t.stop() time.sleep(0.1) destroy() |
流れる表示
8×8のパターンを横に流れるように表示してみた。led_dotmatrix_data
モジュールに以下を追加。
get_cycle_pattern
関数は、8×8のパターンの表示開始位置を与えてサイクリックに表示をずらすためのパターンを返す関数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
# Retrieve 8x8 pattern from source list cyclically. # Length of source list can be 8 or more def get_cycle_pattern(org_pattern, start_col): pattern = [] for i in range(8): n = start_col bin = 0 for j in range(8): digit = 0 if org_pattern[i][n] == ' ' else 1 bin = bin | (digit << (7 - j)) n = n + 1 if n + 1 < len(org_pattern[i]) else 0 pattern.append(bin) return pattern # Dfine 8x8 pattern to be displayed in horizontal flow. FLOW = [ ' ** ', ' * * ', '* * ', '* * ', ' * *', ' * *', ' * * ', ' ** ', ] |
以下は、この関数を使って流れる表示をさせるコード。データのリストの最後まで来たら、表示させる桁数を先頭に戻している。この結果、正弦波のような波が流れて表示される。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
try: # Continue to display SMILE pattern and WINK pattern alternately col = 0 while True: t = DisplayThread(data.get_cycle_pattern(data.FLOW, col)) t.start() time.sleep(0.05) t.stop() col = col + 1 if col + 1 < len(data.FLOW[0]) else 0 except KeyboardInterrupt: # Before cleanup, it's needed to (1)stop the thread, (2)wait a bit. # Otherwise, runtime error occurs requiring setmode. t.stop() time.sleep(0.1) destroy() |
サイネージ
複数の文字が横に流れるように表示させるようにしてみた。まず、led_dotmatrix_data
モジュールに以下の関数とパターンを追加。
combine_patterns
関数は複数のパターンを引数にとり、それらを横に連ねた2次元リストを返す。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
# Combine more than one patterns into a 2D list with 8 rows. def combine_patterns(*patterns): result_pattern = [''] * 8 for p in patterns: for r in range(8): result_pattern[r] += p[r] return result_pattern # Define patterns for sinage 'OHAYO' in KANA pko = [ ' * ', ' *** ', ' * ** ', ' * * ', ' ***** ', '* * * ', '* * * ', ' ** * ', ] pkha = [ ' * * ', '* ***** ', '* * ', '* * ', '* * ', '* *** ', '*** * * ', '* * * ', ] pkyo = [ ' * ', ' **** ', ' * ', ' * ', ' * ', ' **** ', '* * * ', ' ** * ', ] pku =[ ' *** ', ' ', ' ***** ', '* * ', ' * ', ' * ', ' * ', ' * ', ] |
実行部分は以下の通りで、複数の文字パターンを関数で1つの2次元リストにまとめ、それを上の流れる表示と同じように処理している。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
#message = data.combine_patterns(data.ph, data.pe, data.pl, data.pl, data.po) message = data.combine_patterns(data.pko, data.pkha, data.pkyo, data.pku) initialize() clear() try: # Continue to display SMILE pattern and WINK pattern alternately col = 0 while True: t = DisplayThread(data.get_cycle_pattern(message, col)) t.start() time.sleep(0.1) t.stop() col = col + 1 if col + 1 < len(message[0]) else 0 except KeyboardInterrupt: # Before cleanup, it's needed to (1)stop the thread, (2)wait a bit. # Otherwise, runtime error occurs requiring setmode. t.stop() time.sleep(0.1) destroy() |