2022年 SPRESENSE™ 活用コンテスト 」開催中!ローパワーでハイパフォーマンスなエッジコンピューティングを、あなたの手で活用してみませんか?

eucalyのアイコン画像
eucaly 2022年03月05日作成 (2022年03月05日更新) © MIT
製作品 製作品 閲覧数 912
eucaly 2022年03月05日作成 (2022年03月05日更新) © MIT 製作品 製作品 閲覧数 912

HDMIのパネル情報をArduinoで読んでみる

HDMIのパネル情報をArduinoで読んでみる

こんにちは、ゆうかりです。

まあ、ちょっとしたトラブルの解析用に実験的に作ったものなのですが。
犯人ココじゃない!、の証明になって、まぁこのナレッジ埋めとくのもアレなので。
備忘録的に公開しておきます。

あ、こんなもん作らんでも、WindowsはレジストリにEDIDバイナリ文字列を保存しています。
普通はコッチをご参照くださいな・・・。
キャプションを入力できます

EDID / 拡張表示器個別識別情報

まあ、プラグアンドプレイが騒がれ始めたWindows95あたりから、ディスプレイやモニターの解像度や各種タイミングを、ディスプレイ側のROMに書き込んでPCが読み込む仕組みができあがりました。
いわゆる、Vesa DDC、てやつです。
https://ja.wikipedia.org/wiki/VESA_Display_Data_Channel

んで、そこに使われるデータ形式を「EDID」というのですが。
https://en.wikipedia.org/wiki/Extended_Display_Identification_Data

まあこれ、実態は、
「I2C接続されたROM」なので。
Arduino経由で読んでみましょうか、というお話です。

留意点

ディスプレイ側実装にもよりますが、特に暗号化(HDCPとか)が無いディスプレイの場合、本当に「I2C経由のROM」が、ディスプレイ側にぶら下がっているだけ、です。
間違えて書き込みコマンド発行したりすると、データが吹っ飛ぶ可能性があるので。
気を付けて下さいね!。

古典的な実装と、近代的な実装

もともとは、上記の通り、「I2C接続されたROM」だったのですが。
ディスプレイケーブルの中で、唯一の「双方向通信が出来る信号線」なので。
様々な規格連中に狙われており、まあ便利に使われています。
例えば、HDMIのリピーター実装、これはEDID ROMの内容を書き換え、ソース側に情報を渡します。
例えばAVアンプ挟んだ時に、モニターの解像度とAVアンプの対応音源をくっつけて、PS4に「これ流せ」って要求したりね。
また、HDCPなどの暗号を使う場合、鍵交換にDDCラインを使います。
なので、条件によっては、ROMの内容が読めなかったりしたりして、アレ?。

具体的にどーなってるの?

ま、ブロック図的には単純です。
キャプションを入力できます
HDMIは、主データ線が単方向で3線+クロック1線、リモコン的な制御に使うCEC線が1線、そしてDDCラインが1系統あります。
ブロック図では省略されていますが、DDCラインには、「ソースからシンクに向かう5V電源」もあり、シンク側の「EDID ROM」を動かす電源は、ソース側から供給される、ことになっています。

ピンアサインは、こんな感じ。
キャプションを入力できます
DDCを構成するのは「15-18」ピン、そしてオマケに19ピンです。
もう思いっきりSCLとSDAがありますね。

ハードウェア

みんな大好き秋月電子のAE-ATMEGA328と、千石電商に売っているHDMI変換基板を使いました。
キャプションを入力できます

配線は以下な感じで。

端子名 HDMI AE-ATMEGA328
SCL 15 A5/SCL
SDA 16 A4/SDA
GND 17 GND
5V 18 +5.0V
HPD 19 A0/D14

キャプションを入力できます

ソース

いやあ、なかなかうまくいかなくて。
以下サイトを参考に、小改造で乗り越えました。
https://www.lab-z.com/arduinoedid/

edidreader.ino

