AppliedMicro APM82181上のWD MyBook LiveからキャラクターLCDをボードに接続します。 終了

こんにちは NAS WesternDigital MyBook Liveのボードとそれに接続されたLCDインジケーターを引き続き使用します。 そのため、前のパートでは、ボード上にI2Cバスに接続する場所を見つけ、ポートエクスパンダーとインジケーターを接続して、すべてが機能することを確認しました。 今日、私たちはインジケータにシステムのステータスを表示します。

画像

画像
始まりはここにありました: AppliedMicro APM82181上のWD MyBook LiveからキャラクターLCDをボードに接続



最初の部分の内容:



1.コンソールの接続

2.ディスクなしでダウンロード

3. LEDEでのコンパイル

4.ポート管理(LuCIおよびコンソール経由)

5. I2Cバスへの接続

6. PCF8574ポートエクスパンダーの接続


今日私達は考慮します:



7. i2csetによるHD44780の初期化

8. I2Cバスにデータを書き込むためのキャラクターデバイス

9.カーネルへのHD44780ドライバーの追加

10.必要なVT100コマンドの処理をHD44780ドライバーに追加する

11.いくつかのVT100コマンドを含むディスプレイをLCD4Linuxに追加する

12. HD44780ドライバーへの文字ジェネレータープログラミングコマンドの追加

13. I2Cバス上のデータ伝送の最適化


以前と同様に、追加やコメントを歓迎します。



そのため、この時点で、ポートエクスパンダーがシステムに接続され、これを制御できます。 HD44780コントローラーのクローンのシンボルLCDインジケーターがエキスパンダーに接続されています。 理論的には、出力用にすべてのポートをオンにし、その目的を知ることで制御できます。 バックライトを点滅させて、3番目のポートをヤンクすることはすでに可能でした。



7. i2csetによるHD44780の初期化



HD44780コントローラーとポートエクスパンダー間の接続は、次のように構成されています。



RS-P0

R / W-P1

E-P2

BL-P3

D4-P4

D5-P5

D6-P6

D7-P7


これは、見つかったオプションの1つです。 コントローラは変換され、4ビットモードで動作し、バイトは部分的に送信されます。



エクスパンダーのすべてのポートを自由に使用できるため、ビットごとにデータを発行して、ディスプレイを制御できます。 これはあまり便利ではないということに同意していただけると思います。



I2Cバス経由で直接制御してみましょう。 この可能性をテストする簡単なオプションは、I2C-toolsユーティリティスイートを使用することです。 LEDEでは、Utilitesセクションにあります。 キットには、i2cdetect、i2cdump、i2cget、i2csetが含まれています。 私たちは後者に興味があり、少しは診断に興味があります。



i2cdetectを使用して、バスに接続されているデバイスを検出し、それらのアドレスを決定できます。



この例では、アドレス0x27のみがビジーです。
root@lede: i2cdetect 0 WARNING! This program can confuse your I2C bus, cause data loss and worse! I will probe file /dev/i2c-0. I will probe address range 0x03-0x77. Continue? [Y/n] y 0 1 2 3 4 5 6 7 8 9 abcdef 00: -- -- -- -- -- -- -- -- -- -- -- -- -- 10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 20: -- -- -- -- -- -- -- 27 -- -- -- -- -- -- -- -- 30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 70: -- -- -- -- -- -- -- --
      
      







i2csetユーティリティは、I2Cバス上の特定のアドレスを持つデバイスにデータを出力するために使用されます。

LCDの初期化シーケンスがわかれば、簡単に実行して画面に文字を表示できます。



車輪を再発明しないために、ここからダウンロードすることをお勧めします: PCF8574エキスパンダー 「i2c lcd testilik」のI2C hd44780モジュール 。 ここに直接リンクがあります。 シェルアーカイブ内には、i2csetコマンドを介してインジケーターと連動し、画面に文字を交互に表示するスクリプトがあります。 使用前の唯一のことは、ファイルの先頭の行をコメントアウトまたは削除することです。



 insmod i2c-dev insmod i2c-gpio-custom bus0=0,$sda_gpio,$scl_gpio
      
      





空きI / OポートにI2Cソフトウェアポートを作成しますが、既にハードウェアポートがあります。 それに、4 * 40ディメンションのインジケーター用に設計されていますが、パフォーマンスをチェックし、i2csetユーティリティの使用を理解するのに問題はありません。



結果:



画像






その実装に関する簡単な説明。 プロシージャwrite_CMDおよびprint_LCDは、それぞれインジケーターにコマンドまたはデータを表示します。 これはRS信号に依存します。この場合、ゼロビットに依存します。



init_LCDプロシージャは、データシートに従ってインジケータを初期化するためのコマンドを順次発行します。データシートはインターネット上で広く配布されています。 たとえば、 こちら 。 次に、さまざまな文字が画面に順次表示されます。



8. I2Cバスにデータを書き込むためのキャラクターデバイス



すべては問題ありませんが、ユーティリティの使用を避け、文字デバイスを使用して、指定されたアドレスでI2Cバスに直接入るバイトを表示したいと思います。



残念ながら、LEDEにはI2Cバス用のそのようなドライバーが見つかりませんでした。 そのため、実験的な目標により、既存の目標の1つをやり直すことが決定されました。 さらに使用したい場合は、やり直す必要はありませんが、少なくともそのベースに基づいて新しいものを作成することは明らかです。



I2Cバス用のEEPROMドライバーは、実験に適していることが判明しました。 kmod-eeprom-at24ドライバーはLEDEコアに接続されていました。システムを更新した後、デバイスを追加しようとしました:



 root@lede: echo 24c00 0x27 > /sys/bus/i2c/devices/i2c-0/new_device root@lede: echo 24c00 0x27 > [ 33.335472] at24 0-0027: 16 byte 24c00 EEPROM, writable, 1 bytes/write [ 33.342102] i2c i2c-0: new_device: Instantiated device 24c00 at 0x27
      
      





接続に成功しました。 デバイスに何かを出力する場合:



 root@lede: echo "1111" > /sys/bus/i2c/devices/i2c-0/0-0027/eeprom
      
      





