11
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

OrigamiAdvent Calendar 2016

Day 9

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

Last updated at Posted at 2016-12-08

はじめに

この記事は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のデータシートに、初期設定例がついているので、これを読みましょう。

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

  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)
}

完成

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

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

まとめ

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

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

11
6
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
11
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?