系統框圖:
需要用到的模塊有:
1,UART_RX(串口接收模塊);
2,串口接受的數據存放到RAM模塊;
3,RAM IP核;
4,時鐘IP核 (TFT顯示屏驅動時鐘的產生);
5,TFT顯示驅動模塊;
1,UART_RX(串口接收模塊)
具體構建方式及詳見(其中的串口接收部分)
FPGA-UART串口https://blog.csdn.net/weixin_46897065/article/details/135586405?spm=1001.2014.3001.5502
2,串口接受的數據存放到RAM模塊
串口接受的數據存放到RAM的邏輯時序圖如下:
然后編輯控制器邏輯代碼:
module img_rx_wr(Clk ,Reset_n ,rx_data ,rx_done ,ram_wren ,ram_eraddr ,ram_wrdata ,led );input Clk ;input Reset_n ;input [7:0] rx_data ;input rx_done ;output reg ram_wren ;output reg [15:0] ram_eraddr ;output [15:0] ram_wrdata ;output reg led ;reg[16:0]data_cnt; //統計串口接收數據個數計數器always@(posedge Clk or negedge Reset_n)if(!Reset_n)data_cnt <= 0;else if(rx_done)data_cnt <= data_cnt + 1'd1;reg [15:0]rx_data_temp; always@(posedge Clk or negedge Reset_n)if(!Reset_n)rx_data_temp <= 1'b0;else if(rx_done)rx_data_temp <= {rx_data_temp[7:0],rx_data};always@(posedge Clk or negedge Reset_n)if(!Reset_n)ram_wren <= 0;else if(rx_done && data_cnt[0])ram_wren <= 1'd1;elseram_wren <= 0;always@(posedge Clk or negedge Reset_n)if(!Reset_n) ram_eraddr <= 0;else if (rx_done && data_cnt[0])ram_eraddr <= data_cnt[16:1]; // data_cnt/2assign ram_wrdata = rx_data_temp;always@(posedge Clk or negedge Reset_n)if(!Reset_n)led <= 0;else if((rx_done)&&(data_cnt == 131071))led <= ~led ;
endmodule
測試此邏輯代碼的正確性:
編寫測試文件:
`timescale 1ns / 1ps
module img_rx_wr_tb;reg Clk ;reg Reset_n ;reg [7:0] rx_data ;reg rx_done ;wire ram_wren ;wire [15:0] ram_eraddr ;wire [15:0] ram_wrdata ;wire led ;img_rx_wr img_rx_wr(.Clk (Clk ) ,.Reset_n (Reset_n ) ,.rx_data (rx_data ) ,.rx_done (rx_done ) ,.ram_wren (ram_wren ) ,.ram_eraddr(ram_eraddr) ,.ram_wrdata(ram_wrdata) ,.led (led ) );initial Clk =1;always #10 Clk = ~Clk;initial beginReset_n = 0;rx_data = 0;rx_done = 0;#201;Reset_n = 1;#2000;rx_data = 8'd255;repeat(131072)beginrx_done = 1;#20;rx_done = 0;#200;rx_data = rx_data - 1;end#2000000;repeat(131072)beginrx_done = 1;#20;rx_done = 0;#200;rx_data = rx_data - 1;end$stop;endendmodule
仿真波形如下:
寫入第一個數據時:
寫入最后一個數據時:
RAM寫邏輯已經完成,接下來完成RAM的讀邏輯。
3,構建RAM IP核
具體構建方式及其內部參數詳見FPGA-學會使用vivado中的存儲器資源RAM(IP核)https://blog.csdn.net/weixin_46897065/article/details/136325283?spm=1001.2014.3001.5502
4,TFT顯示屏驅動時鐘產生
具體構建方式詳見:
FPGA-時鐘管理單元https://blog.csdn.net/weixin_46897065/article/details/136356331?spm=1001.2014.3001.5502
5,TFT顯示驅動模塊
具體原理詳見
FPGA- RGB_TFT顯示屏原理及驅動邏輯https://blog.csdn.net/weixin_46897065/article/details/136401589?spm=1001.2014.3001.5502? ? ? 以及FPGA-VGA成像原理與時序
https://blog.csdn.net/weixin_46897065/article/details/136386813?spm=1001.2014.3001.5502
在以上鏈接中介紹的TFT顯示邏輯其中使用的組合邏輯,為了使得整體得到更好的時序性(RAM得出地址后數據輸出是有延遲時,后面使用時為了確保數據一個都不丟,進行時序對齊)將鏈接中的邏輯代碼重新設計,如下:
`include "disp_parameter_cfg.v"
//800x480
//H_Right_Borde = 0 V_Bottom_Bord = 8
//H_Front_Porch = 40 V_Front_Porch = 2
//H_Sync_Time = 128 V_Sync_Time = 2
//H_Back_Porch = 88 V_Back_Porch = 25
//H_Left_Border = 0 V_Top_Border = 8
//H_Data_Time = 800 V_Data_Time = 480
//H_Total_Time = 1056 V_Total_Time = 525module TFT_Ctrl(Clk_33M ,Reset_n ,Data_in ,Data_req ,hcount , //行掃描位置(顯示圖像行掃描地址)vcount , //場掃描位置(顯示圖像場掃描地址)TFT_HS , //行同步信號TFT_VS , //場同步信號TFT_DE , //有效數據輸出 TFT_CLK , TFT_DATA , //紅綠藍三色 分別8位量化 R[7:0]G[7:0]B[7:0] TFT_BL);input Clk_33M;input Reset_n;input [15:0] Data_in;output reg Data_req;output reg [11:0] hcount;output reg [11:0] vcount;output TFT_HS;output TFT_VS;output TFT_DE;output TFT_CLK;output reg [15:0] TFT_DATA; //紅綠藍三色 分別8位量化 R[7:0]G[7:0]B[7:0] output TFT_BL;
// parameter VGA_HS_end = 11'd127 ,
// hdat_begin = 11'd216 ,
// hdat_end = 11'd1016 ,
// hpixel_end = 11'd1055 ,
// VGA_VS_end = 11'd1 ,
// vdat_begin = 11'd35 ,
// vdat_end = 11'd515 ,
// vline_end = 11'd524 ;parameter TFT_HS_end = `H_Sync_Time-1 ;parameter hdat_begin = `H_Sync_Time + `H_Back_Porch +`H_Left_Border - 1'b1;parameter hdat_end = `H_Total_Time - `H_Right_Border -`H_Front_Porch - 1'b1;parameter vdat_begin = `V_Sync_Time + `V_Back_Porch +`V_Top_Border - 1'b1;parameter vdat_end = `V_Total_Time - `V_Bottom_Border -`V_Front_Porch - 1'b1; parameter hpixel_end = `H_Total_Time -1 ;parameter TFT_VS_end = `V_Sync_Time-1 ; parameter vline_end = `V_Total_Time -1 ; reg [11:0] hcount_r;reg [11:0] vcount_r;always@(posedge Clk_33M or negedge Reset_n)if(!Reset_n)hcount_r <= 11'd0; else if(hcount_r == hpixel_end )hcount_r <= 11'd0;elsehcount_r <= hcount_r + 1'd1;always@(posedge Clk_33M or negedge Reset_n)if(!Reset_n)vcount_r <= 11'd0; else if(hcount_r == hpixel_end) if(vcount_r == vline_end )vcount_r <= 11'd0;elsevcount_r <= vcount_r + 1'd1;elsevcount_r <= vcount_r;always@(posedge Clk_33M)Data_req <= ((hcount_r >= hdat_begin) && (hcount_r < hdat_end)&&(vcount_r >= vdat_begin) && (vcount_r < vdat_end)) ? 1'b1 : 1'b0; reg [3:0]TFT_DE_r; always@(posedge Clk_33M) beginTFT_DE_r[0] <= Data_req;TFT_DE_r[3:1] <= TFT_DE_r[2:0];endassign TFT_DE = TFT_DE_r[2];
// assign TFT_DE = ((hcount_r >= hdat_begin) && (hcount_r < hdat_end)&&
// (vcount_r >= vdat_begin) && (vcount_r < vdat_end)) ? 1'b1 : 1'b0; always@(posedge Clk_33M) beginhcount <= Data_req ? (hcount_r - hdat_begin) : 10'd0;vcount <= Data_req ? (vcount_r - vdat_begin) : vcount;end
// assign hcount = TFT_DE ? (hcount_r - hdat_begin) : 10'd0;
// assign vcount = TFT_DE ? (vcount_r - vdat_begin) : 10'd0; reg [3:0]TFT_HS_r; always@(posedge Clk_33M) beginTFT_HS_r[0] <= (hcount_r > TFT_HS_end)? 1'b1 :1'b0; TFT_HS_r[3:1] <= TFT_HS_r[2:0];end assign TFT_HS = TFT_HS_r[2];
// assign TFT_HS = (hcount_r > TFT_HS_end)? 1'b1 :1'b0;reg [3:0]TFT_VS_r; always@(posedge Clk_33M) beginTFT_VS_r[0] <= (vcount_r > TFT_VS_end)? 1'b1 :1'b0; TFT_VS_r[3:1] <= TFT_VS_r[2:0];end assign TFT_VS = TFT_VS_r[2];
// assign TFT_VS = (vcount_r > TFT_VS_end)? 1'b1 :1'b0; always@(posedge Clk_33M) beginTFT_DATA <= (TFT_DE) ? Data_in : 16'h0000; end
// assign TFT_DATA = (TFT_DE) ? Data_in : 24'h000000;assign TFT_CLK = ~Clk_33M;assign TFT_BL = 1;endmodule
6,頂層模塊
將以上5個小模塊設計好后,根據以下系統框圖設計頂層模塊。
代碼如下:
`timescale 1ns / 1ps
module UART_RAM_TFT(Clk, Reset_n,uart_rx,TFT_RGB, //TFT數據輸出TFT_HS, // TFT行同步信號TFT_VS, //TFT場同步信號TFT_DE, //TFT數據有效信號TFT_CLK,TFT_BL, //TFT背光led);input Clk; input Reset_n;input uart_rx;output [15:0]TFT_RGB;output TFT_HS;output TFT_VS;output TFT_DE;output TFT_CLK;output TFT_BL;output led;// assign TFT_BL = 1;reg [15:0] ram_rdaddr ;wire [15:0] ram_rdata ;wire [7:0] rx_data ;wire rx_done ;wire ram_wren ;wire [15:0] ram_eraddr ;wire [15:0] ram_wrdata ;wire led ; wire clk_TFT ;
// wire locked ;MMCM MMCM(// Clock out ports.clk_out1(clk_TFT), // output clk_out1// Status and control signals.reset(!Reset_n), // input reset// Clock in ports.clk_in1(Clk)); img_rx_wr img_rx_wr(.Clk (Clk ) ,.Reset_n (Reset_n ) ,.rx_data (rx_data ) ,.rx_done (rx_done ) ,.ram_wren (ram_wren ) ,.ram_eraddr(ram_eraddr) ,.ram_wrdata(ram_wrdata) ,.led (led ) );uart_byte_rx uart_byte_rx(.Clk (Clk ) ,.Reset_n (Reset_n) ,.uart_rx (uart_rx) ,.Baud_Set(2 ) ,.Data (rx_data) ,.Rx_done (rx_done)); RAM RAM (.clka(Clk), // input wire clka.ena(1), // input wire ena.wea(ram_wren), // input wire [0 : 0] wea.addra(ram_eraddr), // input wire [15 : 0] addra.dina(ram_wrdata), // input wire [15 : 0] dina.clkb(clk_TFT), // input wire clkb.enb(1), // input wire enb.addrb(ram_rdaddr), // input wire [15 : 0] addrb.doutb(ram_rdata ) // output wire [15 : 0] doutb); wire ram_data_en;wire Data_req;//RAM中存儲的數據時256*256的像素矩陣always@(posedge clk_TFT or negedge Reset_n)if(!Reset_n)ram_rdaddr <= 0;else if(ram_data_en)ram_rdaddr <= ram_rdaddr + 1'd1;wire [11:0]h_count,v_count;wire [15:0]dis_data;assign ram_data_en = Data_req && (h_count >= 272 && h_count < 528) && (v_count >= 112 && v_count < 368);assign dis_data = ram_data_en ? ram_rdata: 0;TFT_Ctrl TFT_Ctrl(.Clk_33M (clk_TFT) ,.Reset_n (Reset_n) ,.Data_in (dis_data) ,.Data_req(Data_req) ,.hcount (h_count) , //行掃描位置(顯示圖像行掃描地址).vcount (v_count) , //場掃描位置(顯示圖像場掃描地址).TFT_HS (TFT_HS ) , //行同步信號.TFT_VS (TFT_VS ) , //場同步信號.TFT_DE (TFT_DE ) , //有效數據輸出 .TFT_CLK (TFT_CLK ) , .TFT_DATA(TFT_RGB) , //紅綠藍三色 分別8位量化 R[7:0]G[7:0]B[7:0] .TFT_BL (TFT_BL ));
endmodule
為了仿真驗證邏輯代碼的準確性,我們可以在RAM中提前寫入一張256*256大小的圖片數據,如下圖:
然后編寫測試代碼,驗證邏輯的正確性:
測試代碼如下:
`timescale 1ns / 1ps
module UART_RAM_TFT_TB();reg Clk; reg Reset_n;reg uart_rx;wire [15:0]TFT_RGB;wire TFT_HS;wire TFT_VS;wire TFT_DE;wire TFT_CLK;wire TFT_BL;wire led;UART_RAM_TFT UART_RAM_TFT(.Clk (Clk ) , .Reset_n(Reset_n) ,.uart_rx(uart_rx) ,.TFT_RGB(TFT_RGB) , //TFT數據輸出.TFT_HS (TFT_HS ) , // TFT行同步信號.TFT_VS (TFT_VS ) , //TFT場同步信號.TFT_DE (TFT_DE ) , //TFT數據有效信號.TFT_CLK(TFT_CLK) ,.TFT_BL (TFT_BL ) , //TFT背光.led (led )); initial Clk = 1;always #10 Clk = ~Clk;initial beginReset_n = 0;#201;Reset_n = 1;#2000;#20000000;$stop;end
endmodule
仿真波形如下:
TFT顯示屏開始接收的數據波形圖:
TFT顯示屏最后接收的數據波形圖:
7,總結
在本博客中實現了串口接收圖像寫入RAM并讀出在TFT顯示屏上顯示的這樣一個實驗。這個實驗中使用的時FPGA中片內RAM,所以只能顯示一個256*256大小的圖片。如果能夠將存儲器的容量擴大,比如DDR4存儲器,那這個時候就可以用串口傳輸一整幅圖像,就可以將完整圖片顯示在整個顯示屏上去。再其次把串口接收到的數據改為攝像頭采集到的實時的數據流,那就可以做一個攝像采集頭圖像,存儲,實時顯示的應用。再者,對采集到的圖像數據。進行一定的各種濾波算法,檢測算法等等,就可以實現圖像處理功能。