次に、バス上に次のバイトシーケンスが表示されます。



 0x4e-0x00-0x31 0x4e-0x01-0x31 0x4e-0x02-0x31 0x4e-0x03-0x31 0x4e-0x04-0x0a
      
      





各トリプルの最初のバイトは、デバイスアドレスに2(0x27 x 2)を掛けたものです。 2番目はEEPROMのセルアドレス、3番目はデータです。 ドライバーは、セルアドレスの発行を除き、LCDにデータを送信するのに非常に適しています。



これを削除するには、ドライバーファイルbuild_dir / target-powerpc_464fp_musl-1.1.15 / linux-apm821xx_sata / linux-4.4.21 / drivers / misc / eeprom / at24.cを修正します。 at24_eeprom_writeプロシージャ(335-337)の数行をコメントアウトします。



 //if (at24->chip.flags & AT24_FLAG_ADDR16) // msg.buf[i++] = offset >> 8; //msg.buf[i++] = offset;
      
      





コンパイル更新、デバイスの追加、出力の確認



 root@lede: echo 24c00 0x27 > /sys/bus/i2c/devices/i2c-0/new_device [ 2708.782356] at24 0-0027: 16 byte 24c00 EEPROM, writable, 1 bytes/write [ 2708.788891] i2c i2c-0: new_device: Instantiated device 24c00 at 0x27 root@lede: echo "1111" > /sys/bus/i2c/devices/i2c-0/0-0027/eeprom
      
      





すべてが正しい、セルアドレスなしの出力、必要なもののみ:



画像



ここから、i2csetユーティリティ呼び出しを削除して、テストプログラムをやり直すことができます。
 #!/bin/sh i2c_adres=0x27 i2c_dev=/sys/bus/i2c/devices/i2c-0/0-0027/eeprom led=8 ansi=0 to_octal () { hh3=$(($hh / 64)) hh1=$(($hh - $hh3 * 64)) hh2=$(($hh1 / 8)) hh1=$(($hh1 - $hh2 * 8)) } write_CMD () { : $((hb = $c & 240)) : $((lb = ($c << 4) & 240 )) hh=$((4 + $hb + $led)) to_octal echo -e -n \\$hh3$hh2$hh1 >> $i2c_dev hh=$((0 + $hb + $led)) to_octal echo -e -n \\$hh3$hh2$hh1 >> $i2c_dev hh=$((4 + $lb + $led)) to_octal echo -e -n \\$hh3$hh2$hh1 >> $i2c_dev hh=$((0 + $lb + $led)) to_octal echo -e -n \\$hh3$hh2$hh1 >> $i2c_dev } print_LCD () { : $((hb = $c & 240)) : $((lb = ($c << 4) & 240 )) hh=$((5 + $hb + $led)) to_octal echo -e -n \\$hh3$hh2$hh1 >> $i2c_dev hh=$((1 + $hb + $led)) to_octal echo -e -n \\$hh3$hh2$hh1 >> $i2c_dev hh=$((5 + $lb + $led)) to_octal echo -e -n \\$hh3$hh2$hh1 >> $i2c_dev hh=$((1 + $lb + $led)) to_octal echo -e -n \\$hh3$hh2$hh1 >> $i2c_dev } ########## init LCD ##################### init_LCD () { if [[ ! -w $i2c_dev ]] then echo 24c00 0x27 > /sys/bus/i2c/devices/i2c-0/new_device sleep 0.5 fi sleep 0.5 c=3 write_CMD c=3 write_CMD c=2 write_CMD c=40 #28 write_CMD c=44 #2C write_CMD c=44 #2C write_CMD c=12 #0C write_CMD c=1 write_CMD sleep 0.2 c=6 write_CMD c=2 write_CMD } ############################### init_LCD c=0x80 # stroka - 1 write_CMD for i in `seq 32 63`; do if [ "$i" == 48 ]; then c=0xC0 # stroka - 2 write_CMD fi c=$(($i + $ansi)) print_LCD done sleep 3 c=0x80 # stroka - 1 write_CMD for i in `seq 64 95`; do if [ "$i" == 80 ]; then c=0xC0 # stroka - 2 write_CMD fi c=$(($i + $ansi)) print_LCD done sleep 3 c=0x80 # stroka - 1 write_CMD for i in `seq 96 127`; do if [ "$i" == 112 ]; then c=0xC0 # stroka - 2 write_CMD fi c=$(($i + $ansi)) print_LCD done
      
      







同時に、現在、プログラムは画面ジオメトリ(2x16文字)専用に設計されています。 build_dirディレクトリのソースを変更すると、近い将来、アセンブリ中に元のパッケージからファイルが復元されると予想されるはずです。 永続的な修正を作成するには、ビルド段階でパッチを適用する機能を使用します。



9.カーネルへのHD44780ドライバーの追加



このLCD接続オプションの操作性の問題を研究した後、インジケータにいくつかの機能を割り当てることを試みることが決定されました。 たとえば、オペレーティングシステムの状態の一部を表示します。



このようなパッケージは既に存在し、LEDEにも含まれています。 これはLCD4Linuxです。 これにより、OSのコンポーネントに関する必要な情報を取得し、適切な場所のインジケーターに配置できます。 当然、リアルタイム更新。



ただし、I2Cバスのインジケーターで使用すると、いくつかの問題が発生しました。

LCD4LinuxキットからディスプレイをHD44780-I2Cに接続する



ファイル\ etc \ lcd4linux.conf
 Display HD44780-I2C { Driver 'HD44780' Model 'generic' Bus 'i2c' Port '/dev/i2c-0' Device '0x27' Bits '4' Size '16x2' asc255bug 0 Icons 1 Wire { RW 'DB1' RS 'DB0' ENABLE 'DB2' GPO 'GND' } }
      
      



エラーの原因:
 root@lede: /usr/bin/lcd4linux -v -F LCD4Linux 0.11.0-SVN-1193 starting HD44780: $Rev: 1202 $ HD44780: using model 'generic' HD44780: using I2C bus HD44780: using 1 Controller(s) HD44780: using 4 bit mode udelay: using gettimeofday() delay loop Segmentation fault
      
      







