Raspberry Pi 備忘録 / Mbedもあるよ!

Raspberry Pi であれこれやった事の記録

i2c LCDを試す

i2cを有効にする

raspi-config から、 “8 Advanced Options” -> “A7 I2C Enable/Disable automatic loading of I2C kernel module”

ACM1602NI について

まず、

I2C接続キャラクタLCDモジュール 16x2行 白色バックライト付: ディスプレイ関連 秋月電子通商 電子部品 ネット通販

を購入したのだが、 購入後に調べていくうちに、

1 readするとフリーズする
2 baudrate を 50000に変更する必要がある。

など、素人に扱いにくいと判明。

けど、使ってみる。
まず、I2Cの通信スピードを変更する必要が有るのだが、みなさんがブログに書いている

# modprobe -r i2c-bcm2708
# modprobe i2c-bcm2708 baudrate=50000
#
# dmesg | grep i2c
[    4.212034] bcm2708_i2c 3f804000.i2c: BSC1 Controller at 0x3f804000 (irq 79) (baudrate 100000)
[    7.270286] i2c /dev entries driver

しても、50000に成っていない。

# dmesg | grep i2c

で確認すると、成っていないはず。

これは、何らかのバージョンの違いが原因らしい。

仕方ないので、/boot/config.txtを変更する。 末尾にでも、

dtparam=i2c_baudrate=50000

を加えておく。

そして、再起動

# dmesg | grep i2c
[    4.071204] bcm2708_i2c 3f804000.i2c: BSC1 Controller at 0x3f804000 (irq 79) (baudrate 50000)

できた!

そしたら、、、

i2cset -y 1 0x50 0x00 0x01     # clear display
i2cset -y 1 0x50 0x00 0x38     # function set: 8bit, 2lines, 5x8
i2cset -y 1 0x50 0x00 0x0c     # display on, no cursor
i2cset -y 1 0x50 0x00 0x06     # direction=INC, no shift

i2cset -y 1 0x50 0x80 0x41     # 'A'
i2cset -y 1 0x50 0x80 0x42     # 'B'
i2cset -y 1 0x50 0x80 0x43     # 'C'
i2cset -y 1 0x50 0x00 0xc0     # move to second line
i2cset -y 1 0x50 0x80 0x61     # 'a'
i2cset -y 1 0x50 0x80 0x62     # 'b'
i2cset -y 1 0x50 0x80 0x63     # 'c'

などして、動作を確認する。

制御コードなど

サンプルコードをコピペして、動作確認できたが、制御コマンドの意味がわからないので、勉強する。

まず、

7.0.2 I2C interface:の段落に、

Slaver address could only set to 1010000, no other slaver address could be set.

とあり、ACM1602NI のアドレスが2進数で 1010000、16進数で 0x50 だと分かる。

次に、

7.0.3 I2C Interface protocol:の段落に、

When the control byte is 0x00,the next folling byte is command byte.
When the control byte is 0x80 ,the next folling byte is data byte.

とあり、
制御コマンドは、まず 0x00 を送り、データの場合は、まず 0x80 を送れば良いことが分かる。

データシートを見ると、表がある。
f:id:pongsuke:20160114155407j:plain
どうも、2進数の桁と、意味の対応表らしい(たぶん、常識なのだろう)

例として、カーソルを表示して、ブリンクさせてみよう。

(ヘッドを数えずに)4行目に、 Display ON/OFF Control ってのがある。

1100 なら  1(固定) 1(Display on)  0(Cursor off) 0(Brink off)
1111 なら  1(固定) 1(Display on)  1(Cursor on) 1(Brink on)

2進数の 1100 は、10進数の 12 で、 16進数で 0x0C
2進数の 1111 は、10進数の 15 で、 16進数で 0x0F

という意味に成るらしい。

なので、上記コマンドの i2cset -y 1 0x50 0x00 0x0Ci2cset -y 1 0x50 0x00 0x0F に変えると、確かに、カーソルが表示されて、ブリンクする。

次に、書き込む場所(アドレス)

Set DDRAM Address を見ると、 1xxxxxxx とあり、7桁で場所を指定するらしい。 そして、アドレスリストがコレだ。

f:id:pongsuke:20160114162143j:plain

0x0f = 16 を指定すると、1行目の右端になり、
0x41 = 65 を指定すると、2行目の左から2つ目なる。

実際に指定するには、
128 + 16 や、 128 + 65 になるので、
0x8F や 0xC1 になる。

実際に、やってみる。

i2cset -y 1 0x50 0x00 0x01     # clear display
i2cset -y 1 0x50 0x00 0x38     # function set: 8bit, 2lines, 5x8 
i2cset -y 1 0x50 0x00 0x0c     # display on, no cursor
i2cset -y 1 0x50 0x00 0x06     # direction=INC, no shift

