GoでI2C制御のAQM0802液晶を使う

# はじめに
この記事はAQM0802というありふれた液晶をGoからI2Cで動かすための記事です。
秋月でAQM0802を購入する場合、キットと完成品とあるのですが、手間と差額を考えると完成品を買っておくのがよいと思います。

[Ｉ２Ｃ接続小型ＬＣＤモジュール（８ｘ２行）ピッチ変換モジュール（完成品）: パーツ一般 秋月電子通商 電子部品 ネット通販](http://akizukidenshi.com/catalog/g/gM-09109/)

# Raspberry Pi 3側の準備
## SSH出来るようにしておく
私はRaspberry Piで動くGoプログラムを書く際、Raspberry Pi側にGoの環境を作らず、MacやPCにGo環境を作り、そこでクロスコンパイルビルドしたバイナリをRaspberry Piに送って動かしています。
ちなみにRaspberry Pi 2と3をターゲットにしてクロスコンパイルする場合は、以下のようにします。

```
$ env GOOS=linux GOARCH=arm GOARM=7 go build
```

このような開発スタイルを取る場合、SSHできないといろいろ面倒なので出来るようにしておきます。
同じLAN内で互いが見える場合、Raspberry Pi側でavahi-daemonを入れておくとBonjourでつなげられて、`ssh pi@raspberrypi.local` でアクセス出来て便利です。

セキュリティ上、互いが見えないようになっている場合(公衆解放されている場合ありがち)、有線でつなぐとかいろいろ手がありますが、ここでは詳しく書きません。

またRaspberry Pi側にGoの開発環境を作れば、当然SSHなどは不要です。

## I2Cの有効化と必要なアプリケーションのインストール
まずI2Cを有効化します。Raspberry Piでraspi-configを起動してください。

```
$ sudo raspi-config
```

設定画面が出てくるので、以下の順に選んで行きます。

```
1 Advanced Options

A7 I2C

Yes

Finish
```

終わったら `sudo reboot` しましょう。

次にユーティリティアプリケーションをインストールします。
```
$ sudo apt-get install i2c-tools
```

`sudo i2cdetect -y 1` で何か動いたらインストールできています。

最後にI2Cの転送レートを設定します。
このやり方だと、再起動のたびに設定がリセットされます。
`/etc/modprobe.d/i2c.conf` を変更することで、再起動しても設定が残るようになりますが、この記事では触れません。

```
$ sudo rmmod i2c-dev i2c-bcm2708
$ sudo modprobe i2c-bcm2708 baudrate=50000
$ sudo modprobe i2c-dev
```



## 配線
AQM0802の端子とRaspberry Pi 3を以下のように配線します。
左がAQM0802の端子で、右がRaspberry Pi 3です。
RESETについては今回はVCCにショートさせます。

- Vcc (3.3V) ----- PIN1 (Pi3)
- RESET      ----- Vccにショート
- SCL        ----- PIN5
- SDA        ----- PIN3
- GND        ----- PIN6

この記事を書くにあたって気づいたのですが、秋月のよくある質問を読むと、I2Cバスリピータを噛ませろと書いてありました。
いろいろ調べると、入荷時期によってRaspberry Piと直接つないでも動作しないようです。
動かない方はリピータを試してみてください。

### 接続確認

接続ができたら `sudo i2cdetect -y 1` と叩いてみてください。
テーブルみたいなものが出て `3e` と表示されていたら成功です。

# 準備が出来ました
お疲れ様です。ここからGoの話が始まります。

# GoでI2C制御の液晶を動かす
## I2C
今回の液晶はI2Cで接続するものを使いました。I2Cとは何でしょう？
詳しくはWikipedia [I2C - Wikipedia](https://ja.wikipedia.org/wiki/I2C) を読んでもらうとして、早い話が、シリアルバスです。
Linuxの場合、 `/dev/i2c-*` のデバイスファイルとして扱えます。
つまり、このデバイスファイルをごにょごにょすればよいわけです。

GoにはGobotというこの手のことをするのにうってつけのライブラリ(群)があるのですが、こんな小さな液晶を動かすにはちょっと大仰です。
そこで今回はDave Cheny御大の   [GitHub - davecheney/i2c: I2C package](https://github.com/davecheney/i2c)  を使うことにします。
このライブラリ、事実上コメント込みで100行にも満たず、非常にコンパクトなため、簡単に利用できます。

### i2cパッケージの使い方
先ほど、 `sudo i2cdetect -y 1` というコマンドを叩きましたが、この1というのは `/dev/i2c-*` の `*` の部分にあたる数字です。そしてこのコマンドで得られた `3e` というのは、今回操作したい液晶のアドレスです。
この2つの値があれば、このように `I2C` structを初期化するだけで準備は完了です。

```go
dev := i2c.New(0x3e, 1)
```

`I2C` struct(のポインタ)は以下の4つのメソッドを持っており、非常に直感的な設計となっています。

* Write(buf []byte) (int, error)
* WriteByte(b byte) (int, error)
* Read(p []byte) (int, error)
* Close()

ここからは、`I2C`  structを使って、実際に液晶をいじることにしてみます。

## ゴール

いまさらですが、この記事のゴールとして、液晶にOrigamiと出すことを目標とします。

## 初期設定
まずは液晶を使えるように初期設定する必要があります。
AQM0802のデータシートに、初期設定例がついているので、これを読みましょう。

簡単にフローを抜粋するとこのようになります。

1. Function Set IS=1 (0x38)
2. Function Set IS=2 (0x39)
3. Internal OSC frequency (0x14) 
4. Contrast set (0x70)
5. Power/ICON/Contrast control (0x56)
6. Follower control (0x6c)
7. Function Set IS=1 (0x38)
8. Display ON/OFF control (0x0C)
9. Clear Display (0x01)

この順番でコマンドを送ってやれば、液晶の初期化が完了です。
コマンドを送る場合、数値の他にコントロールバイトとして`0x0`も一緒に送る必要があります。

つまりこのような感じです。

```
dev := i2c.New(0x3e, 1)
dev.Write([]byte{0x0, 0x38})
```

またコマンドは連続で送り込んではだめで、各信号間に26.3μSのwaitをかける必要があります。
(ただし6の後は200ms)
ということでこんな関数を作ると便利です。

```
const (
	wait = 26300 * time.Nanosecond
)

func cmd(dev *i2c.I2C, b byte) {
	dev.Write([]byte{0x0, b})
	time.Sleep(wait)
}
```

## 文字データの書き込み

文字を液晶に表示するためには、文字のバイトを送るだけです。
記号を除くとASCIIなので、特に難しくありません。
文字データを書き込む前には、コントロールバイトとして`0x40`を送る必要があります。
コマンドのときと同様に、こんな関数を作ると便利です。

```
func write(dev *i2c.I2C, s string) {
	chars := []byte(s)
	dev.Write(append([]byte{0x40}, chars...))
	time.Sleep(wait)
}
```

# 完成

最終的に、こんなコードになりました。

```go
package main

import (
	"time"

	"github.com/davecheney/i2c"
)

const (
	wait = 26300 * time.Nanosecond
)

func cmd(dev *i2c.I2C, b byte) {
	dev.Write([]byte{0x0, b})
	time.Sleep(wait)
}

func write(dev *i2c.I2C, s string) {
	chars := []byte(s)
	dev.Write(append([]byte{0x40}, chars...))
	time.Sleep(wait)
}

func main() {
	dev, _ := i2c.New(0x3e, 1)

	cmd(dev, 0x38)
	cmd(dev, 0x39)
	cmd(dev, 0x14)
	cmd(dev, 0x70)
	cmd(dev, 0x56)
	cmd(dev, 0x6c)
	time.Sleep(200 * time.Millisecond)
	cmd(dev, 0x38)
	cmd(dev, 0x0c)
	cmd(dev, 0x01)
	time.Sleep(1080 * time.Microsecond)

	write(dev, "Origami")
}
```

MacやWinの人は　`env GOOS=linux GOARCH=arm GOARM=7 go build` でビルドして、
Raspberry Piに送って確かめてみましょう。
ちなみにですが、私の手元のバイナリだと1.44MBを超えており、フロッピーディスクに入りません。
Raspberry Piくらいリッチな環境ならともかく、ガチ組み込みにGoは向かないことがわかります。

![aqm0802_origami.jpg](https://qiita-image-store.s3.amazonaws.com/0/17916/c1feed75-76b0-60db-0ba6-7aef6d52e325.jpeg)


# まとめ

Goだとクロスコンパイルが簡単なので、Raspberry Pi相手であればMacやWinあるいは
デスクトップLinuxでビルドして、バイナリをRaspberry Piに送って確認すると、
環境構築の手間が少なく、マシンパワーも存分に使うことができて楽です。

単純にI2Cを使うときはdavecheney/i2cが便利ですが、このライブラリにも不満があって、
Slave Addressが決め打ちだったりします(データシートのSlave Addressと違うのになぜ動くのだろう？)。
暇を見つけてPRを送りたいと思います。