先日作ったトラ技付録のApple Piの記事の中に、RasberryPiでLCDが表示できない件についてコラムが載っていた。
RaspberryPi側のプルアップ抵抗が小さく、デバイス側のLレベルが既定の電圧まで下がり切れないのが原因とのこと。
このことについては某巨大掲示板ではだいぶ前に判明していて、SDAとGNDの間に抵抗を挟むとか対策があったけど、それをやるならi2cレベルコンバータ(これとかこれ)が使えないかなとか考えていたけど、面倒だったのでほったらかし状態になっていた。
で、先のトラ技付録ではPCA9515Aを使って対応していた。
コラム中にある写真にはテキサス・インスツルメンツ社と説明書きされているが、実際に載っている写真はどう見てもNXP社のPCA9515Aの写真である。

販売されている部品セットではテキサス・インスツルメンツ社の物が入っていたけど。
そのトラ技が出版されてしばらくしてから、PCA9515Aを秋月電子で取り扱いを始めたので早速入手して遊んでみることにした。

今回作った2作品。
左のはブレッドボード用にPCA9515Aを2つ積んだもの。
右はi2c LCDの確認用に作ったもの。

5ピンコネクタのメス-メスケーブルでRaspberryPiの1,3,5,7,9ピンとつなげばすぐ使えるようにしてある。
RasspberryPi側とつながる方は2段のコネクタにしてあり、GPIO4をEnable用に使う場合は下段に、Enableを使わない場合は上段につなぐようにしてある。
もちろん、余った側から別のi2cデバイスにつなぐこともできる。
右のデバイス側のコネクタは3.3VとGNDを2本分出していて、LCD側に/Reset端子とかあったり、バックライト用だったり使えるようにしてある。
また、デバイス側のプルアップ抵抗の実装の有無に合わせてスライドスイッチでプルアップ抵抗を使うか使わないか選択できるようにしてある。

ブレッドボード用は最初PCA9515Aを1個だけ使用と考えていたけど、基板のサイズがどうしても中途半端になってしまうので、2個積んで28ピンサイズにすることにした。
RaspberryPi側につながる方は、オス-メスの6ピンコネクタで1,3,5,7,9,11ピンとストレートでつなぐことができるようにしてある。
この場合GPIO4が1側のEnableを、GPIO17が2側のEnableを制御できるようになる。
こちらもスイッチでSDA/SCLのプルアップの有無を選択できるようにしてある。
ブレッドボードでの使用を想定しているので、/Reset端子用に10KΩでプルアップされたピンも準備して、ブレッドボード上に別にプルアップ用の抵抗を準備しなくてもいいように作ってある。(というかピンが余ったからそうした)

まずはLCDを直接つないでみた場合
pi@testhard01:~/script$
pi@testhard01:~/script$ sudo i2cdetect 1
WARNING! This program can confuse your I2C bus, cause data loss and worse!
I will probe file /dev/i2c-1.
I will probe address range 0x03-0x77.
Continue? [Y/n]
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: -- -- -- -- -- -- -- --
pi@testhard01:~/script$
pi@testhard01:~/script$
pi@testhard01:~/script$ ./i2c-disp-aki.sh -ic Akizuki
pi@testhard01:~/script$

秋月の8桁×2行のLCDはちゃんと認識して表示される。
pi@testhard01:~/script$
pi@testhard01:~/script$ sudo i2cdetect 1
WARNING! This program can confuse your I2C bus, cause data loss and worse!
I will probe file /dev/i2c-1.
I will probe address range 0x03-0x77.
Continue? [Y/n]
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: -- -- -- -- -- -- -- --
pi@testhard01:~/script$
pi@testhard01:~/script$
pi@testhard01:~/script$ ./i2c-disp-sbl.sh -ic Strawberry
pi@testhard01:~/script$ ./i2c-disp-sbl.sh -p 0x40 Linux
pi@testhard01:~/script$

