秋月のTFT液晶モジュールで画像を簡単に表示出来たので記事を書いてみました。
今回はとりあえず画像を表示してみたかったので、出来るだけ簡単に妥協して作成しました。まず妥協点1は、TFT液晶のRGBデータがそれぞれ8bit分の配線が大変なのでTFT液晶のRGBのそれぞれ上位4bitと接続しました(表示出来ればあとからbit拡張すれば8bit出力も出来る)。妥協点2は、TFT液晶は画素数480x272であるがメモリブロック1つでは収まらないので240x136を拡大して出力する(dramかメモリブロックを4つにすれば改善可能だと思います)。
-
使用した環境
-
Vivado 2015.4
-
Visual Studio 2013
-
使用したもの
ZYBOのClock Sourcesである125MHzからIP CatalogのClocking Wizardを用いて9MHzを生成します(dclkでありこれよりhsync,vsync生成)。IP CatalogのBlock Memory Generatorを用いて12bitの240x136wordsのブロックRAMを生成し、Memory Initializationで予め画像データを入力し、それを読み出しTFT液晶に表示しました。
TFT液晶モジュールのタイミングチャートよりdclkを用いてhsyncとvsyncを生成し、タイミングを合わせて画素データを読み出すように記述します。以下に動かした時のコードを載せます(動作は確認しましたがタイミングの正確性は保証しません)。
module display_test(
input reset,
input clk,
output [3:0] r,
output [3:0] g,
output [3:0] b,
output reg hsync,
output reg vsync,
output wire dclk
);
//125MHz to 9MHz
clk_wiz_0 clk_wiz_0_inst(
.clk_in1(clk),
.clk_out1(dclk),
.reset(1'b0)
);
//block RAM 240 x 136
blk_mem_gen_0 blk_mem_gen_0_inst(
.clka(dclk), // input wire clka
.wea(1'b0), // input wire [0 : 0] wea
.addra(16'h0), // input wire [15 : 0] addra
.dina(12'h0), // input wire [11 : 0] dina
.clkb(dclk), // input wire clkb
.enb(1'b1), // input wire enb
.addrb(addrb), // input wire [15 : 0] addrb
.doutb({r,g,b}) // output wire [11 : 0] doutb
);
//display parameter
parameter H_PERIOD = 531;
parameter H_BPORCH = 43;
parameter V_PERIOD = 288;
parameter V_BPORCH = 12;
reg [9:0] hcnt;
reg [9:0] pcnt;
wire [15:0] addrb;
wire [9:0] p_addr;
wire [9:0] h_addr;
always @(posedge dclk) begin
if(pcnt == (H_PERIOD-1)) begin
pcnt <= 0;
end else begin
pcnt <= pcnt + 1;
end
end
always @(posedge dclk) begin
if(pcnt < (H_BPORCH-1))begin
hsync <= 0;
end else begin
hsync <= 1;
end
end
always @(posedge hsync) begin
if((V_PERIOD-1) == hcnt) begin
hcnt <= 0;
end else begin
hcnt <= hcnt + 1;
end
end
always @(posedge dclk) begin
if((V_BPORCH-1) < hcnt) begin
vsync <= 1;
end else begin
vsync <= 0;
end
end
assign p_addr = (hsync == 1 && vsync == 1) ? pcnt - 43 : 10'h0;
assign h_addr = (hsync == 1 && vsync == 1) ? hcnt - 12 : 10'h0;
assign addrb = (hsync == 1 && vsync == 1) ? p_addr[9:1] + h_addr[9:1] * 240 : 12'h0;
endmodule
ハード関連はこれで終了です。書いたコードもこれくらいなので簡単だと思います。
次にMemory Initializationのデータ生成について書きます。入力画像は240x136を用います。Visual Studio2013でOpenCV(2.4.10)を用いてjpgやpng画像からrawデータ(RGBを4bit化)を取り出しかつMemory Initializationのフォーマットに合わせてテキストを出力するコードを記述します。以下にコードを載せます(私の環境では上手く行きましたが動作保証出来ません)。
#include <opencv2/nonfree/nonfree.hpp>
#include <opencv/highgui.h>
void main()
{
FILE *outputfile;
outputfile = fopen("d.txt", "w");
if (outputfile == NULL) {
printf("cannot open\n");
exit(1);
}
// 画像の読み込み
cv::Mat srcImg = cv::imread("yamada.jpg");
for (int y = 0; y < srcImg.rows; y++) {
cv::Vec3b* ptr = srcImg.ptr(y);
for (int x = 0; x < srcImg.cols; x++) {
cv::Vec3b bgr = ptr[x];
fprintf(outputfile, "%x%x%x,\n", bgr[2] >> 4, bgr[1] >> 4, bgr[0] >> 4);
}
}
fclose(outputfile);
}
プロジェクトの作り方は、説明していませんが上2つのコードでこんな感じで画像が出ました。
っねすごく簡単でしょ!?
画像の通り今はジャンパ線で接続しているので、今度は基板にして遊びやすくしたいです。
参考URL
Memory Initializationのcoeフォーマット
Opencv 画素抽出
C言語ファイル出力