赞
踩
需要用到的模块有:
1,UART_RX(串口接收模块);
2,串口接受的数据存放到RAM模块;
3,RAM IP核;
4,时钟IP核 (TFT显示屏驱动时钟的产生);
5,TFT显示驱动模块;
具体构建方式及详见(其中的串口接收部分)
FPGA-UART串口https://blog.csdn.net/weixin_46897065/article/details/135586405?spm=1001.2014.3001.5502
串口接受的数据存放到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;
- else
- ram_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/2
-
-
- assign 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 begin
- Reset_n = 0;
- rx_data = 0;
- rx_done = 0;
- #201;
- Reset_n = 1;
- #2000;
- rx_data = 8'd255;
- repeat(131072)begin
- rx_done = 1;
- #20;
- rx_done = 0;
- #200;
- rx_data = rx_data - 1;
- end
- #2000000;
-
- repeat(131072)begin
- rx_done = 1;
- #20;
- rx_done = 0;
- #200;
- rx_data = rx_data - 1;
- end
- $stop;
-
- end
-
- endmodule
仿真波形如下:
写入第一个数据时:
写入最后一个数据时:
RAM写逻辑已经完成,接下来完成RAM的读逻辑。
具体构建方式及其内部参数详见FPGA-学会使用vivado中的存储器资源RAM(IP核)https://blog.csdn.net/weixin_46897065/article/details/136325283?spm=1001.2014.3001.5502
具体构建方式详见:
FPGA-时钟管理单元https://blog.csdn.net/weixin_46897065/article/details/136356331?spm=1001.2014.3001.5502
具体原理详见
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 = 525
-
- module 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;
- else
- hcount_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;
- else
- vcount_r <= vcount_r + 1'd1;
- else
- vcount_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) begin
- TFT_DE_r[0] <= Data_req;
- TFT_DE_r[3:1] <= TFT_DE_r[2:0];
- end
-
- assign 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) begin
- hcount <= 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) begin
- TFT_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) begin
- TFT_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) begin
- TFT_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
将以上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 begin
- Reset_n = 0;
- #201;
- Reset_n = 1;
- #2000;
- #20000000;
- $stop;
- end
- endmodule
仿真波形如下:
TFT显示屏开始接收的数据波形图:
TFT显示屏最后接收的数据波形图:
在本博客中实现了串口接收图像写入RAM并读出在TFT显示屏上显示的这样一个实验。这个实验中使用的时FPGA中片内RAM,所以只能显示一个256*256大小的图片。如果能够将存储器的容量扩大,比如DDR4存储器,那这个时候就可以用串口传输一整幅图像,就可以将完整图片显示在整个显示屏上去。再其次把串口接收到的数据改为摄像头采集到的实时的数据流,那就可以做一个摄像采集头图像,存储,实时显示的应用。再者,对采集到的图像数据。进行一定的各种滤波算法,检测算法等等,就可以实现图像处理功能。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。