はじめに
この記事はAQM0802というありふれた液晶をGoからI2Cで動かすための記事です。
秋月でAQM0802を購入する場合、キットと完成品とあるのですが、手間と差額を考えると完成品を買っておくのがよいと思います。
I2C接続小型LCDモジュール(8x2行)ピッチ変換モジュール(完成品): パーツ一般 秋月電子通商 電子部品 ネット通販
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 を読んでもらうとして、早い話が、シリアルバスです。
Linuxの場合、 /dev/i2c-*
のデバイスファイルとして扱えます。
つまり、このデバイスファイルをごにょごにょすればよいわけです。
GoにはGobotというこの手のことをするのにうってつけのライブラリ(群)があるのですが、こんな小さな液晶を動かすにはちょっと大仰です。
そこで今回はDave Cheny御大の GitHub - davecheney/i2c: I2C package を使うことにします。
このライブラリ、事実上コメント込みで100行にも満たず、非常にコンパクトなため、簡単に利用できます。
i2cパッケージの使い方
先ほど、 sudo i2cdetect -y 1
というコマンドを叩きましたが、この1というのは /dev/i2c-*
の *
の部分にあたる数字です。そしてこのコマンドで得られた 3e
というのは、今回操作したい液晶のアドレスです。
この2つの値があれば、このように I2C
structを初期化するだけで準備は完了です。
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のデータシートに、初期設定例がついているので、これを読みましょう。
簡単にフローを抜粋するとこのようになります。
- Function Set IS=1 (0x38)
- Function Set IS=2 (0x39)
- Internal OSC frequency (0x14)
- Contrast set (0x70)
- Power/ICON/Contrast control (0x56)
- Follower control (0x6c)
- Function Set IS=1 (0x38)
- Display ON/OFF control (0x0C)
- 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)
}
完成
最終的に、こんなコードになりました。
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は向かないことがわかります。
まとめ
Goだとクロスコンパイルが簡単なので、Raspberry Pi相手であればMacやWinあるいは
デスクトップLinuxでビルドして、バイナリをRaspberry Piに送って確認すると、
環境構築の手間が少なく、マシンパワーも存分に使うことができて楽です。
単純にI2Cを使うときはdavecheney/i2cが便利ですが、このライブラリにも不満があって、
Slave Addressが決め打ちだったりします(データシートのSlave Addressと違うのになぜ動くのだろう?)。
暇を見つけてPRを送りたいと思います。