#include <Wire.h> #include <avr/wdt.h> const int i2c_port = 0x50; byte buffer[128]; // 128 byte EEPROM data buffer void setup() { Serial.begin(9600); wdt_enable(WDTO_4S); Wire.begin(); pinMode(14, OUTPUT); Serial.println (BUFFER_LENGTH); while (!Serial) {;} } void loop() { digitalWrite(14, HIGH); delay(50); digitalWrite(14, HIGH); delay(50); ddcRead(); delay(50); wdt_reset(); } void printError(String message) { Serial.println("Error: " + message); } void ddcRead() { int totalblocks = 128; int blocks = totalblocks / BUFFER_LENGTH; Serial.println("Reading DDC..."); Wire.beginTransmission(i2c_port); Wire.write(0); Wire.endTransmission(); for (int block = 0; block < totalblocks; block += BUFFER_LENGTH) { Wire.requestFrom(i2c_port, BUFFER_LENGTH); for (int i = 0; i < BUFFER_LENGTH; i++) { // Serial.println(block + i, HEX); byte x = Wire.read(); buffer[block + i] = x; // Serial.print(x, HEX); Serial.print(" "); } } Serial.println("Finished reading DDC."); printData(); } void printData() { int rows = 128 / 16; for (int row = 0; row < rows; row++) { // Serial.print(" ("); // if (row == 0) // Serial.print(0, HEX); // Serial.print(row * 16, HEX); // Serial.print(") "); for (int half_col = 0; half_col < 2; half_col++) { for (int col = 0; col < 8; col++) { int index = (row * 16) + (half_col * 8) + col; byte b = buffer[index]; Serial.print("0x"); if (b < 16) { Serial.print(0, HEX); } Serial.print(b, HEX); Serial.print(" "); // Serial.print("["); Serial.print(index, HEX); Serial.print("]"); } if (half_col == 0) { // Serial.print("- "); } else { Serial.println(); } } } } void getInput() { int input_buffer_len = 16; char input[input_buffer_len]; Serial.println("Enter a string. 32 chars max. Input not echoed."); while (!Serial.available()) { ; } int input_len = Serial.readBytes(&input[0], input_buffer_len); Serial.print("Input is: "); for (int i = 0; i < input_len; i++) { Serial.write(input[i]); } Serial.println("."); Serial.println(""); }

実際の動作

ひったすらEDIDを読もうとします。
結果を、シリアルに流します。

読み込み結果は、こんな感じ!。

Reading DDC...
Finished reading DDC.
0x00 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0x00 0x1E 0x6D 0x01 0x00 0x01 0x01 0x01 0x01 
0x01 0x1C 0x01 0x03 0x80 0x7A 0x45 0x78 0xEA 0x7C 0x5B 0xA6 0x54 0x4E 0x9A 0x26 
0x0F 0x47 0x4A 0x21 0x08 0x00 0x31 0x40 0x45 0x40 0x61 0x40 0x71 0x40 0x81 0x80 
0xB3 0x00 0x01 0x01 0x01 0x01 0x08 0xE8 0x00 0x30 0xF2 0x70 0x5A 0x80 0xB0 0x58 
0x8A 0x00 0x40 0x84 0x63 0x00 0x00 0x1E 0x02 0x3A 0x80 0x18 0x71 0x38 0x2D 0x40 
0x58 0x2C 0x45 0x00 0x40 0x84 0x63 0x00 0x00 0x1E 0x00 0x00 0x00 0xFD 0x00 0x3A 
0x3E 0x1E 0x88 0x3C 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0xFC 
0x00 0x4C 0x47 0x20 0x53 0x49 0x47 0x4E 0x41 0x47 0x45 0x0A 0x20 0x20 0x01 0x92

EDIDデータは、オンラインでの解析サイトがあったります。
http://www.edidreader.com/

早速解析してみましょうか。
キャプションを入力できます

無事に読めてました!。

実際に読んでみると

電源OFF時の挙動が、ディスプレイ側実装によって違ったりしました。
電源OFF時、データが読めるものと、読めないもの。
更には中途半端に書き込まれた形跡があるもの、みたいなのもあったりして。
但し、このへんは規格上許容されているので、ディスプレイ側のソフト実装不良ではありません。

やらかしてみて

無事動いてなにより!。

以上です!。

eucalyのアイコン画像
いつも、てきとうです
ログインしてコメントを投稿する