ストロベリー・リナックスのLCDも認識して表示もできる。
pi@testhard01:~/script$
pi@testhard01:~/script$ sudo i2cdetect 1
WARNING! This program can confuse your I2C bus, cause data loss and worse!
I will probe file /dev/i2c-1.
I will probe address range 0x03-0x77.
Continue? [Y/n]
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --
pi@testhard01:~/script$
pi@testhard01:~/script$

秋月の16桁×2行のLCDは認識できない。
ゆえに表示もできない。
次にPCA9515を使って表示してみる。


秋月の8桁×2行のものとストロベリー・リナックスの物は変わらず表示される。
では、問題の秋月の16桁×2行のLCDで使用してみる。
pi@testhard01:~/script$
pi@testhard01:~/script$ sudo i2cdetect 1
WARNING! This program can confuse your I2C bus, cause data loss and worse!
I will probe file /dev/i2c-1.
I will probe address range 0x03-0x77.
Continue? [Y/n]
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: -- -- -- -- -- -- -- --
pi@testhard01:~/script$
pi@testhard01:~/script$
pi@testhard01:~/script$ ./i2c-disp-aki.sh -ic Akizuki
pi@testhard01:~/script$ ./i2c-disp-aki.sh -p 0x40 "AE-AQM1602A"
pi@testhard01:~/script$

今度はちゃんと認識して文字を表示されるようになった。
続いて、ブレッドボード用に作った物の確認をしてみる。


それぞれ単体での動作確認をして正しく動くことを確認。
それでは、今度はLCDを2台つないでそれぞれ別々に表示させてみる。
まずGPIO4と17を出力に設定して、両方をLレベルに設定。
pi@testhard01:~/script$
pi@testhard01:~/script$ echo 4 > /sys/class/gpio/export
pi@testhard01:~/script$ echo 17 > /sys/class/gpio/export
pi@testhard01:~/script$ sudo sh -c 'echo low > /sys/class/gpio/gpio4/direction'
pi@testhard01:~/script$ sudo sh -c 'echo low > /sys/class/gpio/gpio17/direction'
pi@testhard01:~/script$
pi@testhard01:~/script$
pi@testhard01:~/script$ sudo i2cdetect 1
WARNING! This program can confuse your I2C bus, cause data loss and worse!
I will probe file /dev/i2c-1.
I will probe address range 0x03-0x77.
Continue? [Y/n]
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --
pi@testhard01:~/script$
pi@testhard01:~/script$
もちろん両方のEN端子がLレベルなので、LCDは認識されない。
続いて、それぞれのEN端子をHレベルにして各LCDに文字を表示させてみる。
pi@testhard01:~/script$
pi@testhard01:~/script$ sudo sh -c 'echo low > /sys/class/gpio/gpio17/direction'
pi@testhard01:~/script$ sudo sh -c 'echo high > /sys/class/gpio/gpio4/direction'
pi@testhard01:~/script$
pi@testhard01:~/script$ sudo i2cdetect 1
WARNING! This program can confuse your I2C bus, cause data loss and worse!
I will probe file /dev/i2c-1.
I will probe address range 0x03-0x77.
Continue? [Y/n]
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: -- -- -- -- -- -- -- --
pi@testhard01:~/script$
pi@testhard01:~/script$ ./i2c-disp-sbl.sh -ic Strawberry
pi@testhard01:~/script$ ./i2c-disp-sbl.sh -p 0x40 "Linux lcd1"
pi@testhard01:~/script$
pi@testhard01:~/script$
pi@testhard01:~/script$ sudo sh -c 'echo low > /sys/class/gpio/gpio4/direction'
pi@testhard01:~/script$ sudo sh -c 'echo high > /sys/class/gpio/gpio17/direction'
pi@testhard01:~/script$
pi@testhard01:~/script$ sudo i2cdetect 1
WARNING! This program can confuse your I2C bus, cause data loss and worse!
I will probe file /dev/i2c-1.
I will probe address range 0x03-0x77.
Continue? [Y/n]
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: -- -- -- -- -- -- -- --
pi@testhard01:~/script$
pi@testhard01:~/script$ ./i2c-disp-sbl.sh -ic Strawberry
pi@testhard01:~/script$ ./i2c-disp-sbl.sh -p 0x40 "Linux lcd2"
pi@testhard01:~/script$