前の章で作成されたEEPROMドライバーに基づいたキャラクターデバイスの使用を含め、パッケージからのほぼすべての可能なディスプレイもテストされました。 うまくいきませんでした。



それから、他の方法で行くことにしました。 この特定のインジケーターのドライバーをシステムに追加します。システムはシンボルと表示用の制御コマンドを受け入れ、このドライバーを使用してLCD4Linuxに新しいディスプレイを追加します。これにはマニュアルがあります。



そのため、ここからI2C上のHD44780の準備ができたドライバーを取得します: PCF8574 I / Oエクスパンダーを介してI2Cバスに接続されたHitachi HD44780 LCDのLinuxドライバー 。 このドライバーはRaspberry Piでテストされ、VT100端末制御コマンドをいくつか理解し、さまざまなインジケータージオメトリ用に構成され、カーソルを表示、消去、点滅させることができます。 それをLEDEに統合し、少し改良することは残っています。

ダウンロードし、パッケージ/ hd44780 / srcフォルダーに解凍します。



樹木
 ls -l -rw-r--r-- 1 root root 18092 Feb 21 2016 LICENSE -rw-r--r-- 1 root root 60 Nov 9 06:17 Makefile -rw-r--r-- 1 root root 1945 Feb 21 2016 README.md -rw-r--r-- 1 root root 10316 Nov 16 04:33 hd44780-dev.c -rw-r--r-- 1 root root 7756 Feb 21 2016 hd44780-i2c.c -rw-r--r-- 1 root root 1122 Nov 16 03:28 hd44780.h -rw-r--r-- 1 root root 235 Feb 21 2016 make.sh
      
      







Makefileにはこれだけを残します:



 obj-m := hd44780.o hd44780-y := hd44780-i2c.o hd44780-dev.o
      
      





そして、他のLEDEパッケージのファイルと同様に、パッケージ/ hd44780に上記のフォルダーのみの新しいMakefileを作成します。



