Petalinux2018.3でキャラクタLCD制御(ST7032i) の変更点
更新- 追加された行はこの色です。
- 削除された行はこの色です。
- 電気回路/zynq/Petalinux2018.3でキャラクタLCD制御(ST7032i) へ行く。
- 電気回路/zynq/Petalinux2018.3でキャラクタLCD制御(ST7032i) の差分を削除
[[電気回路/zynq]]
* I2C 経由で繋いだキャラクタ型の液晶ディスプレイ(LCD)を制御する [#b227347f]
繋いだのはこれ [[digikey:NHD-C0216CIZ-FSW-FBW-3V3-ND]]
16 文字 x 2 行 で白色バックライト付きの LCD
付属の PDF だけだといろいろ情報が足りなくて困るのだけれど、
どうやらこの LCD には [[Strawberry Linux の ST7032i>https://strawberry-linux.com/pub/ST7032i.pdf]]
という有名なコントローラが載っているらしいので、このキーワードで検索すればいろいろと情報が得られる。
ST7032i のマニュアル:~
https://strawberry-linux.com/pub/ST7032i.pdf
** 目次 [#v08453ec]
#contents
* 環境 [#h9cf1202]
[[作業履歴>電気回路/zynq#kc37dfd8]] のように
Petalinux 2018.3 を基本として作業してます。
LCD は i2c-1 に繋いだ。Petalinux は i2c-1 を認識して dtb に反映してくれていた。
Zynq 機器で動作する Ubuntu 18.04LTC のコンソールで、
LANG:console
$ less /boot/system.dts
...
i2c1: i2c@e0005000 {
compatible = "cdns,i2c-r1p10";
status = "disabled";
clocks = <&clkc 39>;
interrupt-parent = <&intc>;
interrupts = <0 48 4>;
reg = <0xe0005000 0x1000>;
#address-cells = <1>;
#size-cells = <0>;
};
...
$ sudo grep i2c /var/log/syslog
Jan 28 15:58:20 zturn2018_3 kernel: i2c /dev entries driver
Jan 28 15:58:20 zturn2018_3 kernel: cdns-i2c e0004000.i2c: 400 kHz mmio e0004000 irq 24
Jan 28 15:58:20 zturn2018_3 kernel: cdns-i2c e0005000.i2c: 400 kHz mmio e0005000 irq 25
$ ls -l /dev | grep i2c
crw------- 1 root root 89, 0 1月 28 15:58 i2c-0
crw------- 1 root root 89, 1 1月 28 15:58 i2c-1
$ cat /proc/device-tree/amba/i2c@e0005000/status
okay
ちゃんと 400kHz クロックになっており、status = "okay" なのでそのまま使える。
* リセットピンを制御可能にする [#e9ee91d6]
[[電気回路/zynq/Petalinux2018.3でaxi_gpio]] により、
/sys/class/gpio/gpio905/value 経由でリセットピンの上げ下げができるようになった。
リセットは Active Low なので、使用時には上げておく必要がある。
LANG:console
$ echo 905 > /sys/class/gpio/export
$ echo out > /sys/class/gpio/gpio905/direction
$ echo 0 > /sys/class/gpio/gpio905/value # reset
$ echo 1 > /sys/class/gpio/gpio905/value # release reset
* LCD との通信 [#cdba4dc7]
LCD のアドレスは 0x7c だそうだ(後で間違いであったことが発覚)。
- int i2c = open("/dev/i2c-1", O_RDWR);
- ioctl(i2c, I2C_SLAVE, 0x7c);
- write(i2c, &bytes, length_of_bytes);
- read(i2c, &buffer, length_to_receive);
の組み合わせでやりとりすればいい・・・のかと思ったのだけれど、
https://www.kernel.org/doc/Documentation/i2c/dev-interface
を見ると、
> Note that only a subset of the I2C and SMBus protocols can be achieved by
the means of read() and write() calls. In particular, so-called combined
transactions (mixing read and write messages in the same transaction)
aren't supported. For this reason, this interface is almost never used by
user-space programs.
などと書かれてた orz
LCD の制御だけが目的の場合にはデータの読み出しを必要としないので、
まあそこそこ行ける・・・かな?
試してみる。
* ちょっとうまく行かないので調べ中 [#f00a2f54]
まずはこれだけ。
LANG:c
int i2c = open("/dev/i2c-1",O_RDWR);
ioctl(i2c, I2C_SLAVE, 0x7c);
write(i2c, "\x00\x38", 2);
close(i2c);
オシロスコープで見た信号は、
&tchart(
@w_hold 5
@w_transient 1
SCL ~~~___|~~__|~~__|~~__|~~__|~~__|~~__|~~__|~~__|~~__|~~~~~
SDA ~~|___~~~~~~~~~~~~~~~~~~~~____________~~~~____|~~
DATA -=S=--=1=--=1=--=1=--=1=--=1=--=0=--=0=--=0=--=1=--=0=-=P=-
HEX -----==========7=X==============C=XW===XNA===-------
);
のようになっていた。
例えば http://www.picfun.com/c15.html を参考に波形を読み解くと、
- SCL = 1 における SDA negedge がスタートコンディション (S)
- 以降、SCL = 0 でのみ SDA の遷移が許される
- SCL posedge にてデータの読み取り
- 1バイト目は 7bit のスレーブアドレスに R/W フラグを付けたもの
- 今の場合は (0x7C << 1) + (write ? 0 : 1) の write モードになる
- スレーブが自分のアドレスを受け取ると、上のチャートで NA となっているところで SDA を 0 にする(Acknowledge)のだが、実測ではここが1になっているのでスレーブが反応していない状況だ
- SCL = 1 における SDA posedge がストップコンディション (P)
- ストップを送るためには SDA を一旦 0 にしなければならないので、直前にマスターが 0 を出している
思った通りの信号が出ているにもかかわらずスレーブが応答しない理由は・・・まさかアドレスが間違ってる?!
もう一度 LCD のマニュアルを見直すと、
#ref(lcd-address.png,,50%,around);
でかでかと Slave Address = 0x7C と書いておきながら、
サンプルコード等で実際に送っているアドレスは 0b0111110 = 0x3e だった orz
すっかり騙されたよ。
ioctl(i2c, I2C_SLAVE, 0x3e); にしたところ、うまく行き始めた。
* 操作プログラム [#k04ab626]
こういうプログラムを作った。
lcd-control : https://github.com/osamutake/zynq-utils/tree/master/src/lcd-control
マニュアルによれば、これを使って、
LANG:console
$ sudo lcd-control -c 0x38
$ sudo lcd-control -c 0x39
$ sudo lcd-control -c 0x14 0x78 0x5e 0x6d 0x0c 0x06 0x01
$ sudo lcd-control "Hello, world!"
で良いはずなのだけれど、実際には
- コントラスト、というか、黒が強すぎる
- 1行目の上半分が表示されない
の不具合があった。
コントラストについては、0x5e となっていたところを 0x5d にすれば良くなった。
1行目の上半分が表示されないのは、たまたま当たってしまった LCD の初期不良な可能性が高い。~
→ LCD を指で押すと表示されない領域が増えたり減ったりする orz
ということで、以下のようにして使えるようになった。
LANG:console
$ # 初期化
$ sudo lcd-control -c 0x38
$ sudo lcd-control -c 0x39
$ sudo lcd-control -c 0x14 0x78 0x5d 0x6d 0x0c 0x06
$ # 通常の使い方
$ sudo lcd-control -c 0x01 # 画面消去&ホームポジション
$ sudo lcd-control -c 0xc0 # 2行目先頭へ移動
$ sudo lcd-control "Hello, world!"
$ # IP アドレスを表示
$ sudo lcd-control -c 0x01 # 画面消去&ホームポジション
$ sudo lcd-control -c 0xc0 # 2行目先頭へ移動
$ sudo lcd-control `ip a s eth0 | grep "inet " | sed -e "s/.*inet //" | sed -e "s/\/.*//"`
元々この LCD は Ethernet 経由でデバイスを制御する目的で
IP アドレスを表示するためのものだったので、さしあたりは
1行しか表示できなくてもまあなんとかなりそう。
* LAN ケーブルの抜き差しに応じて表示を変更する [#x7edb681]
eth0 が有効・無効になった直後に LCD 表示を更新する。
/usr/local/bin/lcd-show-ip
LANG:sh
#!/bin/sh
test -e /sys/class/gpio/gpio905 || exit 1
# Deassert active-low reset
echo out > /sys/class/gpio/gpio905/direction
echo 0 > /sys/class/gpio/gpio905/value # reset
echo 1 > /sys/class/gpio/gpio905/value # release reset
lcd-control -c 0x38
lcd-control -c 0x39
lcd-control -c 0x14 0x7c 0x5d 0x6d 0x0c 0x06 0x01
lcd-control -c 0x01 # clear screen
lcd-control -c 0xc0 # move to second line
# show ip address
ADDR=`ip a s eth0 | grep "inet " | sed -e "s/.*inet //" | sed -e "s/\/.*//"`
if [ -z $ADDR ]; then
lcd-control "NO ADDRESS"
else
lcd-control $ADDR
fi
を用意して、
LANG:console
$ sudo chown root:root /usr/local/bin/lcd-show-ip
$ sudo ln -s /usr/local/bin/lcd-show-ip /usr/lib/networkd-dispatcher/routable.d/lcd-show-ip
$ sudo ln -s /usr/local/bin/lcd-show-ip /usr/lib/networkd-dispatcher/no-carrier.d/lcd-show-ip
としたところ、
- LAN ケーブルを抜くと NO ADDRESS を表示
- LAN ケーブルを挿すと dhcp で自動取得した IP アドレスを表示
を実現できた。
作業履歴へ戻る → [[電気回路/zynq]]
* コメント・質問 [#d67302dc]
#article_kcaptcha
Counter: 3889 (from 2010/06/03),
today: 1,
yesterday: 1