Raspberry Piから秋月のI2C接続の有機ELディスプレイを制御する

秋月で売られている有機ELキャラクタディスプレイ、SO1602AシリーズをRaspberry PiとPythonを使って制御してみます。
[有機ELキャラクタディスプレイモジュール 16ｘ2行 白色](http://akizukidenshi.com/catalog/g/gP-08277/)
今回は白を使いましたが、緑と黄色も同じ価格で売られています。

# 配線
RasPiとSO1602Aの接続はとても簡単で、電源（3.3V）、GNDに、I2CのSCL、SDAの計4本です。
また、SO1602側で数カ所配線が必要になります。

|ピン番号|機能|
|--:|:--|
|1|GND|
|2|VDD (3.3V)|
|3|/CS (Chip Select, Low)|
|4|SA0 (Slave Address)|
|5, 6|No Connect|
|7|SCL|
|8|SDA_in|
|9|SDA_out|
|10-14|No Connect|

上の表はデータシートのp.7より抜粋したものです。
配線時の注意点は以下の通りです。

* 3番ピン（CS)はGNDに接続する。
* 4番ピンはVDDかGNDのどちらかに接続する。  
どちらに接続するかでI2Cのスレーブアドレスが変わります。
* 8番ピンと9番ピンはまとめてSDAとして使う。
* SCLとSDAのプルアップはRasPi内部でされているので不要。

# RasPiの準備
RasPiでI2Cを使うための下準備はここでは割愛します。
[こちらの記事](http://make.bcde.jp/raspberry-pi/i2c%E3%82%92%E4%BD%BF%E3%81%86%EF%BC%88%E8%A8%AD%E5%AE%9A%E7%B7%A8%EF%BC%89/)などを参考に設定します。

# Pythonの準備
PythonからI2Cを操作するために、SMBusを使います。
apt-getでインストール出来ます。

```bash
$ sudo apt-get install python-smbus
```

# ディスプレイの制御
## 接続と初期化
SO1602Aのスレーブアドレスは、0x3C（SA0=Low）か0x3D（SA0=High）のいずれかです。
今回は以下のようにコンストラクタを書いてみました。
SA0がLow/Highと、初期状態でのカーソルの表示状態を選べるようにしました。

```Python
class SO1602A():
    def __init__(self, sa0 = 0, cursor = False, blink = False):
        self.bus = smbus.SMBus(1)
        if (sa0 == 0):
            self.addr = 0x3c
        else:
            self.addr = 0x3d
        self.clearDisplay()
        self.returnHome()
        self.displayOn(cursor, blink)
        self.clearDisplay()
```

## コマンドの実行
SO1602Aにはいくつかコマンドが用意されています。
上のコンストラクタにあったclearDisplayやreturnHomeはそれらのコマンドに対応したメソッドとなっています。
コマンドを実行させるためには、

1. 「これからコマンドを送ります」というデータ（0x00）を送る。
2. コマンドを送る。

という手順を踏む必要があります。
これをPythonで書いてみると以下のような感じです。

```Python
    def clearDisplay(self):
        self.bus.write_byte_data(self.addr, 0x00, 0x01)
    
    def returnHome(self):
        self.bus.write_byte_data(self.addr, 0x00, 0x02)

    def displayOn(self, cursor = False, blink = False):
        cmd = 0x0c
        if (cursor):
            cmd += 0x02
        if (blink):
            cmd += 0x01
        self.bus.write_byte_data(self.addr, 0x00, cmd)

    def displayOff(self):
        self.bus.write_byte_data(self.addr, 0x00, 0x08)
```

他にも色々なコマンド・オプションがありますが、とりあえずこれだけあればディスプレイとして動作させることが出来ます。

## 文字列を送る
いよいよディスプレイに文字を表示させます。
文字列を送るためには、

1. コマンドでカーソル位置を任意の場所に
2. 「これから文字列を送ります」というデータ（0x40）を送る。
3. 文字列を送る。

という手順を踏む必要があります。
同様にPythonで書いてみると、以下のような感じになりました。

```Python
def writeLine(self, str = '', line = 0, align = 'left'):
    # 文字列が16文字に満たない場合空白で埋める
    while (len(str) < 16):
        if (align == 'right'):
            str = ' ' + str
        else:
            str = str + ' '

    # カーソル位置をあわせる
    if (line == 1):
        self.bus.write_byte_data(self.addr, 0x00, (0x80 + 0x20))
    else:
        self.bus.write_byte_data(self.addr, 0x00, 0x80)

    # 1文字ずつ送信
    for i in range(len(str)):
        self.bus.write_byte_data(self.addr, 0x40, ord(str[i]))
```

文字列はメモリを1ブロックずつ上書きする形で書き込まれていきますので、
"Hello"と表示していたところに"ABC"と書き込むと、表示は"ABClo"となってしまいます。
文字列が16文字に満たない場合に空白で埋めているのはこのためです。

またカーソル位置を移動するコマンドですが、
0x80にアドレスカウンタの値を足した値を送ることで行います。
アドレスカウンタの値は1行目の行頭が0x00で、0x01、0x02、、、0x0Fと続いていきます。
2行目の行頭は0x20で、また0x21、、、と続いていきます。
ここではwriteLineを1行目または2行目を丸々書き換えるメソッドとして実装しています。

その後は文字のASCIIコードを順番に送っていきます。
データをまとめて送るwrite_block_dataというメソッドもあるようなのですが、
なぜかうまく動かなかったため、write_byte_dataで1文字ずつ送っています。

![IMG_0749.jpg](https://qiita-image-store.s3.amazonaws.com/0/148726/37fdcced-2b68-0b72-5f91-8a810365fdac.jpeg)

ということで、無事にディスプレイに文字を表示させることが出来ました。
有機ELディスプレイはとても発色が良くて、視認性もすごく高いです。LCDのように視野角も狭くありません。
写真のブレッドボードは他にもスイッチなどを繋いでいるので配線がわちゃわちゃしていますが、ディスプレイだけを動かすならば4本で済みます。

![IMG_0750.jpg](https://qiita-image-store.s3.amazonaws.com/0/148726/a30553ff-2c11-b0fc-13ea-2dd773422fbc.jpeg)

また、文字列は右詰めあるいは左詰めを選んで表示させることも出来ます。
'g'が少し不格好ですが、それはキャラクタディスプレイのご愛嬌ということで。

# さいごに
今回書いたコードを[gist](https://gist.github.com/uhey22e/2a4d676363722d59428a937ac729def0)に上げました。
Pythonは初心者なので見苦しい部分もあるかと思いますが、ご容赦ください。