パッケージ/ hd44780 / Makefile
 include $(TOPDIR)/rules.mk include $(INCLUDE_DIR)/kernel.mk PKG_NAME:=hd44780 PKG_RELEASE:=1 PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME) include $(INCLUDE_DIR)/package.mk define KernelPackage/hd44780 SUBMENU:=Other modules TITLE:=I2C HD44780 driver FILES:=$(PKG_BUILD_DIR)/hd44780.ko AUTOLOAD:=$(call AutoLoad,70,hd44780) KCONFIG:= endef define Package/hd44780/description Big comments.... ... endef MAKE_OPTS:= \ ARCH="$(LINUX_KARCH)" \ CROSS_COMPILE="$(TARGET_CROSS)" \ SUBDIRS="$(PKG_BUILD_DIR)" \ EXTRA_CFLAGS="$(EXTRA_CFLAGS)" define Build/Prepare mkdir -p $(PKG_BUILD_DIR) $(CP) ./src/* $(PKG_BUILD_DIR)/ endef define Build/Compile $(MAKE) -C "$(LINUX_DIR)" \ $(MAKE_OPTS) \ modules endef $(eval $(call KernelPackage,hd44780))
      
      







自動ロード(AUTOLOAD:= $(AutoLoad、70、hd44780を呼び出す))を含む行は、後でドライバーのテスト時に追加できます。 これで、LEDEコンフィギュレーターを呼び出すとき



 make menuconfig
      
      





ドライバーはカーネルモジュール(kmod-hd44780)に表示され、構成に追加できます。



LEDEの構成
画像



コンパイル、更新、再起動後、モジュールの自動ロードが有効になっていない場合は、ロードを試行し、結果を確認します。



 root@lede: insmod hd44780 root@lede: lsmod |grep 44780 hd44780 5450 0
      
      





デバイスを追加しようとしています:



 root@lede: echo hd44780 0x27 > /sys/bus/i2c/devices/i2c-0/new_device [ 9463.913178] i2c i2c-0: new_device: Instantiated device hd44780 at 0x27
      
      





初期化中に、ドライバーで設定されたインジケーターに、作成されたデバイスのアドレスが表示されます: "/ dev / lcd0"、最後に点滅カーソルがあります。



インジケーターに表示される文字をこのデバイスに送信できます。



 root@lede: echo -n 123 > /dev/lcd0
      
      





sysfs(/ sys / class / hd44780 / lcd0)を介して動作モードを制御することもできます。 このパスに沿ったファイル名は、backlight、cursor_display、geometry、cursor_blinkです。 それらを使用して、画面のジオメトリをカスタマイズし、カーソルモードとバックライトを制御できます。 たとえば、点滅カーソルをオフにするには、次のコマンドを入力します。



 root@lede: echo -n 0 > /sys/class/hd44780/lcd0/cursor_blink
      
      





さらに、2つのVT100端末コマンドがサポートされています。これにより、画面がクリアされ、カーソルが開始位置に設定されます。 次のように送信できます。



 root@lede: echo -n -e '\x1b'[2J > /dev/lcd0 root@lede: echo -n -e '\x1b'[H > /dev/lcd0
      
      





OSをロードするときに、必要なモードを設定することもできます。 これを行うには、以下の内容を含むターゲット/ linux / apm821xx / base-files / etc / board.d / 03_lcdファイルをLEDEに追加します。



 #!/bin/sh echo hd44780 0x27 > /sys/bus/i2c/devices/i2c-0/new_device echo -n 16x2 > /sys/class/hd44780/lcd0/geometry echo -n 0 > /sys/class/hd44780/lcd0/cursor_display echo -n 0 > /sys/class/hd44780/lcd0/cursor_blink echo -n -e '\x1b'[2JHello! > /dev/lcd0 exit 0
      
      





これで、システムを起動するたびにボードが挨拶します。



10.必要なVT100コマンドの処理をHD44780ドライバーに追加する



したがって、ドライバーは動作しますが、LCD4Linuxで使用するには、画面上の任意の位置に文字を配置できる必要があります。 ターミナルコマンドのリストに従って、必要なコマンドを選択します。



Esc [Line; ColumnH-カーソルを画面の位置v、hに移動



ファイルパッケージ/ hd44780 / src / hd44780-dev.cを見つけて、新しいコマンドの検出と実行を追加します。 escシーケンスを処理する手順を改良する必要があります。



オリジナル:
 static void hd44780_handle_esc_seq_char(struct hd44780 *lcd, char ch) { int prev_row, prev_col; lcd->esc_seq_buf.buf[lcd->esc_seq_buf.length++] = ch; if (!strcmp(lcd->esc_seq_buf.buf, "[2J")) { prev_row = lcd->pos.row; prev_col = lcd->pos.col; hd44780_clear_display(lcd); hd44780_write_instruction(lcd, HD44780_DDRAM_ADDR | (lcd->geometry->start_addrs[prev_row] + prev_col)); hd44780_leave_esc_seq(lcd); } else if (!strcmp(lcd->esc_seq_buf.buf, "[H")) { hd44780_write_instruction(lcd, HD44780_RETURN_HOME); lcd->pos.row = 0; lcd->pos.col = 0; hd44780_leave_esc_seq(lcd); } else if (lcd->esc_seq_buf.length == ESC_SEQ_BUF_SIZE) { hd44780_flush_esc_seq(lcd); } }
      
      





変更されたバージョン:
 static void hd44780_handle_esc_seq_char(struct hd44780 *lcd, char ch) { int prev_row, prev_col; struct hd44780_geometry *geo = lcd->geometry; lcd->esc_seq_buf.buf[lcd->esc_seq_buf.length++] = ch; if (!strcmp(lcd->esc_seq_buf.buf, "[2J")) { prev_row = lcd->pos.row; prev_col = lcd->pos.col; hd44780_clear_display(lcd); hd44780_write_instruction(lcd, HD44780_DDRAM_ADDR | (lcd->geometry->start_addrs[prev_row] + prev_col)); hd44780_leave_esc_seq(lcd); } else if (!strcmp(lcd->esc_seq_buf.buf, "[H")) { hd44780_write_instruction(lcd, HD44780_RETURN_HOME); lcd->pos.row = 0; lcd->pos.col = 0; hd44780_leave_esc_seq(lcd); } else if ((lcd->esc_seq_buf.buf[0]=='[') && (lcd->esc_seq_buf.buf[4]=='H') && // Esc[ Line ; Column H (lcd->esc_seq_buf.buf[2]==';' ) && (lcd->esc_seq_buf.length == 5)) { lcd->pos.row = lcd->esc_seq_buf.buf[1] % geo->rows; lcd->pos.col = lcd->esc_seq_buf.buf[3] % geo->cols; hd44780_write_instruction(lcd, HD44780_DDRAM_ADDR | (geo->start_addrs[lcd->pos.row] + lcd->pos.col)); hd44780_leave_esc_seq(lcd); } else if (lcd->esc_seq_buf.length == ESC_SEQ_BUF_SIZE) { hd44780_flush_esc_seq(lcd); } }
      
      





また、escシーケンスを蓄積して分析するには、バッファーの長さを変更する必要があります。 ファイルhd44780.hで次の行を見つけます。



 #define ESC_SEQ_BUF_SIZE 4
      
      





そして、値を4から6に修正します。コンパイルして確認できます。 LEDEの1つのパッケージのみをコンパイルできます。



 root@debian:/apm82181-lede-master# make package/hd44780/compile make[1] package/hd44780/compile make[2] -C package/hd44780 compile
      
      





エラーがない場合は、プロジェクト全体をコンパイルし、更新して再起動します。 私たちはチェックします:



 root@lede:/ echo -n -e '\x1b[1;6H' > /dev/lcd0
      
      





カーソルが2行目と7番目の位置に移動します(最初から番号が付けられます)。
画像








11.いくつかのVT100コマンドを含むディスプレイをLCD4Linuxに追加する



LCDドライバーはその機能を実行します。 これで、LCD4Linuxパッケージで使用してシステムのステータスを表示できるようになりました。 ただし、ターミナルプロトコルを使用するドライバーで動作するディスプレイは見つかりませんでした。



だからあなた自身を書いてください。 新しいディスプレイドライバの書き方によると。

ソースファイルは、build_dir / target-powerpc_464fp_musl-1.1.15 / lcd4linux-custom / lcd4linux-r1203ディレクトリ、またはdl / lcd4linux-r1203.tar.bz2パッケージから取得できます。



すべてがマニュアルのとおりです。



  1. drv_Sample.c drvファイルから、drv_vt100.cのコピーを作成します
  2. GPIOを使用して、drv_vt100.cを編集し、グラフィックモードに関連するすべてを削除します
  3. drv.cに新しいドライバーを追加します
  4. Makefile.amに追加



  5. drivers.m4に追加します
     if test "$VT100" = "yes"; then TEXT="yes" I2C="yes" DRIVERS="$DRIVERS drv_vt100.o" AC_DEFINE(WITH_VT100,1,[vt100 driver]) fi
          
          





  6. Makefile.amに追加


次に、drv_vt100.cファイルに手順を記述します。



drv_vt100_open:
 static int drv_vt100_open(const char *section) { char *s; int f = -1; s = cfg_get(section, "Port", NULL); if (s == NULL || *s == '\0' || strlen(s) > 80) { error("%s: no '%s.Port' entry from %s", Name, section, cfg_source()); return -1; } strcpy(Port, s); f = open(Port, O_WRONLY); if (f == -1) { error("open(%s) failed: %s", Port, strerror(errno)); return -1; } close (f); return 0; }
      
      







drv_vt100_send:
 static void drv_vt100_send(const char *data, const unsigned int len) { unsigned int i; int f; f = open(Port, O_WRONLY); write (f, data, len); close (f); }
      
      





drv_vt100_clear:
 static void drv_vt100_clear(void) { char cmd[4]; cmd[0] = 0x1B; // ESC cmd[1] = '['; // [ cmd[2] = '2'; // 2 cmd[3] = 'J'; // J drv_vt100_send(cmd, 4); cmd[2] = 'H'; // H drv_vt100_send(cmd, 3); }
      
      





drv_vt100_write:
 static void drv_vt100_write(const int row, const int col, const char *data, int len) { char cmd[6]; cmd[0] = 0x1B; // ESC cmd[1] = '['; // [ cmd[2] = row & 0xff; // Line cmd[3] = ';'; // ; cmd[4] = col & 0xff; // Column cmd[5] = 'H'; // H drv_vt100_send(cmd, 6); }
      
      





drv_vt100_closeは空のままにします。



LEDEプロジェクトとは別のフォルダーにファイルを編集して作成します。 次に、プロジェクトをコンパイルすると、LCD4linuxファイルがアーカイブから更新されるため、build_dir / ...フォルダーでそれらを変更しても意味がありません。 パッチを適用する機会を利用する必要があります。 LCD4Linuxのパッチは、feeds / packages / utils / lcd4linux / patchフォルダーにあります。 独自の新しいディスプレイドライバVT100の追加は、すぐに配置する必要があります。



パッチを作成するには、2つのフォルダーを並べて作成します。 一方(let 1 /)に元のファイルを配置し、もう一方(let 2 /)に同じファイルを配置しますが、変更します。 次に、diffコマンドを実行します。



 diff -Naur ./1 ./2 > 180-vt100.patch
      
      





その結果、およそ次の内容の180-vt100.patchファイルがあります。
 diff -Naur ./vt100/Makefile.am ./vt100-f/Makefile.am --- ./vt100/Makefile.am 2016-11-28 11:01:56.000000000 +0000 +++ ./vt100-f/Makefile.am 2016-11-14 07:33:41.000000000 +0000 @@ -125,6 +125,7 @@ drv_USBHUB.c \ drv_USBLCD.c \ drv_vnc.c \ +drv_vt100.c \ drv_WincorNixdorf.c \ drv_X11.c \ \ diff -Naur ./vt100/drivers.m4 ./vt100-f/drivers.m4 --- ./vt100/drivers.m4 2016-11-14 11:54:41.000000000 +0000 +++ ./vt100-f/drivers.m4 2016-11-14 07:37:00.000000000 +0000 @@ -39,7 +39,7 @@ [ Newhaven, Noritake, NULL, Pertelian, PHAnderson,] [ PICGraphic, picoLCD, picoLCDGraphic, PNG, PPM, RouterBoard,] [ Sample, SamsungSPF, serdisplib, ShuttleVFD, SimpleLCD, st2205, T6963,] - [ TeakLCM, TEW673GRU, Trefon, ULA200, USBHUB, USBLCD, VNC, WincorNixdorf, X11], + [ TeakLCM, TEW673GRU, Trefon, ULA200, USBHUB, USBLCD, VNC, vt100, WincorNixdorf, X11], drivers=$withval, drivers=all ) @@ -114,6 +114,7 @@ USBHUB="yes" USBLCD="yes" VNC="yes" + VT100="yes" WINCORNIXDORF="yes" X11="yes" ;; @@ -279,6 +280,9 @@ VNC) VNC=$val ;; + vt100) + VT100=$val + ;; WincorNixdorf) WINCORNIXDORF=$val ;; @@ -869,6 +873,13 @@ fi fi +if test "$VT100" = "yes"; then + TEXT="yes" + I2C="yes" + DRIVERS="$DRIVERS drv_vt100.o" + AC_DEFINE(WITH_VT100,1,[vt100 driver]) +fi + if test "$WINCORNIXDORF" = "yes"; then TEXT="yes" SERIAL="yes"
      
      





すべてのファイルに対して1つのパッチが作成されます。 feeds / packages / utils / lcd4linux / patchsフォルダーにすでにあるパッチを見ると、実行されているdiff -Naur ...コマンドを示す行はありません。 パッチを同じ状態にし、それをフォルダーにコピーします。

LEDE Con​​figuratorに入ります。



LCD4Linuc-customにディスプレイドライバーの外観が表示されます。
画像



これをプロジェクトに含め、設定を保存し、コンパイル、更新、再起動します。 構成ファイルにドライバーを含めます。



lcd4linux.conf
 Variables { tick 500 tack 100 minute 60000 } Display VT100 { Driver 'vt100' Size '16x2' Port '/dev/lcd0' } Widget Test { class 'Text' expression '1234567890123456' width 16 } Layout Test { Row01.Col1 'Test' Row02.Col1 'Test' } Display 'VT100' Layout 'Test'
      
      







私たちはチェックします:
 root@lede:/# /usr/bin/lcd4linux -v -F LCD4Linux 0.11.0-SVN-1193 starting vt100: $Rev: 001 $ initializing layout 'Test' Creating new timer group (1000 ms) widget 'Test': Class 'text', Parent '<root>', Layer 1, Row 0, Col 0 (to 0,16) widget 'Test': Class 'text', Parent 'Test', Layer 1, Row 1, Col 0 (to 1,16)
      
      







設定で計画されているように、インジケータに数字が表示されます。



画像



12. HD44780およびVT100ドライバーへの文字ジェネレータープログラミングコマンドの追加



HD44780コントローラーに基づくインジケーターには、数字と記号を含む有線ラテンアルファベットと8つの自由にプログラム可能な文字があります(ロシア語の文字もありますが、この場合はありません)。 プログラム可能なキャラクターの表示を変更することにより、簡単なアニメーションを取得できます。 そのオプションはLCD4Linuxの構成例に示されていますが、ドライバーはこの機能をサポートしていませんが、使用することはできません。



これについて複雑なことは何もありません.1つのドライバーで8バイトを受け入れ、コマンドで文字ジェネレーターに送信し、別のドライバーで送信する必要があります。



再度修正する

hd44780-dev.cのプロシージャhd44780_handle_esc_seq_char
 static void hd44780_handle_esc_seq_char(struct hd44780 *lcd, char ch) { int prev_row, prev_col; struct hd44780_geometry *geo = lcd->geometry; if (lcd->is_in_set_char == 0) { lcd->esc_seq_buf.buf[lcd->esc_seq_buf.length++] = ch; if (!strcmp(lcd->esc_seq_buf.buf, "[2J")) { prev_row = lcd->pos.row; prev_col = lcd->pos.col; hd44780_clear_display(lcd); hd44780_write_instruction(lcd, HD44780_DDRAM_ADDR | (lcd->geometry->start_addrs[prev_row] + prev_col)); hd44780_leave_esc_seq(lcd); } else if (!strcmp(lcd->esc_seq_buf.buf, "[H")) { hd44780_write_instruction(lcd, HD44780_RETURN_HOME); lcd->pos.row = 0; lcd->pos.col = 0; hd44780_leave_esc_seq(lcd); } else if ((lcd->esc_seq_buf.buf[0]=='[') && (lcd->esc_seq_buf.buf[4]=='H') && // Esc[ Line ; Column H (lcd->esc_seq_buf.buf[2]==';' ) && (lcd->esc_seq_buf.length == 5)) { lcd->pos.row = lcd->esc_seq_buf.buf[1] % geo->rows; lcd->pos.col = lcd->esc_seq_buf.buf[3] % geo->cols; hd44780_write_instruction(lcd, HD44780_DDRAM_ADDR | (geo->start_addrs[lcd->pos.row] + lcd->pos.col)); hd44780_leave_esc_seq(lcd); } else if (!strcmp(lcd->esc_seq_buf.buf, "(S")) { // Esc(S code matrix(8) lcd->is_in_set_char = 1; } else if (lcd->esc_seq_buf.length == ESC_SEQ_BUF_SIZE) { hd44780_flush_esc_seq(lcd); } } else if (lcd->is_in_set_char == 1) { // start set CGRAM code hd44780_write_instruction(lcd, HD44780_CGRAM_ADDR | 8 * (ch & 0x07)); lcd->is_in_set_char++; } else { hd44780_write_data(lcd, ch & 0x1f); // set 8 bytes CGRAM code lcd->is_in_set_char++; if (lcd->is_in_set_char == 10){ // go to DDRAM mode hd44780_write_instruction(lcd, HD44780_DDRAM_ADDR | (geo->start_addrs[lcd->pos.row] + lcd->pos.col)); hd44780_leave_esc_seq(lcd); } } }
      
      





8バイトの文字ジェネレーターを受け取り、CGRAM(文字ジェネレーター)に書き込むモードを追加します。 VT100でESC文字プログラミングシーケンスが見つからなかったため、何かを考え出す必要がありました。 それをEsc(S 8バイト、つまりESCコード、次に左括弧、ラテン文字S、8バイトのマトリックス)とします。この場合、親しみやすさのサイズ8 * 5では、各バイトの最下位5ビットのみが使用されます。



プロシージャhd44780_writeで、文字ジェネレーターの受信モードのリセットを追加します(行lcd-> is_in_set_char = 0)。



hd44780_write
  void hd44780_write(struct hd44780 *lcd, const char *buf, size_t count) ... case '\e': lcd->is_in_esc_seq = true; lcd->is_in_set_char = 0; break; default: hd44780_write_char(lcd, ch); ...
      
      





そして、この構造フィールド(is_in_set_char)をヘッダーファイルhd44780.hに記述します。



struct hd44780
 struct hd44780 { struct cdev cdev; struct device *device; struct i2c_client *i2c_client; struct hd44780_geometry *geometry; struct { int row; int col; } pos; char buf[BUF_SIZE]; struct { char buf[ESC_SEQ_BUF_SIZE]; int length; } esc_seq_buf; bool is_in_esc_seq; int is_in_set_char; bool backlight; bool cursor_blink; bool cursor_display; bool dirty; struct mutex lock; struct list_head list; };
      
      





次に、この機能をLCD4Linuxディスプレイドライバーに追加します。 drv_vt100.cファイルのdrv_vt100_defchar関数:



drv_vt100_defchar
 static void drv_vt100_defchar(const int ascii, const unsigned char *matrix) { char cmd[12]; int i; /* call the 'define character' function */ cmd[0] = 0x1B; // ESC cmd[1] = '('; // ( cmd[2] = 'S'; // S cmd[3] = ascii & 0x07; // code /* send bitmap to the display */ for (i = 0; i < 8; i++) { cmd[i + 4] = (*matrix++) & 0x1f; } drv_vt100_send(cmd, 12); }
      
      