i2cset -y 1 0x50 0x00 0xc1     # 2nd line, 2nd.
i2cset -y 1 0x50 0x80 0x41     # 'A' 
i2cset -y 1 0x50 0x00 0x8F     # 1st line, 15th
i2cset -y 1 0x50 0x80 0x63     # 'c' 

たしかに、右上と、左下の方に出た。

現在時刻とIPを表示させる

write_i2c_block_data だが、書き込みのスピードの問題なのか、やはり正常に動かない。
面倒だが、write_byte_data を繰り返したほうが確実に動作する。

#!/usr/bin/python
# coding: utf-8 

import smbus
import time
import datetime
import subprocess

bus_number = 1
addr = 0x50
register_setting = 0x00
register_display = 0x80


def setup_lcd():
        #bus.write_i2c_block_data(addr, register_setting, [0x01, 0x38, 0x0c, 0x06])
        #bus.write_i2c_block_data(addr, register_setting, [0x01, 0x38, 0x0f, 0x06])
        bus.write_byte_data(addr, register_setting, 0x01)
        time.sleep(0.02)
        bus.write_byte_data(addr, register_setting, 0x38)
        time.sleep(0.02)
        bus.write_byte_data(addr, register_setting, 0x0f)
        time.sleep(0.02)
        bus.write_byte_data(addr, register_setting, 0x06)
        time.sleep(0.02)
'''
0x01     # clear display
0x38     # function set: 8bit, 2lines, 5x8
0x0c     # display on, no cursor
0x06     # direction=INC, no shift
0x80     # 1行目の左端
0xc0     # 2行目の左端
'''

def write_str( str ):
        for c in list( str ):
                bus.write_byte_data(addr, register_display, ord(c))
                time.sleep(0.1)


bus = smbus.SMBus(bus_number)

setup_lcd()

cmd = 'ifconfig | grep inet | grep -v "127" | cut --delimiter ":" -f 2 | sed -e "s/ .*//"'
p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdout_data, stderr_data = p.communicate()
ip = stdout_data.rstrip()

d_n = datetime.datetime.now().strftime("%Y/%m/%d %H:%M")

print d_n
print ip

write_str(d_n)
bus.write_byte_data(addr, register_setting, 0xc0)
write_str( ip )

できた! f:id:pongsuke:20160114170911j:plain

AQM0802

秋月のキットを買ってきて、自分でハンダ付けした。

I2C接続小型LCDモジュール(8x2行)ピッチ変換キット: 組立キット 秋月電子通商 電子部品 ネット通販

しかし、5vを流さないと i2cdetect -y 1 しても出てこないし、
5vで 3e に出てきたので

i2cdump -y 1 0x3e

しても、全部 XX でうめつくされている。
はんだ付けを失敗して、壊したのかもしれない・・・

※ 追記 2016-07-25

秋月HPに、下記の記載が見当たりました。

FAQ詳細 秋月電子通商 電子部品 ネット通販

【質問】 I2C液晶をラズパイに接続しても表示されません。どのようにすればいいでしょうか?
[2016/07/19 14:19:14]
【回答】 Raspberry Pi 全シリーズに共通する仕様の制約から、AQMシリーズの液晶を接続する場合にはバッファを介して接続してください。 I2CバスはRaspberry Pi(マスターデバイス、GPIO 2, GPIO 3)の基板内で1.8kΩの抵抗によって固定的にプルアップされています。
3.3Vに1.8kΩでプルアップされたバスラインをスレーブデバイスがLレベル(0.3Vdd=0.99V以下)にするためには、最低でも1.28mA以上の電流引き込み能力を必要とします[3.3V-0.00128A×1800Ω≒0.99V]。 AQMシリーズ液晶は低消費電流(1mA)設計のため、[SDA(データ)]ラインで過大な電流を引き込むことができず、通信エラーを超す場合があります。
これを回避するためには[SDA(データ)]ラインに双方向バッファ(リピーター)を介在させ、正常なデータ通信を確保する必要があります。 簡単な方法として、I2Cバスリピーター[ I-10882 ]を使用した例を下記PDFにてご紹介いたしますので、ご参照ください。

参考資料PDF

また、

「実例で学ぶRaspberry Pi電子工作」補足情報: 読者の方々の声

こちらを拝見すると、詳細が書かれております。

私の環境では、2.2kΩの抵抗を追加することで、下記スイッチサイエンスの製品と同様、キチンと動きました。

仕方ないので、スイッチサイエンスの完成品を購入した。

http://www.amazon.co.jp/gp/product/B00YU40L06/ref=oh_aui_detailpage_o01_s00?ie=UTF8&psc=1www.amazon.co.jp

配線が秋月と異なり、

VDD SDA SCL XRS GND

と成っており、PIN 1,3,5,7,9 に、そのまま挿せばOKという楽チン仕様。

