十四、TFT屏顯示圖片
本文由杭電網友曾凱峰貢獻,特此感謝
學習了小梅哥的TFT顯示屏驅動設計后,想着在此基礎上通過TFT屏顯示一張圖片,有了這個想法就開始動工了。首先想到是利用FPGA內部ROM存儲圖片數據,然后通過控制讀取數據地址將圖片數據傳給TFT驅動模塊,從而將每個圖片數據顯示在對應的像素點上。整個設計的框圖如下:
主要是在小梅哥TFT驅動設計基礎上增加了圖片數據發送控制模塊Imgdata_send,該模塊包括存儲圖片數據的rom,和一些簡單的邏輯控制。具體的rom IP核的建立我這里就不說了,不懂的可以參看小梅哥的相關內容的視頻,我這里主要講如何將圖片轉換成mif文件。這里有兩種方法,可以作為參考,主要用到如下軟件:
步驟1:利用Img2Lcd將圖片轉化為BMP格式的(當然圖片本身為BMP格式就不需要轉了,直接進入步驟2);
步驟2:利用BMP2Mif可將圖片轉化為mif文件。
具體實現如下:
步驟1:先打開Img2Lcd打開一張圖片,選擇輸出格式為BMP格式,輸出灰度選擇24位真彩色(由於BMP2Mif軟件只能載入24位或8位的,所以這里就沒有直接選16位的真彩色),最大寬度和高度根據圖片實際的大小進行選擇的,由於內部rom能存儲的數據量有限,我選擇了一張像素為160*120的圖片如果想顯示大的圖片,如480*272圖片,用這種方法就不能實現(看到這里有人想,如果想顯示大點的圖片那應該怎么解決,后面我會有其他方法來實現TFT屏保顯示)。
步驟2:打開BMP2Mif軟件,加載剛轉換輸出的24位BMP格式圖片,選擇輸出圖像格式和文件類型,點擊一鍵轉換便將圖片轉換為了mif文件了,可以將mif文件名更改下以區別不同圖片mif文件。
可以用Notepad++將轉化的mif文件打開看看,截取部分圖如下:
rom IP核設置完成后就是數據發送控制模塊Imgdata_send中控制邏輯的編寫,主要是讓圖片顯示在屏幕指定的地方,這就需要根據TFT_CTRL模塊的TFT行和場掃描計數器輸出信號來控制rom的數據地址,從而控制TFT_CTRL的待顯示數據data_in。具體代碼如下:
1 module Imgdata_send( 2 clk50M, 3 rst_n, 4 tft_de, 5 hcount, 6 vcount, 7 data_in 8 ); 9 10 input clk50M; //系統時鍾,默認50M 11 input rst_n; //復位信號,低電平有效 12 input tft_de; //TFT數據使能 13 input [9:0]hcount; //TFT行掃描計數器 14 input [9:0]vcount; //TFT場掃描計數器 15 output [15:0]data_in; //待顯示圖片數據 16 17 wire img_ack; //圖片數據使能 18 19 localparam IMG_H = 160, //圖片行像素點個數 20 IMG_V = 120; //圖片場像素點個數 21 22 localparam TFT_H = 480, //TFT屏行像素點個數 23 TFT_V = 272; //TFT屏場像素點個數 24 25 localparam IMG_HM = TFT_H - IMG_H, //圖片行方向可移動像素點個數 26 IMG_VM = TFT_V - IMG_V; //圖片場方向可移動像素點個數 27 28 reg [9:0]img_hbegin = 0; //圖片左上角第一個像素點在TFT屏的行向坐標 29 reg [9:0]img_vbegin = 0; //圖片左上角第一個像素點在TFT屏的場向坐標 30 31 reg [14:0]addr; //讀圖片數據rom地址 32 wire [15:0]img_data; //讀出圖片數據 33 34 rom u4_rom( 35 .address(addr), 36 .clock(clk50M), 37 .q(img_data) 38 ); 39 40 assign img_ack = tft_de && (hcount >= img_hbegin && hcount < img_hbegin + IMG_H) && 41 (vcount >= img_vbegin && vcount < img_vbegin + IMG_V)?1'b1:1'b0; 42 43 always@(posedge clk50M or negedge rst_n) 44 begin 45 if(!rst_n) 46 addr <= 15'd0; 47 else if(img_ack) 48 addr <= (hcount - img_hbegin) + (vcount - img_vbegin)*IMG_H; 49 else 50 addr <= 15'd0; 51 end 52 53 assign data_in = img_ack ? img_data : 16'h0; 54 55 endmodule
接下來就是仿真驗證,利用已有的TFT_CTRL模塊的hcount、vcount、tft_de作為Imgdata_send模塊的輸出進行仿真驗證,代碼如下:
1 `timescale 1ns/1ns 2 `define PERIOD_CLK 20 3 4 module Imgdata_send_tb; 5 6 reg clk50M; 7 reg rst_n; 8 wire tft_de; 9 wire [9:0]hcount; 10 wire [9:0]vcount; 11 wire [15:0]data_in; 12 13 wire clk9M; 14 15 Imgdata_send u0_Imgdata_send( 16 .clk50M(clk50M), 17 .rst_n(rst_n), 18 .tft_de(tft_de), 19 .hcount(hcount), 20 .vcount(vcount), 21 .data_in(data_in) 22 ); 23 24 pll u1_pll( 25 .areset(!rst_n), 26 .inclk0(clk50M), 27 .c0(clk9M) 28 ); 29 30 TFT_CTRL u2_TFT_CTRL( 31 .clk9M(clk9M), 32 .rst_n(rst_n), 33 .data_in(), 34 .hcount(hcount), 35 .vcount(vcount), 36 .tft_rgb(), 37 .tft_hs(), 38 .tft_vs(), 39 .tft_clk(), 40 .tft_de(tft_de), 41 .tft_pwm() 42 ); 43 44 initial clk50M = 1'b1; 45 always #(`PERIOD_CLK/2) clk50M = ~clk50M; 46 47 initial 48 begin 49 rst_n = 1'b0; 50 #(`PERIOD_CLK*200+1) 51 rst_n = 1'b1; 52 end 53 54 endmodule
仿真驗證的波形圖如下:
從仿真結果可以看出,圖片數據是在我們指定的區域輸出的。該模塊仿真驗證正確后,進行頂層電路文件的設計。頂層文件編寫如下:
1 module rom_tft_img( 2 3 clk50M, 4 rst_n, 5 6 tft_rgb, 7 tft_hs, 8 tft_vs, 9 tft_clk, 10 tft_de, 11 tft_pwm 12 ); 13 14 input clk50M; 15 input rst_n; 16 17 output [15:0]tft_rgb; 18 output tft_hs; 19 output tft_vs; 20 output tft_clk; 21 output tft_de; 22 output tft_pwm; 23 24 wire [15:0]data_in; 25 wire [9:0]hcount; 26 wire [9:0]vcount; 27 wire clk9M; 28 29 Imgdata_send u0_Imgdata_send( 30 .clk50M(clk50M), 31 .rst_n(rst_n), 32 .tft_de(tft_de), 33 .hcount(hcount), 34 .vcount(vcount), 35 .data_in(data_in) 36 ); 37 38 pll u1_pll( 39 .areset(!rst_n), 40 .inclk0(clk50M), 41 .c0(clk9M) 42 ); 43 44 TFT_CTRL u2_TFT_CTRL( 45 .clk9M(clk9M), 46 .rst_n(rst_n), 47 .data_in(data_in), 48 .hcount(hcount), 49 .vcount(vcount), 50 .tft_rgb(tft_rgb), 51 .tft_hs(tft_hs), 52 .tft_vs(tft_vs), 53 .tft_clk(tft_clk), 54 .tft_de(tft_de), 55 .tft_pwm(tft_pwm) 56 ); 57 58 endmodule
生成的頂層電路圖如下:
接下來就是仿真驗證,仿真驗證程序如下:
1 `timescale 1ns/1ps 2 `define PERIOD_CLK 20 3 4 module rom_tft_img_tb; 5 //模塊輸入端口 6 reg clk50M; 7 reg rst_n; 8 9 //模塊輸出端口 10 wire [15:0]tft_rgb; 11 wire tft_hs; 12 wire tft_vs; 13 wire tft_clk; 14 wire tft_de; 15 wire tft_pwm; 16 17 reg [7:0]v_cnt = 0; //掃描幀數統計計數器 18 19 rom_tft_img rom_tft_img( 20 21 .clk50M(clk50M), 22 .rst_n(rst_n), 23 24 .tft_rgb(tft_rgb), 25 .tft_hs(tft_hs), 26 .tft_vs(tft_vs), 27 .tft_clk(tft_clk), 28 .tft_de(tft_de), 29 .tft_pwm(tft_pwm) 30 ); 31 32 initial clk50M = 1'b1; 33 always #(`PERIOD_CLK/2) clk50M = ~clk50M; 34 35 initial 36 begin 37 rst_n = 1'b0; 38 #(`PERIOD_CLK*200 + 1); 39 rst_n = 1'b1; 40 end 41 42 initial 43 begin 44 wait(v_cnt == 3); //等待掃描2幀后結束仿真 45 $stop; 46 end 47 48 always@(posedge tft_vs) //統計總掃描幀數 49 v_cnt = v_cnt + 1'b1; 50 51 endmodule
仿真波形如下:
從波形可以看出,圖片數據在指定區域顯示。接下來就是板級驗證,引腳分配參照芯航線FPGA學習套件引腳分配表進行分配,然后布局布線,板級程序下載最后實現的效果圖如下:
我們設置的是顯示在屏幕的左上角,與預期效果是一致的,想要改變圖片的位置,可以更改Imgdata_send模塊的28、29行代碼:
28 reg [9:0]img_hbegin = 0; //圖片左上角第一個像素點在TFT屏的行向坐標 29 reg [9:0]img_vbegin = 0; //圖片左上角第一個像素點在TFT屏的場向坐標
將上面的(0、0)更改為其他的數,圖片位置就會改變,如果想讓圖片在屏幕上自動的移動,可以自己設置一種路徑讓img_hbegin、img_vbegin的值按你的路徑變化就可實現圖片的自動移動,讀者可以自己改進學習。
如有更多問題,歡迎加入芯航線 FPGA 技術支持群交流學習:472607506
小梅哥
芯航線電子工作室
關於學習資料,小梅哥系列所有能夠開放的資料和更新(包括視頻教程,程序代碼,教程文檔,工具軟件,開發板資料)都會發布在我的雲分享。(記得訂閱)鏈接:http://yun.baidu.com/share/home?uk=402885837&view=share#category/type=0
贈送芯航線AC6102型開發板配套資料預覽版下載鏈接:鏈接:http://pan.baidu.com/s/1slW2Ojj 密碼:9fn3
贈送SOPC公開課鏈接和FPGA進階視頻教程。鏈接:http://pan.baidu.com/s/1bEzaFW 密碼:rsyh