コンパイル、更新、再起動。 LCD4linux構成を再度変更します。



lcd4linux.conf
 Variables { tick 500 tack 100 minute 60000 } Display VT100 { Driver 'vt100' Size '16x2' Port '/dev/lcd0' Icons 1 } Widget RAM { class 'Text' expression meminfo('MemFree')/1024 postfix ' MB RAM' width 11 precision 0 align 'R' update tick } Widget Busy { class 'Text' expression proc_stat::cpu('busy', 500) prefix 'Busy' postfix '%' width 9 precision 1 align 'R' update tick } Widget Uptime { class 'Text' expression uptime('%d days %H:%M:%S') width 20 align 'R' prefix 'Up ' update 1000 } Widget Uptime { class 'Text' expression 'Up '.uptime('%d %H:%M:%S') width 16 align 'L' update 1000 } # Icons Widget Timer { class 'Icon' speed 83 Bitmap { Row1 '.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|' Row2 '.***.|.*+*.|.*++.|.*++.|.*++.|.*++.|.*++.|.*++.|.*++.|.*++.|.*++.|.*++.|.+++.|.+*+.|.+**.|.+**.|.+**.|.+**.|.+**.|.+**.|.+**.|.+**.|.+**.|.+**.|' Row3 '*****|**+**|**++*|**+++|**++.|**++.|**+++|**+++|**+++|**+++|**+++|+++++|+++++|++*++|++**+|++***|++**.|++**.|++***|++***|++***|++***|++***|*****|' Row4 '*****|**+**|**+**|**+**|**+++|**+++|**+++|**+++|**+++|**+++|+++++|+++++|+++++|++*++|++*++|++*++|++***|++***|++***|++***|++***|++***|*****|*****|' Row5 '*****|*****|*****|*****|*****|***++|***++|**+++|*++++|+++++|+++++|+++++|+++++|+++++|+++++|+++++|+++++|+++**|+++**|++***|+****|*****|*****|*****|' Row6 '.***.|.***.|.***.|.***.|.***.|.***.|.**+.|.*++.|.+++.|.+++.|.+++.|.+++.|.+++.|.+++.|.+++.|.+++.|.+++.|.+++.|.++*.|.+**.|.***.|.***.|.***.|.***.|' Row7 '.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|' Row8 '.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|' } } Layout L16x2 { Row1 { Col1 'Uptime' col16 'Timer' } Row2 { Col1 'Busy' Col11 'RAM' } } Display 'VT100' Layout 'L16x2'
      
      





