PSoCでVGA出力!
PSoCやばいよ。
ものの数時間でカラーバー出せちゃったよ。
これで1000円ぽっちなんて…。
というわけで、何ができるかも良く分からないまま、単に安いからという理由で買っていた PSoC 5LP Prototyping Kit を使ってVGA出力に再挑戦しました。 なんとなく、回路図を書くようなビジュアルプログラミングでマイコンに食わせるバイナリを吐ける感じのフレームワークだと思ってましたが、とんでもない勘違いでしたね。 ペリフェラルを自由に設計してワンチップマイコンそのものを作るような仕組みだと、とりあえず素人目に理解しました。 しかもアナログ回路もいけるという。 完全に手に余る代物です。
PSoCを始めましょう(Lチカまで) - Qiita
こちらの記事で基本操作を一通り勉強させてもらって、あとは勘でアタックです。
先日の実験でビデオ信号のタイミングはばっちり把握できたので、改めて目標地点を定めます。
希望要件としては「モニタ全画面を使う」「正方形のピクセル」の2つ。 つまりモニタのフルスペック1024x768から半々にしていった解像度のどれかになります。 そこに「素人が作るZ80システムでどうにかできる規模感」という制限事項を勘案するとずばり、256x192px ということになるんじゃないでしょうか。
で、再びこちらのページでXGAのタイミングを確認させてもらうと……。
水平方向のタイミングが全部4で割り切れるので、ピクセルクロックの周波数(Pixel freq.)も1/4としてよさそうです。 上記資料では65MHzとあるところキリが悪いので64MHzと考えると1/4で16MHz。 この丸めの結果、垂直周波数が59Hzくらいになりますがモニタのスペックとしては問題なし。
16MHzのクロックを基準に、水平方向を数えるカウンタ、垂直方向を数えるカウンタを動かして同期信号と色信号を作ります。 色に関しては可視範囲内でのみアクティブになるOE信号のようなものが出せれば今回はOK。
以上を念頭に入れてPSoC Creatorのコンポーネントを色々見ていった結果、
こんな回路になりました。
デジタル回路のセオリーのようなものは全く知らないので、アホなことやってる可能性が高いですが…。
数を数えられるコンポーネントはConter、PWMなどもありましたが、1個のカウンターで複数回の値比較をしようと思ったらBasicCounter+DigitalComparatorに落ち着きました。
回路部分にはカウンターだけ置いて、カウント値から信号を生成するのはソフトウェアで書くという方法もあるみたいです。 でも、タイミング重要なのでデジタル回路だけで実装した方がよいんじゃないかなと。
pixel = line = 0 while true do hsync = pixel < 34 h_visible = 74 <= pixel && pixel < 330 vsync = line < 6 v_visible = 35 <= line && line < 803 visible = h_visible && v_visible; pixel += 1 pixel %= 336 line += 1 if pixel == 0 line %= 806 end
処理内容をプログラム脳で考えると上記のような感じです。 垂直方向は4pxずつというわけにはいかないのでXGAの806ラインを全部数える必要があります。 また、同期信号はアクティブLOWなので、回路では不等号が逆になっています。
こちらはクロック設定。
16MHzを直接作れなかったため、48MHzから3分周しています。
はじめ左上のXTALに16MHzを設定してましたが、external、つまり自分で繋いだ外部水晶のパラメータを入力するものだと気付かずにドハマリしました。
ピンアサイン。ここも罠があって、P0あたりにアサインしたらアナログ回路と繋がってる?ピンだったらしくVSYNCが鈍ってしまいました。
デフォルトで役割があるらしきピンを避けると自由にできるのは10個もないですね……というのはおかしいので、
いらない機能をdisableできるとか、デジタル/アナログごとにアサインしていいピンがあるとか、ちょっと勉強が必要かと思います。
書いたコードは初期化のみ。得も言われぬ気持ちよさがあります。
ここではVISIBLE信号を0.7Vくらいに降圧して赤に繋いでいます。
でもぉ、見たくないですか? カラーバー……。
こうして……
可視範囲でピクセルをカウントして、カウント値の適当なビットをRGBとして出力すれば8色のバーが出せますね。 使用するビットとRGBへのアサイン先を変えれば、色が変化する周期や順番を変えられます。
楽しすぎるぅ……。
ちなみに、カウンタのバスからビット毎のワイヤを引き出す方法はこちらです。
というわけで、ビデオ出力の目途は立った感じです。 VRAMの設計などまだ課題はありますけどね。256*192とはいってもフルカラー・フルグラフィックなんてのは難しいでしょうし。
それから、今回出力した映像はちょっとノイズが酷いです。
ピクセル境界がギザギザのゆれゆれで、これも解消の要有り。
単にクロック精度の問題であってくれると非常に助かります。今回は水晶使ってないので。
追記
16MHzの外部水晶を使ったらピシっとした映像になりました!
追追記
回路ちょっと修正。 LineCounterのインクリメントが想定より約1クロック早くなってしまってました。 BasicCounterはクロックの立ち上がりでカウント動作するので、前掲の回路だと水平カウンタがラインの右端に達した瞬間、垂直カウンタが1増えます(今回の回路では問題にならないですが)。 ならばLineCounterのclockの前にnot入れればいいのかというと、そもそも前段カウンタのキャリーを後段のクロックに入れる、いわゆるリップルカウンタが良くないみたいですね。 後段カウンタの変化が遅延してしまいます。
前段カウンタのキャリーを次段のclockではなくenableに入れてclockを共通とすれば、前段リセットと後段インクリメントが同時になってベター。
上記の気付きは ディジタル回路テイクオフ指南 という本によるものです。 教科書的でなく、この世界の常識・非常識を切り分けるセンスが語り口から滲み出てくる感じで大変勉強になります。 まぁ、リップルカウンタが非同期式なんていうのは学生レベルの常識かもしれませんが……。