今回はI2C接続の16x2有機ELキャラクターディスプレイSO1602AWWB-UC-WBを使ってみます。
秋月電子で1580(税込み)でした。
有機ELキャラクタディスプレイモジュール 16×2行 白色 | 秋月電子通商
■ ピン
- VSS: ラズパイのGND端子に接続
電源グランド - VDD: ラズパイの3.3V端子に接続
電源 - /CS: VSSに接続
- SA0: GNDもしくはVDDに接続
I2Cスレーブアドレス。この端子がLOWなら0x3C
、 HIGHなら0x3D
※ 今回はLOWに接続 - 接続しない
- 接続しない
- SCL: ラズパイのSCL端子に接続
I2Cクロック入力 - SDA in: ラズパイのSDAに接続
I2Cのデータ入力 - SDA out: ラズパイのSDAに接続
I2Cのデータ出力 - 以降は接続しない
■ データの送信
データの送信は「コントロールバイト」と「データバイト」の2つを送信します。
「コントロールバイト」は送信するデータのメタデータで、「データバイト」が送信するデータの本体です。
「コントロールバイト」には以下のフラグをもたせます。
- bit 7
Co
: データバイトが複数の場合は1、1組のみの場合は0を設定 - bit6
D/C#
: データバイトがコマンドの場合は0
、 データの場合は1
※ 基本的にデータバイトを複数送ることはしないので、コマンドの場合は 0x00
、データ書込みの場合は 0x40
をコントロールバイトに指定することになります。
■ 利用できる文字
SO1602AWでは下記の文字が利用できます。
「データバイト」に文字に対応するアドレスを格納して、SO1602AWに送信することで液晶に表示されます。
※ 例えば a
は 0b01000001
(0x41) 、ア
は 0b10110001
(0xB1) となります。
■ 液晶表示 DDRAMアドレス
液晶の各セルにはアドレスが紐付けられています。 (これをDDRAMアドレスと呼ぶようです)
SO1602AWではカーソルという概念があり、このカーソルを任意のDDRAMアドレスに移動して表示したい文字を送信すると、指定したDDRAMアドレスに対応するセルに文字を表示することができます。
※ カーソルの移動は後述の「コマンド」で行います。
カーソルは1文字表示するごとに自動でインクリメントするので、表示ごとにいちいち設定し直す必要はありません。データを送信する前に表示開始位置(例えば 0x00
(1行目先頭))に設定しておけばOKです。
■ よく使うコマンド
SO1602AWはコマンドを利用してデバイスの動作設定を行います。
この設定が微妙にややこしいので、少し解説します。
このコマンドには、 IS
, RE
, SD
という3つのレジスタが存在し、このレジスタの状態で利用できるコマンドが異なります。
※ IS
, RE
, SD
はいずれも 0
か 1
を値に取ります。
めんどくさいことに、これら3つのレジスタを変更するコマンドは1つにまとまっておらず、レジスタを変更するコマンドは、レジスタの状態によって実行できたりできなかったりします。
※ 例えば SD
レジスタを設定するコマンドは IS=0, RE=1, SD=0
の状態でないと利用できません、、、 (何だこの仕様)
なので、コマンドは IS
, RE
, SD
の状態ごとに解説していきます。
IS=X, RE=X, SD=0 (Xは任意の値)
コマンド名 | bit 7 | bit 6 | bit 5 | bit 4 | bit 3 | bit 2 | bit 1 | bit 0 | 説明 |
---|---|---|---|---|---|---|---|---|---|
Clear Display | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | すべてのDDRAMに 0x20 (スペース) を書き込み、DDRAMアドレスを 0x00 (1行目先頭) に設定する |
IS=X, RE=0, SD=0
コマンド名 | bit 7 | bit 6 | bit 5 | bit 4 | bit 3 | bit 2 | bit 1 | bit 0 | 説明 |
---|---|---|---|---|---|---|---|---|---|
Return Home | 0 | 0 | 0 | 0 | 0 | 0 | 1 | * | DDRAMアドレスを 0x00 (1行目先頭) に設定する |
Entry Mode Set | 0 | 0 | 0 | 0 | 0 | 1 | I/D | S | データ書き込み時のカーソルの移動方向の設定 |
Display ON/OFF | 0 | 0 | 0 | 0 | 0 | D | C | B | ディスプレイ・カーソル・ブリンク(カーソルの点滅)のON/OFFの設定 |
Function Set | 0 | 0 | 1 | * | N | DH | RE(0) | IS |
RE を 0 に、 IS を任意の値に設定できるコマンド。その他設定表示設定も兼ねている。 |
※ *
は任意の値
- Entry Mode Set
-
I/D
:
(1) カーソルが右に移動しDDRAMアドレスがインクリメント (default)
(0) カーソルが左に移動しDDRAMアドレスがデクリメント -
S
: ディスプレイ全体のシフト
(1) I/D=1ならディスプレイ全体が右にシフト。I/D=0なら左にシフト。
(0) ディスプレイのシフトを行わない (default)
-
- Display ON/OFF
-
D
: ディスプレイ
(1) ON
(0) OFF (default) -
C
: カーソル表示
(1) ON
(0) OFF (default) -
B
: ブリンク(カーソルの点滅)表示
(1) ON
(0) OFF (default)
-
- Function Set
- N: 表示行数制御
(1) 2行表示
(0) 1行表示 - DH: 2行高フォント制御
(1) 2行分の高さで1文字を表示 (DDRAMは0x00 - 0x27まで利用可能)
(0) 通常表示 - RE: 拡張命令用レジスタ
(0) 固定 - IS: 拡張命令用レジスタ
(1) 利用する
(0) 利用しない
- N: 表示行数制御
IS=0, RE=0, SD=0
コマンド名 | bit 7 | bit 6 | bit 5 | bit 4 | bit 3 | bit 2 | bit 1 | bit 0 | 説明 |
---|---|---|---|---|---|---|---|---|---|
Cursor or Display Shift | 0 | 0 | 0 | 1 | S/C | R/L | * | * | カーソル(もしくは画面)をどうシフトするかを設定 |
- Cursor or Display Shift
-
S/C
: 画面とカーソルどちらをシフトするかを選択
(1) 画面をシフト (文字が画面を流れるように表示される)
(0) カーソルをシフト -
R/L
: 左右どちらにシフトするかを選択
(1) 右にシフト
(0) 左にシフト
-
IS=0, RE=X, SD=0
コマンド名 | bit 7 | bit 6 | bit 5 | bit 4 | bit 3 | bit 2 | bit 1 | bit 0 | 説明 |
---|---|---|---|---|---|---|---|---|---|
Set DDRAM Address | 1 | AC6 | AC5 | AC4 | AC3 | AC2 | AC1 | AC0 | カーソルを指定したDDRAMアドレスに移動します。 |
Write Data | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 | (DDRAM/CGRAM) にデータを書き込む |
IS=0, RE=1, SD=0
コマンド名 | bit 7 | bit 6 | bit 5 | bit 4 | bit 3 | bit 2 | bit 1 | bit 0 | 説明 |
---|---|---|---|---|---|---|---|---|---|
Function Set | 0 | 0 | 1 | * | N | DH | RE(1) | REV |
RE を 1 に設定できるコマンド。その他設定表示設定も兼ねている。 |
OLED Characterization | 0 | 1 | 1 | 1 | 1 | 0 | 0 | SD |
SD を任意の値に設定できるコマンド |
Double Height(4-line) / Display-dot shift | 0 | 0 | 0 | 1 | UD2 | UD1 | * | DH' | 選択した行だけディスプレイシフトを行う設定の有効化・無効化 |
- Function Set
-
N
: 表示行数制御
(1) 2行表示
(0) 1行表示 -
DH
: 2行高フォント制御
(1) 2行分の高さで1文字を表示(5 x 16ドット) (DDRAMは0x00 - 0x27まで利用可能)
(0) 通常表示 (5 x 8ドット) -
RE
: 拡張命令用レジスタ
(1) 固定 -
REV
: 表示ドットの反転設定
(1) 反転する
(0) 反転しない
-
- OLED Characterization (IS=0, RE=1, SD=0)
-
SD
: 拡張命令用レジスタ
(1) 利用する
(0) 利用しない
-
- Double Height(4line)/Display-dot shift (IS=0, RE=1, SD=0)
-UD2,UD2
: あまり関係ないので11
(default)
-DH'
: 選択した行だけディスプレイシフトを行う設定の有効・無効
(1) 有効化 (ディスプレイシフト)
(0) 無効化 (スクロール) (default)
IS=1, RE=1, SD=0
コマンド名 | bit 7 | bit 6 | bit 5 | bit 4 | bit 3 | bit 2 | bit 1 | bit 0 | 説明 |
---|---|---|---|---|---|---|---|---|---|
Shift / Scroll Enable | 0 | 0 | 0 | 1 | DS4/HS4 | DS3/HS3 | DS2/HS2 | DS1/HS1 | 選択した行(1 ~ 4)のみを選択的にディスプレイシフトさせる設定 |
- Shift / Scroll Enable
- DS/HS:
-DS4/HS4 ~ DS1/HS1
:DH'=1
の場合は、行毎のディスプレイシフトを有効化。1111 (default)
※DH'=0
の場合は、行毎の水平スクロールを有効化。1111 (default)
実装
1. 固定表示してみる
画面をシフトさせずに固定の文字列を表示します
実際にテキストを表示させるコードは以下になります。基本的にはカーソルを動かして、テキストを書き込むだけですね。
def main(pi, i2c_handler):
"""
固定表示
"""
write_command(pi, i2c_handler, 0b00101000) # bit2 (1: Double height, 0: Single height) (Function Set)
# IS=0, RE=0, SD=0
l1 = b"Temp Pres Hum"
for char in l1:
write_data(pi, i2c_handler, char)
ddram_addr = 0b00100000 # 2行目の先頭 (0x20)
write_command(pi, i2c_handler, 0b10000000 | ddram_addr) # Set DDRAM RAM Address
l2 = b"24.34 1012 50.35"
for ch in l2:
write_data(pi, i2c_handler, ch)
全文
from time import sleep
import pigpio
def write_data(pi, i2c_handler, data: int):
control_byte = 0b01000000 # コントロールバイト: データ書込みは bit6=1
pi.i2c_write_device(i2c_handler, bytes([control_byte, data]))
sleep(0.001)
def write_command(pi, i2c_handler, command: int):
control_byte = 0b00000000 # コントロールバイト: コマンドは bit6=0
pi.i2c_write_device(i2c_handler, bytes([control_byte, command]))
sleep(0.05)
def init(pi, i2c_handler):
"""初期化処理"""
# Clear Display (IS=X, RE=X, SD=0)
# 全DDRAMに0x20を書き込み、DDRAMを0x00(1行目先頭)に設定
# 0 0 0 0 0 0 0 1
write_command(pi, i2c_handler, 0b00000001)
# Return Home (IS=X, RE=0, SD=0)
# DDRAMを0x00(1行目先頭)に設定
# 0 0 0 0 0 0 1 *
write_command(pi, i2c_handler, 0b00000010)
# Display ON/OFF Control
# ディスプレイ・カーソル・ブリンクのON/OFFを設定
# 0 0 0 0 1 D C B
# - D: ディスプレイ (1)
# - C: カーソル表示 (0)
# - B: ブリンク表示 (0)
write_command(pi, i2c_handler, 0b00001100)
# Entry Mode Set (IS=0, RE=0, SD=0)
# データ書き込み時のカーソルの移動方向の設定
# 0 0 0 0 0 1 I/D S
# - I/D: カーソルが右に移動しDDRAMアドレスがインクリメント (1)
# - S : ディスプレイ全体のシフトさせない (0)
write_command(pi, i2c_handler, 0b00000110) # シフト設定をデフォルト値に
# Function Set (IS=0, RE=1, SD=0)
# 機能設定
# 0 0 0 1 * N DH RE(0) REV
# - N: 2行表示 (1)
# - DH: 2行高表示を利用しない (0)
# - RE: (1)
# - REV: ディスプレイを反転表示しない (0)
write_command(pi, i2c_handler, 0b00101010) # IS=0, RE=1, SD=0
# OLED Characterization (IS=0, RE=1, SD=0)
# 0 1 1 1 1 0 0 SD
# - SD: (1)
write_command(pi, i2c_handler, 0b01111001) # IS=0, RE=1, SD=1
# Set Contrast Control
# コントラスト設定。
# 2バイトコマンドなので、このあとに輝度設定バイト(0x00 - 0xFF)を送信
# 1 0 0 0 0 0 0 1
write_command(pi, i2c_handler, 0b10000001) # コントラストセット
write_command(pi, i2c_handler, 0b11111111) # 輝度 max
# OLED Characterization (IS=0, RE=1, SD=0)
# 0 1 1 1 1 0 0 SD
# - SD: (0)
write_command(pi, i2c_handler, 0b01111000) # IS=0, RE=1, SD=0
# Function Set (IS=X, RE=0, SD=0)
# 機能設定
# 0 0 0 1 * N DH RE(0) IS
# - N: 2行表示 (1)
# - DH: 2行高表示を利用しない (0)
# - RE: (0)
# - IS: (0)
write_command(pi, i2c_handler, 0b00101000) # IS=0, RE=0, SD=0
def off(pi, i2c_handler):
"""終了処理"""
write_command(pi, i2c_handler, 0b01111000) # SD=1 (OLED Characterization)
write_command(pi, i2c_handler, 0b00101000) # RE=0, IS=0 (Function Set)
write_command(pi, i2c_handler, 0b00000001) # Clear Display
write_command(pi, i2c_handler, 0b00000010) # Return Home
write_command(pi, i2c_handler, 0b00001000) # Display, cursor, blink = OFF
def main(pi, i2c_handler):
"""
固定表示
"""
write_command(pi, i2c_handler, 0b00101000) # bit2 (1: Double height, 0: Single height) (Function Set)
# IS=0, RE=0, SD=0
l1 = b"Temp Pres Hum"
for char in l1:
write_data(pi, i2c_handler, char)
ddram_addr = 0b00100000 # 2行目の先頭 (0x20)
write_command(pi, i2c_handler, 0b10000000 | ddram_addr) # Set DDRAM RAM Address
l2 = b"24.34 1012 50.35"
for ch in l2:
write_data(pi, i2c_handler, ch)
if __name__ == "__main__":
pi = pigpio.pi()
if not pi.connected:
raise Exception("pigpio connection faild...")
i2c_bus = 1
i2c_address = 0x3C # SA0=L (SA0=Hの場合は0x3D) (i2cdetect 1コマンドで確認)
i2c_flags = 0x0
i2c_handler = pi.i2c_open(i2c_bus, i2c_address)
try:
init(pi, i2c_handler)
main(pi, i2c_handler)
sleep(10)
finally:
off(pi, i2c_handler)
sleep(1)
pi.i2c_close(i2c_handler)
pi.stop()
2. 固定文字列を画面シフトさせて表示
1行20文字以内の文字列を、画面をシフトさせて、右から左へ流れるように表示させます。
カーソルを動かして文字列を書き込むまでは1と同様です。
書込み後にディスプレイを左にシフトする処理をループします。
※ 表示できる文字は1行16文字ですが、20文字までデータとして保持しておけるようです。
※ main 以外は同じなので省略します。
def main(pi, i2c_handler):
"""
繰り返し表示
"""
# 設定
l1 = b"Temp Pres Hum" # 1行 20byteまで保持できる
for char in l1:
write_data(pi, i2c_handler, char)
ddram_addr = 0b00100000 # 2行目の先頭 (0x20)
write_command(pi, i2c_handler, 0b10000000 | ddram_addr) # Set DDRAM RAM Address
l2 = b"24.34 1012 50.35"
for ch in l2:
write_data(pi, i2c_handler, ch) # 0xB1 = ア
while (True):
# Cursor or Display Shift (IS=0, RE=0, SD=0)
# カーソル(ディスプレイ)位置の移動設定
# 0 0 0 1 S/C R/L * *
# - S/C: ディスプレイをシフト (1)
# - R/L: 左にシフト (0)
write_command(pi, i2c_handler, 0b00011000)
sleep(0.5)
3. 長い文字列を画面シフトさせながら表示 (2行同時シフト)
20文字に収まりきらない長い文字列を、画面をシフトさせて、右から左へ流れるように表示させます。
Entry Mode Set
でディスプレイのシフトを有効化し、「カーソルを先頭に移動」 -> 「20文字書き込む」を繰り返します。
表示文字列を20で割り切れる文字数に調整するときれいに表示できます。
※ main 以外は同じなので省略します。
def main(pi, i2c_handler):
"""
画面をシフトさせて長い文字列を表示
画面のシフトは2行同時
"""
l1 = b"Text:"
for char in l1:
write_data(pi, i2c_handler, char)
sleep(1)
# Entry Mode Set (IS=0, RE=0, SD=0)
# データ書き込み時のカーソルの移動方向の設定
# 0 0 0 0 0 1 I/D S
# - I/D: カーソルが右に移動しDDRAMアドレスがインクリメント (1)
# - S : ディスプレイ全体のシフトさせる (1)
write_command(pi, i2c_handler, 0b00000111)
l2 = b"If there was an error the number of bytes read will be less than zero (and will contain the error code)."
l2 += (b" " * (len(l2) % 20))
idx = 0
while (True):
for char in l2:
if (idx % 20 == 0): # 20文字ごとにDDRAMをリセット
# Set DDRAM RAM Address (IS=0, RE=X, SD=0)
# DDRAMアドレスを設定
# 1 N N N N N N N
# - N: アドレスを指定 1行目(0x00 - 0x0F) 2行目(0x20 - 0x2F)
write_command(pi, i2c_handler, 0b10100000) # 2行目の先頭 (0x20)
write_data(pi, i2c_handler, char)
sleep(0.1)
idx += 1
4. 長い文字列を画面シフトさせながら表示 (1行だけシフト)
20文字に収まりきらない長い文字列を、画面をシフトさせて、右から左へ流れるように表示させます。
3との違いは2行目だけ画面をシフトさせているところです。
基本的には3と同じですが、 Double Height(4line)/Display-dot shift
と Shift / Scroll Enable
設定を利用して、特定の行だけ画面のシフトを有効化しています。
※ main 以外は同じなので省略します。
def main(pi, i2c_handler):
"""
スクロール表示 (1行目固定)
※ 長文の表示で利用
"""
l1 = b"Text:"
for char in l1:
write_data(pi, i2c_handler, char)
sleep(1)
# Entry Mode Set (IS=0, RE=0, SD=0)
# データ書き込み時のカーソルの移動方向の設定
# 0 0 0 0 0 1 I/D S
# - I/D: カーソルが右に移動しDDRAMアドレスがインクリメント (1)
# - S : ディスプレイ全体のシフトさせる (1)
write_command(pi, i2c_handler, 0b00000111)
write_command(pi, i2c_handler, 0b00101010) # IS=0, RE=1, SD=0
# Double Height(4line)/Display-dot shift (IS=0, RE=1, SD=0)
# 0 0 0 1 UD2 UD1 * DH'
# - UD2,UD2: あまり関係ないので 11 (default)
# - DH': 選択した行だけディスプレイシフトを行う設定を有効化 (1)
write_command(pi, i2c_handler, 0b00011101)
write_command(pi, i2c_handler, 0b00101001) # IS=1, RE=0, SD=0
write_command(pi, i2c_handler, 0b00101010) # IS=1, RE=1, SD=0
# Shift / Scroll Enable (IS=1, RE=1, SD=0)
# 0 0 0 1 DS4/HS4 DS3/HS3 DS2/HS2 DS1/HS1
# - DS/HS: 2行目のディスプレイシフトを有効化
write_command(pi, i2c_handler, 0b00010010) # display shift enable line2
write_command(pi, i2c_handler, 0b00101000) # IS=0, RE=0, SD=0
try:
l2 = b"If there was an error the number of bytes read will be less than zero (and will contain the error code)."
l2 += (b" " * (len(l2) % 20))
idx = 0
while (True):
for char in l2:
if (idx % 20 == 0): # 20文字ごとにDDRAMをリセット
# Set DDRAM RAM Address (IS=0, RE=X, SD=0)
# DDRAMアドレスを設定
# 1 N N N N N N N
# - N: アドレスを指定 1行目(0x00 - 0x0F) 2行目(0x20 - 0x2F)
write_command(pi, i2c_handler, 0b10100000) # 2行目の先頭 (0x20)
write_data(pi, i2c_handler, char)
sleep(0.1)
idx += 1
finally:
write_command(pi, i2c_handler, 0b00101010) # IS=0, RE=1, SD=0
# Double Height(4line)/Display-dot shift (IS=0, RE=1, SD=0)
# 設定リセット
write_command(pi, i2c_handler, 0b00011101)
write_command(pi, i2c_handler, 0b00101001) # IS=1, RE=0, SD=0
write_command(pi, i2c_handler, 0b00101010) # IS=1, RE=1, SD=0
# Shift / Scroll Enable (IS=1, RE=1, SD=0)
# 設定リセット
write_command(pi, i2c_handler, 0b00011111)
write_command(pi, i2c_handler, 0b00101000) # IS=0, RE=0, SD=0
参考