Petalinux2018.3でキャラクタLCD制御(ST7032i) のバックアップソース(No.7)
更新- バックアップ一覧
- 差分 を表示
- 現在との差分 を表示
- バックアップ を表示
- 電気回路/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 * 環境 [#h9cf1202] [[作業履歴>電気回路/zynq/Petalinux2018.3環境を整える#wa39ab55]] のように 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.c LANG:c #include <stdio.h> #include <fcntl.h> #include <unistd.h> #include <sys/ioctl.h> #include <stdlib.h> #include <string.h> #include <linux/i2c-dev.h> #define DEV_I2C "/dev/i2c-1" #define LCD_ADDR 0x3e int lcd_write(const unsigned char bytes[], int length, int is_cmd) { int i2c = open(DEV_I2C,O_RDWR); if(i2c < 0) { fprintf(stderr, "ERROR: unable to open I2C device \"%s\".\n", DEV_I2C); return i2c; } if (ioctl(i2c, I2C_SLAVE, LCD_ADDR)<0) { perror("ioctl(I2C_SLAVE)"); exit(1); } unsigned char *buffer = (unsigned char *)malloc(length+2); buffer[0] = is_cmd ? 0x00 : 0x40; /* control byte */ strcpy(buffer+1, bytes); /* data bytes */ write(i2c, buffer, length+1); free(buffer); close(i2c); return 0; } unsigned int my_str2num(const char str[]) { unsigned int value; char *p; if (strlen(str)>2 && str[0]=='0' && ((str[1]=='x')||str[1]=='X')) { value = strtol(str+2, &p, 16); /* hex */ } else { value = strtol(str, &p, 10); /* dec */ } if (*p!=0) { fprintf(stderr, "ERROR: Irregal number \"%s\" was given.\n", str); exit(1); } return value; } void show_usage() { fprintf(stderr, "USAGE:\n"); fprintf(stderr, "lcd-control \"Hello!\" \n"); fprintf(stderr, "lcd-control -c 0x39 0x14 0x78 ...\n"); exit(1); } int main(int argc, const char *argv[]) { int i; unsigned char *buffer; if (argc < 2) show_usage(); if (strcmp(argv[1], "-c")==0) { buffer = (unsigned char*)malloc(argc-2); for(i = 2; i < argc; i++) { buffer[i-2] = my_str2num(argv[i]); } lcd_write(buffer, argc-2, 1); free(buffer); } else { for(i = 1; i < argc; i++) { if (i != 1) lcd_write(" ", 1, 0); lcd_write(argv[i], strlen(argv[i]), 0); } } return 0; } マニュアルによれば、これを使って、 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行しか表示できなくてもまあなんとかなりそう。 * コメント・質問 [#d67302dc] #article_kcaptcha
Counter: 3301 (from 2010/06/03),
today: 3,
yesterday: 0