# i2cdetect -y 1
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- -- -- -- -- -- -- -- -- -- -- -- -- 
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- 3e -- 
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
70: -- -- -- -- -- -- -- --

と、アドレスが 3e だと判明する。

データシートに、

コマンドの場合RS=0、データはRS=1になります。

とあり、6桁の Control byte の 1桁上に RS が有るので、恐らく、
7桁目は、2進数で 1000000 = 10進数で 64 = 16進数で 0x40 なので、

コマンドの場合は、 0x00 書き込みの場合は、 0x40

になる。

初期設定例 + 書き込み

i2cset -y 1 0x3e 0x00 0x38 0x39 0x14 0x70 0x56 0x6c i # Function set, 
i2cset -y 1 0x3e 0x00 0x38 0x0c 0x01 i # Function set, 
i2cset -y 1 0x3e 0x00 0x06 # Entry mode set.
i2cset -y 1 0x3e 0x00 0x80 # 1st line. Left.
i2cset -y 1 0x3e 0x40 0x41 0x42 0x43 i # Write String.
i2cset -y 1 0x3e 0x00 0xC0 # 2nd line. Left.
i2cset -y 1 0x3e 0x40 0xCA 0xDB 0xB0 i # Write String.

また、IPを書かせてみる。

#!/usr/bin/python
# coding: utf-8 

import smbus
import subprocess

bus_number = 1
addr = 0x3e
register_setting = 0x00
register_display = 0x40

def setup_lcd():
        bus.write_i2c_block_data(addr, register_setting, [0x38, 0x39, 0x14, 0x70, 0x56, 0x6c])
        bus.write_i2c_block_data(addr, register_setting, [0x38, 0x0c, 0x01])

def write_str( str ):
        counter = 0
        for c in list( str ):
                bus.write_byte_data(addr, register_display, ord(c))
                counter += 1
                if counter == 8:
                        bus.write_byte_data(addr, register_setting, 0xC0)
                elif counter == 16:
                        break

bus = smbus.SMBus(bus_number)

setup_lcd()

cmd = 'ifconfig | grep inet | grep -v "127" | cut --delimiter ":" -f 2 | sed -e "s/ .*//"'
p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdout_data, stderr_data = p.communicate()
ip = stdout_data.rstrip()

print ip

write_str( ip )

なんとなく、ケーブルで向きを変えて、中に差し込んでみた。

f:id:pongsuke:20160115170021j:plain

AQM1602A をちゃんと動かす(2017-03-07追記)

f:id:pongsuke:20170307171948j:plain

秋月のAQM1602Aを改めて購入しました。

akizukidenshi.com

FAQに注意書きが有ります。

Raspberry Pi 全シリーズに共通する仕様の制約から、AQMシリーズの液晶を接続する場合にはバッファを介して接続してください。 I2CバスはRaspberry Pi(マスターデバイス、GPIO 2, GPIO 3)の基板内で1.8kΩの抵抗によって固定的にプルアップされています。 3.3Vに1.8kΩでプルアップされたバスラインをスレーブデバイスがLレベル(0.3Vdd=0.99V以下)にするためには、最低でも1.28mA以上の電流引き込み能力を必要とします[3.3V-0.00128A×1800Ω≒0.99V]。 AQMシリーズ液晶は低消費電流(1mA)設計のため、[SDA(データ)]ラインで過大な電流を引き込むことができず、通信エラーを超す場合があります。 これを回避するためには[SDA(データ)]ラインに双方向バッファ(リピーター)を介在させ、正常なデータ通信を確保する必要があります。 簡単な方法として、I2Cバスリピーター[ I-10882 ]を使用した例を下記PDFにてご紹介いたしますので、ご参照ください。

なので、

akizukidenshi.com

及び、

SOP8(1.27mm)DIP変換基板 金フラッシュ (9枚入): パーツ一般 秋月電子通商 電子部品 ネット通販

基板用リードフレーム SS2.54−6SN (25本入): パーツ一般 秋月電子通商 電子部品 ネット通販

を購入して、配線します。

f:id:pongsuke:20170309115012p:plain f:id:pongsuke:20170309115015p:plain

基盤の左上の白いポッチを、ICの丸いマークに合わせると、ICのPIN1が基盤の1に成ります。

f:id:pongsuke:20170309115400j:plain

ラズパイから、リピーターとディスプレイの両方に給電しつつ、GNDを共有しつつ、SDA,SCLをつなぎます。

AQM1602A PCA9515 Raspberry Pi
Pin1 n.c
SCL Pin2 SCL0
SDA Pin3 SDA0
GND Pin4 GND GND
Pin5 EN
Pin6 SDA1 Pin3 SDA
Pin7 SCL1 Pin5 SCL
+V Pin8 Vcc 3.3v

ちゃんと動きました!