これで複数のLCDをコントロールすることができるようになった。
ちなみに、以前作ったスクリプトをさらに改修。
初期化の際、1度目のi2csetコマンドが失敗してもリトライするように修正。
さらに秋月のLCDとストロベリー・リナックスのLCDではコントラスト調整等のパラメータが微妙に違うので、それぞれ2つに分けた。
秋月やストロベリー・リナックスではこれ以外のLCDも扱っているし、それ以外のところでもLCDを扱っており、場合によってはさらにパラメータが違ってくるので、オプション指定でコントラスト等の調整ができるような改修も必要かもしれない。
i2c-disp-aki.sh
#!/bin/bash
function usage {
echo "Usage: $0 [-ic] [-p pos] message" > /dev/stderr;
echo " -i : LCD init, -c : Clear Screen" > /dev/stderr
echo " -p : position (0:top left, 40:bottom left)" > /dev/stderr
exit 1
}
function abort
{
echo "$@" 1>&2
exit 1
}
[ $# = 0 ] && usage
while getopts "icp:" flag; do
case $flag in
\?) usage ;;
i) i2cset -y 1 0x3e 0 0x38 0x39 0x14 0x70 0x56 0x6c i
if [ $? -ne 0 ]; then
echo "i2c error:initialize step1. retry initialize" 1>&2
sleep 1
i2cset -y 1 0x3e 0 0x38 0x39 0x14 0x70 0x56 0x6c i || abort "i2c error:initialize step1"
fi
sleep 0.25
i2cset -y 1 0x3e 0 0x38 0x0c 0x01 i || abort "i2c error:initialize step2"
sleep 0.05
;;
c) i2cset -y 1 0x3e 0 0x01 || abort "i2c error:clear screen" ;;
p) i2cset -y 1 0x3e 0 $((OPTARG+128)) || abort "i2c error:set position" ;;
esac
done
shift $((OPTIND-1))
[ $# = 0 ] && exit
LANG=C
MSG=`echo -n "$1" | perl -pe '$_=join" ",map{ord }split//'`
#echo $MSG
i2cset -y 1 0x3e 0x40 $MSG i || abort "i2c error:display string"
i2c-disp-sbl.sh
#!/bin/bash
function usage {
echo "Usage: $0 [-ic] [-p pos] message" > /dev/stderr;
echo " -i : LCD init, -c : Clear Screen" > /dev/stderr
echo " -p : position (0:top left, 40:bottom left)" > /dev/stderr
exit 1
}
function abort
{
echo "$@" 1>&2
exit 1
}
[ $# = 0 ] && usage
while getopts "icp:" flag; do
case $flag in
\?) usage ;;
i) i2cset -y 1 0x3e 0 0x38 0x39 0x14 0x78 0x5e 0x6c i
if [ $? -ne 0 ]; then
echo "i2c error:initialize step1. retry initialize" 1>&2
sleep 1
i2cset -y 1 0x3e 0 0x38 0x39 0x14 0x78 0x5e 0x6c i || abort "i2c error:initialize step1"
fi
sleep 0.25
i2cset -y 1 0x3e 0 0x0c 0x01 0x06 i || abort "i2c error:initialize step2"
sleep 0.05
;;
c) i2cset -y 1 0x3e 0 0x01 || abort "i2c error:clear screen" ;;
p) i2cset -y 1 0x3e 0 $((OPTARG+128)) || abort "i2c error:set position" ;;
esac
done
shift $((OPTIND-1))
[ $# = 0 ] && exit
LANG=C
MSG=`echo -n "$1" | perl -pe '$_=join" ",map{ord }split//'`
#echo $MSG
i2cset -y 1 0x3e 0x40 $MSG i || abort "i2c error:display string"