2
0

【電子工作】SO1602AW(キャラクタディスプレイ)を使ってみる

Last updated at Posted at 2023-09-20

今回はI2C接続の16x2有機ELキャラクターディスプレイSO1602AWWB-UC-WBを使ってみます。

秋月電子で1580(税込み)でした。
有機ELキャラクタディスプレイモジュール 16×2行 白色 | 秋月電子通商

■ ピン

スクリーンショット 2023-09-19 1.01.16.png (356.1 kB)
  1. VSS: ラズパイのGND端子に接続
    電源グランド
  2. VDD: ラズパイの3.3V端子に接続
    電源
  3. /CS: VSSに接続
  4. SA0: GNDもしくはVDDに接続
    I2Cスレーブアドレス。この端子がLOWなら0x3C 、 HIGHなら 0x3D
    ※ 今回はLOWに接続
  5. 接続しない
  6. 接続しない
  7. SCL: ラズパイのSCL端子に接続
    I2Cクロック入力
  8. SDA in: ラズパイのSDAに接続
    I2Cのデータ入力
  9. SDA out: ラズパイのSDAに接続
    I2Cのデータ出力
  10. 以降は接続しない

■ データの送信

データの送信は「コントロールバイト」と「データバイト」の2つを送信します。
「コントロールバイト」は送信するデータのメタデータで、「データバイト」が送信するデータの本体です。

「コントロールバイト」には以下のフラグをもたせます。

  • bit 7 Co : データバイトが複数の場合は1、1組のみの場合は0を設定
  • bit6 D/C# : データバイトがコマンドの場合は 0 、 データの場合は 1

※ 基本的にデータバイトを複数送ることはしないので、コマンドの場合は 0x00 、データ書込みの場合は 0x40 をコントロールバイトに指定することになります。

スクリーンショット 2023-09-18 23.26.33.png (73.5 kB)

■ 利用できる文字

SO1602AWでは下記の文字が利用できます。
「データバイト」に文字に対応するアドレスを格納して、SO1602AWに送信することで液晶に表示されます。
※ 例えば a0b01000001 (0x41) 、0b10110001 (0xB1) となります。

スクリーンショット 2023-09-19 0.35.45.png (223.8 kB)

■ 液晶表示 DDRAMアドレス

液晶の各セルにはアドレスが紐付けられています。 (これをDDRAMアドレスと呼ぶようです)
SO1602AWではカーソルという概念があり、このカーソルを任意のDDRAMアドレスに移動して表示したい文字を送信すると、指定したDDRAMアドレスに対応するセルに文字を表示することができます。
※ カーソルの移動は後述の「コマンド」で行います。
カーソルは1文字表示するごとに自動でインクリメントするので、表示ごとにいちいち設定し直す必要はありません。データを送信する前に表示開始位置(例えば 0x00 (1行目先頭))に設定しておけばOKです。

スクリーンショット 2023-09-19 0.36.39.png (46.8 kB)

■ よく使うコマンド

SO1602AWはコマンドを利用してデバイスの動作設定を行います。
この設定が微妙にややこしいので、少し解説します。

このコマンドには、 IS , RE , SDという3つのレジスタが存在し、このレジスタの状態で利用できるコマンドが異なります。
IS , RE , SD はいずれも 01 を値に取ります。

めんどくさいことに、これら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 RE0 に、 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) 利用しない

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 RE1 に設定できるコマンド。
その他設定表示設定も兼ねている。
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. 固定表示してみる

画面をシフトさせずに固定の文字列を表示します

disp1.jpg (529.3 kB)

実際にテキストを表示させるコードは以下になります。基本的にはカーソルを動かして、テキストを書き込むだけですね。

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文字以内の文字列を、画面をシフトさせて、右から左へ流れるように表示させます。

disp2.gif (1.9 MB)

カーソルを動かして文字列を書き込むまでは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文字に収まりきらない長い文字列を、画面をシフトさせて、右から左へ流れるように表示させます。

disp3.gif (1.9 MB)

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行目だけ画面をシフトさせているところです。

disp4.gif (1.9 MB)

基本的には3と同じですが、 Double Height(4line)/Display-dot shiftShift / 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

参考

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0