ArduinoでVGA信号を読む

VGA端子 - Wikipedia」を参考に、5, 6, 7, 8, 10ピンをGNDに、1, 2, 3ピンをそれぞれアナログピンに、13, 14ピンをデジタルピンに接続した。出力値が変動しているのを観察することができた。

ハードウェアをやる時も「早すぎる最適化は諸悪の根元」を唱えながらやるべきだな。ついついサイズを最適化したせいで半田付け大変だった。

横に伸びている3本はDisplay Data Channelとかいう、モニタのサイズや周波数を伝えるための信号線で、「繋がなくても大丈夫なはず、でも一番下の列だから『やっぱ繋がなきゃ』ってなったら後からハンダ付けは至難」と思って一応引っ張りだしておいた。結論から言うとこの3本はオープンなままで大丈夫だった。RGBの3本をアナログピンに、HSYNCとVSYNCをデジタルピンにつなぐ。

で、こんな感じのプログラムを走らせる。

void loop()
{
  valR = analogRead(pinR);
  valG = analogRead(pinG);
  valB = analogRead(pinB);
  Serial.print(valR, HEX);
  Serial.print(',');
  Serial.print(valG, HEX);
  Serial.print(',');
  Serial.println(valB, HEX);
}

「白と黄色と淡い緑が多いかな」ぐらいはわかるようだ。

ところでATMEGA168のクロックは16MHz、VGA信号は25MHz、1ピクセル単位で読むのが無理なのはわかっていたが、横方向のシグナルは8の倍数だから間引いて読むことができるんじゃないかなーと思っていた。しかし、カラーバーを出力する場合と違って、ただ読んだだけでは何も面白くない。読んだ結果を出力しないと。で、読んだものを送信するのにSerialライブラリを使うと当然そこがクロック数を食う。

水平同期信号を1バイトで送信する実装をしてみた。

void loop()
{
  val = digitalRead(pinH);    // read the input pin
  if(val == HIGH){
    Serial.write(49); // ASCII for '1'
  }else{
    Serial.write(48); // ASCII for '0'
  }
}

結果に78文字ごとに改行を入れて1を.に置き換えて見やすくしたもの:

..........................................................................0...
0...0...0...0...0...0...0...0...0........................0.....0.........0....
..0.........0......0.........0......0.........0......0.........0..............
..0................0.........0......0.........0......0.........0..............
..0......0.........0................0.........0......0.........0......0.......
..0......0.........0................0......0.........0.........0......0.......
.........0.........0......0.........0......0.........0......0.........0......0
.........0................0................0.........0......0.........0......0
.........0................0................0................0................0
.........0......0.........0......0.........0................0................0
................0.........0......0.........0......0.........0......0.........0
................0................0................0.........0......0.........0
......0.........0......0.........0......0.........0................0.........0
......0.........0......0.........0......0.........0......0.........0..........
......0................0................0................0.........0......0...
......0......0.........0......0.........0................0................0...
......0......0.........0......0.........0......0.........0................0...
...0.........0.........0......0.........0......0.........0......0.........0...
.............0......0.........0......0.........0................0.........0...
...0.........0......0.........0......0.........0................0.............
...0.........0......0.........0......0.........0......0.........0......0......
...0................0......0.0.......0................0.........0......0......
...0......0.........0................0................0......0..0......0......
...0......0.........0......0.........0......0.........0................0......
0.........0......0.........0................0.........0......0.........0......
0.........0......0.........0................0................0................
0.........0......0.........0......0.........0................0................
0.........0......0.........0......0.........0......0.........0......0.........
0......0.........0......0.........0.........0......0.........0......0.........
0......0.........0......0.........0......0.........0......0.........0.........
0......0................0.........0......0.........0......0.........0......0..
.......0......0.........0.........0......0.........0......0.........0......0..

うーむ、全然サンプリング周波数が足りてない感。

void loop(){
  if(micros() < 1000000){
    Serial.write(48); // ASCII for '0'
  }
}

これで1089個の0が送られてくる。1文字あたり918usec。VGA信号の1行は 800クロック / 25.175MHzで 31.8usec。シリアルで送るのは無理だなぁ。
画面のある領域が特定の色になったらデジタルピンに出力、とかだとできるかもしれないけど深追いはやめておこう。