続いて、LCD をちょっとだけ使ってみたという内容です。使った LCD はシリーズ冒頭から示している Seeed Bazar JP で売られているもの。
...ぶっちゃけ LCD の仕様から調べてたら日が暮れそうなので、本記事の段階では既存のプロジェクトを拾ってきてそれを改造する方針でやっていきたいと思います。
マイコンだったらソフトをサクッと書けるので LCD の仕様から調べるんですがね。Verilog はやっぱり書き慣れないので。
公式サンプルを入れてみる
取り敢えず Sipeed 公式のプロジェクトが GitHub のリポジトリ として上がっていたのでこちらを使いたいと思います。まずはクローンしましょう。
$ git clone https://github.com/sipeed/Tang-Nano-examples
そうしたら、IDE からプロジェクトを開きます。File -> Open から、Tang-Nano-examples/example_lcd/lcd_pjt/lcd_pjt.gprj
を開けばいいと思います。そうしたら論理合成、配置・配線をすれば動く・・・と思うじゃん?
公式の修正
実はバグで動きません。動くように修正されたものが最初から FPGA に書き込まれているのですが、リポジトリのものは Bug Fix(?) が反映されていないようです。
リポジトリの Issues でもこのような問題が報告されています。しかもこれ、直し方まで書いてありますねえ。ありがとうございます!
じゃあ直していきましょう。VGAMod.v
を開いてください。内部に PixelCount
なるレジスタがあって、ここに描画するピクセル数をカウントさせているようです。LineCount
も同様です。
この PixelCount
は PixelClk
が入力されるとインクリメントされなければいけないようですが(44 行目付近)、どうやらそのコードが挿入されていないようです。
なので、下のコードの // ADD
と書かれている部分を書き加えればいいと考えます。
always @( posedge PixelClk or negedge nRST )begin
PixelCount <= PixelCount + 1; // ADD
if( !nRST ) begin
LineCount <= 16'b0;
PixelCount <= 16'b0;
end
else if( PixelCount == PixelForHS ) begin
PixelCount <= 16'b0;
LineCount <= LineCount + 1'b1;
end
else if( LineCount == LineForVS ) begin
LineCount <= 16'b0;
PixelCount <= 16'b0;
end
end
そうして、LCD をつなぐと、無事動くようになりました。まあデモと全く変わらないのですが。
ちょっと改造してみる
流石に「デモと同じやつが動きました!」だけじゃ面白くないので、無い知恵を絞って少しは自分でいじってみたいと思います。LCD ディスプレイの知識はゼロですが。
コードを見ているとわかってくることがあります。箇条書きにしますね。
- データは RGB の生値で指定し、RとBは5ビット、Gだけ何故か6ビット
- おそらくパラレルな信号線が出ていて、LCD につながっている
- 左→右、上→下 の順に走査する
PixelCount
は横のピクセル数をカウントし、行が変わると0にリセット- 横の最大ピクセル数は
PixelForHS
というマクロで定義されている
- 横の最大ピクセル数は
LineCount
は縦のピクセル数をカウント- こちらも最大行数は
LineForVS
で定義されている
- こちらも最大行数は
- それぞれのカウントで対応する RGB 出力はコード下部にある
LCD_R
とかで出力されている
以上を考えて、表示パターンを改造してみました。そのために、VGAMod.v
で、60 行目付近、always
文の前に以下を追加してください。
wire[15:0] x, y;
assign x = PixelCount - H_BackPorch;
assign y = LineCount - V_BackPorch;
これで、x, y という位置パラメータが使えるようになりました。
改造1: 斜め表示
赤いバーを斜めに表示させるだけです。図のような表示を意図しています。
図より、$$y - \frac{1}{2}w \leq x \leq y + \frac{1}{2}w$$とすればよいですね。
LCD の走査方向から、x に関する条件式で表した方がわかりやすいと思います。
コードとしては、LCD_R
を書き換えてください。
parameter W_half = 50;
assign LCD_R = (y < x + W_half && x < y + W_half) ? 5'b11111 : 0; // naname
条件式の第一式を何故書き換えているかですが、Verilog は不用心に負の数を扱おうとするとドツボにはまるので(あくまで経験上のお話)、避けました。というより、最初は y - W_half < x
としていたのですがうまく表示されずにハマっていたんですよね。
この表示結果がこちら。
改造2: 四角を表示
まあただ条件式を増やしてみたということだけです。図で説明するまでもないのでコードです。中心座標は (400, 300)、縦横ともに 100 pxels です。
assign LCD_R = (300 - 50 < y && y < 300 + 50 && 400 - 50 < x && x < 400 + 50) ? 5'b11111 : 5'b00000; // square
改造3: 円を表示したかった
円の方程式
$$ (x-x_0)^2 + (y-y_0)^2 = r^2$$
を再現したくて、愚直に以下のように書いてみました。
assign LCD_R = ((x - 16'd400) ** 2 + (y - 16'd300) ** 2 < 16'd100 ** 2) ? 5'b11111 : 5'b00000;
論理合成はすんなりと完了。動くかなあ?と思ったら、リソース不足でできないということでした。乗算回路が容量を食っていたようで、約 20% のリソースが足りないようです。残念。
なおこの表示は悔しいので、でき次第掲載します。
→ できました。こちらをどうぞ。
DVDのスクリーンセーバーを入れてみた
ネットサーフィン(時代遅れ)をしていたときに発見しました。というかただの Twtter 検索ですが。
Tang Nano play DVD logo on LCD~ pic.twitter.com/RNX0332pwh
— Sipeed (@SipeedIO) January 5, 2020
誰かが絶対にやると思ったこれ。
では僕もやってみます。再現法はとても簡単で、このリポジトリをクローンするだけなんですよ。
$ git clone https://github.com/dotcypress/tang-nano-lcd
そして、後は GOWIN IDE に読ませて論理合成、配置配線を行うだけ。簡単です。
で、思ったこと
色々動かしたり調べてたりして、思ったことを書きます。
結構視野角が狭い
これは安い LCD なので許容範囲です。左右と上方向に傾けてみるとまあ下のようにそれなりの角度まで映ります。これはいいですよね。
一方、下方向に傾けてみると結構すぐに見えなくなります。写真で写っている以上に見づらいです。LCD の起き方を工夫すればいい問題ですけどね。
ひょっとしたら LCD ってこういうものなのですかね?誰か詳しい人教えてください。確かにノート PC の LCD ディスプレイを下から見るなんてことはしないですし...
LCD関連の電源問題があるらしいが...
現状安定して動作しています。が、LCD の電源に問題があるとこのような記事で考察されていました。ちょっと見ておいた方がいいと思います。
〆
今回は取り敢えず LCD を動かしてみると言うことをしました。サンプルコードがあったお陰で、楽に実装できました。サンプルも見てみるとそんなに複雑ではなく、気軽に FPGA で LCD を駆動できて面白いなあと思うばかりでしたね。
次回以降は、取り敢えず
- LCD の動作解明(ロジックレベルで)
- 各種 IP の利用
- Verilog の勉強
を検討したいですねえ。一体何週間かかることやら。それでは本記事は以上です。