私たちはチェックします:



 root@lede: /usr/bin/lcd4linux -v -F LCD4Linux 0.11.0-SVN-1193 starting vt100: $Rev: 001 $ vt100: reserving 1 of 8 user-defined characters for icons initializing layout 'L16x2' Creating new timer group (1000 ms) widget 'Uptime': Class 'text', Parent '<root>', Layer 1, Row 0, Col 0 (to 0,16) Creating new timer group (83 ms) widget 'Timer': Class 'icon', Parent '<root>', Layer 1, Row 0, Col 15 (to 1,16) Creating new timer group (500 ms) widget 'Busy': Class 'text', Parent '<root>', Layer 1, Row 1, Col 0 (to 1,9) widget 'RAM': Class 'text', Parent '<root>', Layer 1, Row 1, Col 10 (to 1,21)
      
      





画面には、最後のブートからのボードの動作時間、システムのブート、空きメモリ、充填およびクリーニングディスクの形のアニメーションシンボルが表示されます。



画像






パッチ180-vt100.patchにLCD4Linux構成修正を追加すると、ブート時に同じ種類のインジケーターが表示されます。



180-vt100.patch
 --- a/lcd4linux.conf.sample 2016-11-15 09:47:46.000000000 +0000 +++ af/lcd4linux.conf.sample 2016-11-18 03:18:22.000000000 +0000 @@ -567,7 +567,14 @@ HttpPort '5800' } - +Display VT100 { + Driver 'vt100' + Size '16x2' + Port '/dev/lcd0' + Icons 1 +} + + Display FutabaVFD { Driver 'FutabaVFD' Port '/dev/parport0' @@ -674,7 +681,7 @@ Widget RAM { class 'Text' - expression meminfo('MemTotal')/1024 + expression meminfo('MemFree')/1024 postfix ' MB RAM' width 11 precision 0 @@ -828,6 +835,14 @@ update 1000 } +Widget Uptime { + class 'Text' + expression 'Up '.uptime('%d %H:%M:%S') + width 16 + align 'L' + update 1000 +} + Widget mpris_TrackPosition_bar { class 'Bar' expression mpris_dbus::method_PositionGet('org.kde.amarok') @@ -1015,7 +1030,7 @@ Widget Timer { class 'Icon' - speed 50 + speed 83 Bitmap { Row1 '.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|' Row2 '.***.|.*+*.|.*++.|.*++.|.*++.|.*++.|.*++.|.*++.|.*++.|.*++.|.*++.|.*++.|.+++.|.+*+.|.+**.|.+**.|.+**.|.+**.|.+**.|.+**.|.+**.|.+**.|.+**.|.+**.|' @@ -1225,6 +1240,17 @@ } } +Layout L16x2-2 { + Row1 { + Col1 'Uptime' + col16 'Timer' + } + Row2 { + Col1 'Busy' + Col11 'RAM' + } +} + Layout L20x2 { Row1 { Col1 'CPUinfo' @@ -1323,7 +1349,7 @@ -Display 'ACool' +#Display 'ACool' #Display 'SerDispLib' #Display 'LCD-Linux' #Display 'LCD2041' @@ -1354,7 +1380,7 @@ #Display 'IRLCD' #Display 'USBLCD' #Display 'BWCT' -#Display 'Image' +Display 'Image' #Display 'TeakLCD' #Display 'Trefon' #Display 'LCD2USB' @@ -1363,15 +1389,17 @@ #Display 'ctinclud' #Display 'picoLCD' #Display 'VNC' +Display 'VT100' #Display 'FutabaVFD' #Display 'GLCD2USB' -#Layout 'Default' -Layout 'TestLayer' +Layout 'Default' +#Layout 'TestLayer' #Layout 'TestImage' #Layout 'L8x2' #Layout 'L16x1' #Layout 'L16x2' +Layout 'L16x2-2' #Layout 'L20x2' #Layout 'L40x2' #Layout 'Test'
      
      







13. I2Cバス上のデータ伝送の最適化



これですべてが計画どおりに動作したので、データ転送速度について少し説明します。2つの点に注目したいと思います。



まず、I2Cバス上のデータは、非常に小さなブロック、つまり1バイトで送信されます。各ブロックには、スレーブデバイスのアドレスが追加されます。ユニットの大きいデバイスのアドレスを転送すると、バスの使用率が増加し、送信時間が短縮されると想定するのは論理的です。



これは、伝送が個別のバイトでどのように見えるかです
画像

1秒ごとにアドレスバイト(0x4E)であることがわかります。



部分的な最適化を実行します。これを行うには、1バイトのデータがインジケーターに送信される方法を覚えておいてください。LCDは4ビットモードで動作します。一度に半分のバイトを取得します。これらのハーフバイトは、「有効」信号を発行して確認する必要があります。その結果、1バイトをプロセッサーからI2Cバスのインジケーターに転送するには、6バイトがあります。



  1. 「有効」信号のない上位ハーフバイト
  2. 「有効」信号を使用した上位ハーフバイト
  3. 「有効」信号のない上位ハーフバイト
  4. 「有効」信号のない下位ハーフバイト
  5. 信号「有効」の下位ハーフバイト
  6. 「有効」信号のない下位ハーフバイト


また、それぞれにアドレスが付随するため、実際には12バイトで、バス速度は100 KHzで1.2ミリ秒です。



同じ6バイトを送信することを提案しますが、1つのブロックで、1つのアドレスバイト、つまり 12ではなく7バイト。HD44780ドライバーからデータを転送するための元の手順。



hd44780_write_data、hd44780_write_nibble、pcf8574_raw_write(hd44780-dev.cから)
 static void pcf8574_raw_write(struct hd44780 *lcd, u8 data) { i2c_smbus_write_byte(lcd->i2c_client, data); } static void hd44780_write_nibble(struct hd44780 *lcd, dest_reg reg, u8 data) { data = (data << 4) & 0xF0; if (reg == DR) data |= RS; data = data | (RW & 0x00); if (lcd->backlight) data |= BL; pcf8574_raw_write(lcd, data); pcf8574_raw_write(lcd, data | E); pcf8574_raw_write(lcd, data); } static void hd44780_write_data(struct hd44780 *lcd, u8 data) { u8 h = (data >> 4) & 0x0F; u8 l = data & 0x0F; hd44780_write_nibble(lcd, DR, h); hd44780_write_nibble(lcd, DR, l); udelay(37 + 4); }
      
      





ドライバHD44780からの手順、パケットデータ転送用に修正。



hd44780-dev.cのhd44780_write_data
 static void hd44780_write_data(struct hd44780 *lcd, u8 data) { u8 h = (data >> 4) & 0x0F; u8 l = data & 0x0F; u8 buf[5]; h = (h << 4) & 0xF0; l = (l << 4) & 0xF0; h |= RS; l |= RS; h = h | (RW & 0x00); l = l | (RW & 0x00); if (lcd->backlight){ h |= BL; l |= BL; } buf[0] = h | E; buf[1] = h; buf[2] = l; buf[3] = l | E; buf[4] = l; i2c_smbus_write_i2c_block_data(lcd->i2c_client, h, 5, (const u8 *)(&buf[0])); udelay(37 + 4); }
      
      





これは、バイト0x1Fの転送がどのように見えるかです
画像



また、0.67ミリ秒かかります。



次に、デフォルトのバス速度は100 KHzであり、これは最大値ではありません。もちろん、LCDのポートエクスパンダーにはこのような速度が推奨されます。しかし同時に、多くの開発者は400 KHzでのスムーズな操作について話しています。もちろん、必要に応じて非標準モードを使用することは正当化され、障害がないことを徹底的にテストしますが、これを行う方法と何が起こるかしか言えません。



政権の包含に関する情報はインターネットで見つけることができなかったので、LEDEのソースコードを修正しなければなりませんでした。その結果、高速モードを有効にするための2つのオプションがあります。400 kHz。



1つ目は、パラメータをカーネルモジュールに渡すことです。モジュールはi2c-ibm_iicです。パラメーターはiic_force_fastです。そのため、起動時にi2c-ibm_iic.iic_force_fast = 1をカーネルパラメーターに追加する必要があります。これは、次のようにU-bootローダーで実行できます。



 setenv addmisc 'setenv bootargs ${bootargs} i2c-ibm_iic.iic_force_fast=1'
      
      





システムをロードすると、次のことができます。



  root@lede: dmesg | grep i2c [ 0.000000] Kernel command line: root=/dev_nfs rw nfsroot=192.168.1.10:/nfs/debian_ppc/rootfs ip=dhcp console=ttyS0,115200 i2c-ibm_iic.iic_force_fast=1 [ 4.770923] i2c /dev entries driver [ 4.774742] ibm-iic 4ef600700.i2c: using fast (400 kHz) mode [ 10.456041] i2c i2c-0: new_device: Instantiated device hd44780 at 0x27
      
      





2番目は、デバイスツリーでバスの動作モードを指定することです(apollo3g.dtsi、高速モードパラメーター):



 IIC0: i2c@ef600700 { compatible = "ibm,iic"; reg = <0xef600700 0x00000014>; interrupt-parent = <&UIC0>; interrupts = <0x2 0x4>; fast-mode; #address-cells = <1>; #size-cells = <0>; };
      
      





コンパイル後、TFTPサーバーのデバイスツリーを更新することを忘れないでください。そして結果:



 root@lede: dmesg | grep i2c [ 4.774585] i2c /dev entries driver [ 4.778396] ibm-iic 4ef600700.i2c: using fast (400 kHz) mode [ 10.464396] i2c i2c-0: new_device: Instantiated device hd44780 at 0x27 root@lede: ls -al /proc/device-tree/plb/opb/i2c@ef600700 -r--r--r-- 1 root root 4 Nov 18 04:13 #address-cells -r--r--r-- 1 root root 4 Nov 18 04:13 #size-cells drwxr-xr-x 2 root root 0 Nov 18 04:13 . drwxr-xr-x 12 root root 0 Nov 18 04:13 .. -r--r--r-- 1 root root 8 Nov 18 04:13 compatible -r--r--r-- 1 root root 0 Nov 18 04:13 fast-mode -r--r--r-- 1 root root 4 Nov 18 04:13 interrupt-parent -r--r--r-- 1 root root 8 Nov 18 04:13 interrupts -r--r--r-- 1 root root 4 Nov 18 04:13 name -r--r--r-- 1 root root 8 Nov 18 04:13 reg
      
      





また、バイト転送レートは0.19ミリ秒です。



画像



これは、オリジナルよりもほぼ1桁優れています。



結論として、完了した作業の結果として、Linux(LEDE)が適用可能なプロジェクトのオープンソースで文書化されていないプロセッサでボードを使用する機会を得たと言えます。メインイーサネットインターフェイス、SATA上のストレージ、I2C経由の管理、およびいくつかのポートは、開発者に十分な機会を提供します。



そして最後に、上記のすべてを複製するために、ディレクトリ構造に従ってLEDEからのファイル(すべてを覚えていたように)が利用可能です



All Articles