串口-RAM-VGA系列知识分享:
(0)串口通信实现-串口接收
(1)VGA成像原理与简单实现
(2)VGA显示板级验证
(3)VGA显示-多分辨率输入
(4)串口发送+RAM+VGA传图
串口发送+RAM+VGA传图
前言
本文使用串口发送模块发送数据存储到RAM中,通过RAM读取数据传输图片给TFT显示屏。
提示:以下是本篇文章正文内容,下面案例可供参考
一、结构总体设计
1.UART_RAM_TFT总体设计草图
2.img_rx_wr(RAM写入逻辑)设计草图
二、顶层模块
代码如下:
`timescale 1ns / 1ps
module UART_RAM_TFT(
Clk,
Reset_n,
uart_rx,
VGA_RGB,//TFT数据输出
VGA_HS, //TFT行同步信号
VGA_VS, //TFT场同步信号
VGA_BLK, //VGA 场消隐信号
VGA_CLK,
TFT_BL //背光
);
input Clk;
input Reset_n;
input uart_rx;
output [15:0]VGA_RGB;
output VGA_HS;
output VGA_VS;
output VGA_BLK; //VGA 场消隐信号
output VGA_CLK;
output TFT_BL;
wire [7:0]rx_data;
wire rx_done;
wire ram_wren;
wire [15:0]ram_wraddr;
wire [15:0]ram_wrdata;
reg [15:0]ram_rdaddr;
wire Clk_TFT;
wire [15:0]ram_rddata;
assign VGA_CLK = Clk_TFT;
assign TFT_BL = 1;
MMCM MMCM(
.clk_out1(Clk_TFT),
.clk_in1(Clk)
);
uart_byte_rx uart_byte_rx(
.Clk(Clk),
.Reset_n(Reset_n),
.Baud_Set(4),
.uart_rx(uart_rx),
.Data(rx_data),
.Rx_Done(rx_done)
);
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_wraddr(ram_wraddr),
.ram_wrdata(ram_wrdata)
);
RAM RAM (
.clka(Clk), // input wire clka
.ena(1), // input wire ena
.wea(ram_wren), // input wire [0 : 0] wea
.addra(ram_wraddr), // 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_rddata) // output wire [15 : 0] doutb
);
wire Data_Req;
wire [11:0]hcount,vcount;
wire [15:0]disp_data;
VGA_CTRL VGA_CTRL(
.Clk(Clk_TFT), //系统输入时钟33MHZ
.Reset_n(Reset_n),
.Data(disp_data), //待显示数据
.Data_Req(Data_Req),
.hcount(hcount), //VGA行扫描计数器
.vcount(vcount), //VGA场扫描计数器
.VGA_RGB(VGA_RGB), //VGA数据输出
.VGA_HS(VGA_HS), //VGA行同步信号
.VGA_VS(VGA_VS), //VGA场同步信号
.VGA_BLK(VGA_BLK) //VGA 场消隐信号
);
//RAM中存储的图像是256*256的像素矩阵
wire ram_data_en;
assign ram_data_en = Data_Req && (hcount <= 255) && (vcount <= 255);
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 data_en;
wire [7:0]R ,G ,B;
assign R = 8'hFF,G = 8'h00,B = 8'h00;
wire [15:0]RED;
assign RED = {R[7:3],G[7:2],B[7:3]};
assign data_en = Data_Req && (hcount <= 257) && (vcount <= 255);
assign disp_data = data_en? ram_rddata:RED;//将多余图像显示红色
//assign disp_data = ram_data_en? ram_rddata:0; //将多余数据归0显示黑色
endmodule
三、img_rx_wr模块
module img_rx_wr(
Clk,
Reset_n,
rx_data,
rx_done,
ram_wren,
ram_wraddr,
ram_wrdata
);
input Clk;
input Reset_n;
input [7:0]rx_data; //串口数据
input rx_done; //串口发送停止信号
output reg ram_wren; //开始写入信号
output reg[15:0]ram_wraddr; //RAM核写入地址
output [15:0]ram_wrdata; //写入数据
reg [15:0]rx_data_tmp;
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;
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
rx_data_tmp <= 0;
else if(rx_done) //串口一次发一个字节,RGB565一次需要2个字节
rx_data_tmp <= {rx_data_tmp[7:0], rx_data};
//---------------------------------------------------------------
//17'h0 ...0000
//17'h1 ...0001
//17'h2 ...0010
//根据规律奇数末位为1
//---------------------------------------------------------------
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_wraddr <= 0;
else if(rx_done && data_cnt[0])
ram_wraddr <= data_cnt[16:1]; //data_cnt/2
assign ram_wrdata = rx_data_tmp;
endmodule
四、UART_rx模块
`timescale 1ns/1ns
module uart_byte_rx(
Clk,
Reset_n,
Baud_Set,
uart_rx,
Data,
Rx_Done,
);
input Clk;
input Reset_n;
input [2:0]Baud_Set;
input uart_rx;
output reg[7:0]Data;
output reg Rx_Done;
reg [1:0]sync_uart_rx;
always@(posedge Clk)begin
sync_uart_rx[0] <= #1 uart_rx;
sync_uart_rx[1] <= #1 sync_uart_rx[0] ;
end
reg [1:0]uart_rx_r;
always@(posedge Clk)begin
uart_rx_r[0] <= #1 sync_uart_rx[1];
uart_rx_r[1] <= #1 uart_rx_r[0] ;
end
wire pedge_uart_rx;
// assign pedge_uart_rx = ((uart_rx_r[1] == 0) && (uart_rx_r[0] == 1));
assign pedge_uart_rx = (uart_rx_r == 2'b01);
wire nedge_uart_rx;
// assign nedge_uart_rx = ((uart_rx_r[1] == 1) && (uart_rx_r[0] == 0));
assign nedge_uart_rx = (uart_rx_r == 2'b10);
reg [8:0] Bps_DR;
always@(*)
case(Baud_Set)
0:Bps_DR = 1000000000/9600/16/20 - 2;
1:Bps_DR = 1000000000/19200/16/20 -2;
2:Bps_DR = 1000000000/38400/16/20 - 2;
3:Bps_DR = 1000000000/57600/16/20 - 2;
4:Bps_DR = 1000000000/115200/16/20 - 2;
5:Bps_DR = 1000000000/1562500/16/20 - 2;
default:Bps_DR = 1000000000/9600/16/20 - 1;
endcase
wire bps_clk_16x;
assign bps_clk_16x = (div_cnt == Bps_DR / 2);
reg RX_EN;
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
RX_EN <= #1 0;
else if(nedge_uart_rx)
RX_EN <= #1 1;
else if(Rx_Done || (sta_bit >= 4))
RX_EN <= #1 0;
reg [8:0]div_cnt;
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
div_cnt <= #1 0;
else if(RX_EN)begin
if(div_cnt == Bps_DR)
div_cnt <= #1 0;
else
div_cnt <= #1 div_cnt + 1'b1;
end
else
div_cnt <= #1 0;
reg [7:0]bps_cnt;
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
bps_cnt <= #1 0;
else if(RX_EN)begin
if(bps_clk_16x)begin
if(bps_cnt == 160)
bps_cnt <= #1 0;
else
bps_cnt <= #1 bps_cnt + 1'b1;
end
else
bps_cnt <= #1 bps_cnt;
end
else
bps_cnt <= #1 0;
//reg width name number/depth
reg[2:0]r_data[7:0];
reg [2:0]sta_bit;
reg [2:0]sto_bit;
always@(posedge Clk or negedge Reset_n)
if(!Reset_n) begin
sta_bit <= #1 0;
sto_bit <= #1 0;
r_data[0] <= #1 0;
r_data[1] <= #1 0;
r_data[2] <= #1 0;
r_data[3] <= #1 0;
r_data[4] <= #1 0;
r_data[5] <= #1 0;
r_data[6] <= #1 0;
r_data[7] <= #1 0;
end
else if(bps_clk_16x)begin
case(bps_cnt)
0:begin
sta_bit <= #1 0;
sto_bit <= #1 0;
r_data[0] <= #1 0;
r_data[1] <= #1 0;
r_data[2] <= #1 0;
r_data[3] <= #1 0;
r_data[4] <= #1 0;
r_data[5] <= #1 0;
r_data[6] <= #1 0;
r_data[7] <= #1 0;
end
5,6,7,8,9,10,11:sta_bit <= #1 sta_bit + sync_uart_rx[1];
21,22,23,24,25,26,27: r_data[0] <= #1 r_data[0] + sync_uart_rx[1];
37,38,39,40,41,42,43: r_data[1] <= #1 r_data[1] + sync_uart_rx[1];
53,54,55,56,57,58,59: r_data[2] <= #1 r_data[2] + sync_uart_rx[1];
69,70,71,72,73,74,75: r_data[3] <= #1 r_data[3] + sync_uart_rx[1];
85,86,87,88,89,90,91: r_data[4] <= #1 r_data[4] + sync_uart_rx[1];
101,102,103,104,105,106,107: r_data[5] <= #1 r_data[5] + sync_uart_rx[1];
117,118,119,120,121,122,123: r_data[6] <= #1 r_data[6] + sync_uart_rx[1];
133,134,135,136,137,138,139: r_data[7] <= #1 r_data[7] + sync_uart_rx[1];
149,150,151,152,153,154,155: sto_bit <= #1 sto_bit + sync_uart_rx[1];
default:;
endcase
end
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
Data <= #1 0;
else if(bps_clk_16x && (bps_cnt == 159))begin
Data[0] <= #1 (r_data[0] >= 4)?1'b1:1'b0;
Data[1] <= #1 (r_data[1] >= 4)?1'b1:1'b0;
Data[2] <= #1 (r_data[2] >= 4)?1'b1:1'b0;
Data[3] <= #1 (r_data[3] >= 4)?1'b1:1'b0;
Data[4] <= #1 (r_data[4] >= 4)?1'b1:1'b0;
Data[5] <= #1 (r_data[5] >= 4)?1'b1:1'b0;
Data[6] <= #1 (r_data[6] >= 4)?1'b1:1'b0;
Data[7] <= #1 (r_data[7] >= 4)?1'b1:1'b0;
end
// always@(posedge Clk or negedge Reset_n)
// if(!Reset_n)
// Data <= #1 0;
// else if(bps_clk_16x && (bps_cnt == 159))begin
// Data[0] <= #1 r_data[0][2];
// Data[1] <= #1 r_data[1][2];
// Data[2] <= #1 r_data[2][2];
// Data[3] <= #1 r_data[3][2];
// Data[4] <= #1 r_data[4][2];
// Data[5] <= #1 r_data[5][2];
// Data[6] <= #1 r_data[6][2];
// Data[7] <= #1 r_data[7][2];
// end
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
Rx_Done <= #1 0;
else if((div_cnt == Bps_DR/2) && (bps_cnt == 160))
Rx_Done <= #1 1;
else
Rx_Done <= #1 0;
endmodule
五、VGA_CTRL模块
module VGA_CTRL(
Clk,
Reset_n,
Data,
Data_Req,
hcount,
vcount,
VGA_HS,
VGA_VS,
VGA_BLK,
VGA_RGB
);
input Clk;
input Reset_n;
input [15:0]Data;
output reg Data_Req;
output reg [11:0]hcount; //当前扫描点的H坐标
output reg [11:0]vcount; //当前扫描点的V坐标
output VGA_HS;
output VGA_VS;
output VGA_BLK;
output reg [15:0]VGA_RGB;//{R[4:0]、G[5:0]、B[4:0]}
`include "vga_parameter.v"
localparam Hsync_End = `H_Total_Time;
localparam HS_End = `H_Sync_Time;
localparam Hdat_Begin = `H_Sync_Time + `H_Back_Porch + `H_Left_Border;
localparam Hdat_End = `H_Sync_Time + `H_Left_Border + `H_Back_Porch + `H_Data_Time;
localparam Vsync_End = `V_Total_Time;
localparam VS_End = `V_Sync_Time;
localparam Vdat_Begin = `V_Sync_Time + `V_Back_Porch + `V_Top_Border;
localparam Vdat_End = `V_Sync_Time + `V_Back_Porch + `V_Top_Border + `V_Data_Time;
reg [11:0]hcnt;//行扫描计数器
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
hcnt <= 0;
else if(hcnt >= Hsync_End -1)
hcnt <= 0;
else
hcnt <= hcnt + 1'b1;
//RAM_IP核输出数据有三拍延迟此处为了对齐数据传输
reg [3:0]VGA_HS_r;
always@(posedge Clk)begin
VGA_HS_r[0] <= (hcnt < HS_End)?0:1;
VGA_HS_r[3:1] <= VGA_HS_r[2:0];
end
assign VGA_HS = VGA_HS_r[2];
reg [11:0]vcnt;//场扫描计数器
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
vcnt <= 0;
else if(hcnt == Hsync_End -1)begin
if(vcnt >= Vsync_End -1)
vcnt <= 0;
else
vcnt <= vcnt + 1'd1;
end
else
vcnt <= vcnt;
//RAM_IP核输出数据有三拍延迟此处为了对齐数据传输
reg [3:0]VGA_VS_r;
always@(posedge Clk)begin
VGA_VS_r[0] <= (vcnt < VS_End)?0:1;
VGA_VS_r[3:1] <= VGA_VS_r[2:0];
end
assign VGA_VS = VGA_VS_r[2];
//BLK表示的就是输出输出的时间段
// assign VGA_BLK = ((hcnt >= Hdat_Begin - 1) && (hcnt < Hdat_End - 1) && (vcnt >= Vdat_Begin - 1) && (vcnt < Vdat_End))?1:0;
// always@(posedge Clk)
// VGA_BLK <= ((hcnt >= Hdat_Begin - 1) && (hcnt < Hdat_End - 1) && (vcnt >= Vdat_Begin - 1) && (vcnt < Vdat_End))?1:0;
always@(posedge Clk)
Data_Req <= ((hcnt >= Hdat_Begin - 1) && (hcnt < Hdat_End - 1) && (vcnt >= Vdat_Begin) && (vcnt < Vdat_End))?1:0;
//RAM_IP核输出数据有三拍延迟此处为了对齐数据传输
reg [3:0]VGA_BLK_r;
always@(posedge Clk)begin
VGA_BLK_r[0] <= Data_Req;
VGA_BLK_r[3:1] <= VGA_BLK_r[2:0];
end
assign VGA_BLK = VGA_BLK_r[3];
// assign VGA_RGB = VGA_BLK? Data:0;
always@(posedge Clk)
VGA_RGB <= Data_Req? Data:0;
always@(posedge Clk)
hcount <= Data_Req? hcnt - Hdat_Begin:0;
always@(posedge Clk)
vcount <= Data_Req? (vcnt - Vdat_Begin):vcount;
endmodule
六、parameter.v文件
//`define Resolution_480x272 1 //刷新率为60Hz时像素时钟为9MHz
//`define Resolution_640x480 1 //刷新率为60Hz时像素时钟为25.175MHz
`define Resolution_800x480 1 //刷新率为60Hz时像素时钟为33MHz
//`define Resolution_800x600 1 //刷新率为60Hz时像素时钟为40MHz
//`define Resolution_1024x768 1 //刷新率为60Hz时像素时钟为65MHz
//`define Resolution_1280x720 1 //刷新率为60Hz时像素时钟为74.25MHz
//`define Resolution_1920x1080 1 //刷新率为60Hz时像素时钟为148.5MHz
`ifdef Resolution_480x272
`define H_Right_Border 0
`define H_Front_Porch 2
`define H_Sync_Time 41
`define H_Back_Porch 2
`define H_Left_Border 0
`define H_Data_Time 480
`define H_Total_Time 525
`define V_Bottom_Border 0
`define V_Front_Porch 2
`define V_Sync_Time 10
`define V_Back_Porch 2
`define V_Top_Border 0
`define V_Data_Time 272
`define V_Total_Time 286
`elsif Resolution_640x480
`define H_Total_Time 12'd800
`define H_Right_Border 12'd8
`define H_Front_Porch 12'd8
`define H_Sync_Time 12'd96
`define H_Data_Time 12'd640
`define H_Back_Porch 12'd40
`define H_Left_Border 12'd8
`define V_Total_Time 12'd525
`define V_Bottom_Border 12'd8
`define V_Front_Porch 12'd2
`define V_Sync_Time 12'd2
`define V_Data_Time 12'd480
`define V_Back_Porch 12'd25
`define V_Top_Border 12'd8
`elsif Resolution_800x480
`define H_Total_Time 12'd1056
`define H_Right_Border 12'd0
`define H_Front_Porch 12'd40
`define H_Sync_Time 12'd128
`define H_Data_Time 12'd800
`define H_Back_Porch 12'd88
`define H_Left_Border 12'd0
`define V_Total_Time 12'd525
`define V_Bottom_Border 12'd8
`define V_Front_Porch 12'd2
`define V_Sync_Time 12'd2
`define V_Data_Time 12'd480
`define V_Back_Porch 12'd25
`define V_Top_Border 12'd8
`elsif Resolution_800x600
`define H_Total_Time 12'd1056
`define H_Right_Border 12'd0
`define H_Front_Porch 12'd40
`define H_Sync_Time 12'd128
`define H_Data_Time 12'd800
`define H_Back_Porch 12'd88
`define H_Left_Border 12'd0
`define V_Total_Time 12'd628
`define V_Bottom_Border 12'd0
`define V_Front_Porch 12'd1
`define V_Sync_Time 12'd4
`define V_Data_Time 12'd600
`define V_Back_Porch 12'd23
`define V_Top_Border 12'd0
`elsif Resolution_1024x768
`define H_Total_Time 12'd1344
`define H_Right_Border 12'd0
`define H_Front_Porch 12'd24
`define H_Sync_Time 12'd136
`define H_Data_Time 12'd1024
`define H_Back_Porch 12'd160
`define H_Left_Border 12'd0
`define V_Total_Time 12'd806
`define V_Bottom_Border 12'd0
`define V_Front_Porch 12'd3
`define V_Sync_Time 12'd6
`define V_Data_Time 12'd768
`define V_Back_Porch 12'd29
`define V_Top_Border 12'd0
`elsif Resolution_1280x720
`define H_Total_Time 12'd1650
`define H_Right_Border 12'd0
`define H_Front_Porch 12'd110
`define H_Sync_Time 12'd40
`define H_Data_Time 12'd1280
`define H_Back_Porch 12'd220
`define H_Left_Border 12'd0
`define V_Total_Time 12'd750
`define V_Bottom_Border 12'd0
`define V_Front_Porch 12'd5
`define V_Sync_Time 12'd5
`define V_Data_Time 12'd720
`define V_Back_Porch 12'd20
`define V_Top_Border 12'd0
`elsif Resolution_1920x1080
`define H_Total_Time 12'd2200
`define H_Right_Border 12'd0
`define H_Front_Porch 12'd88
`define H_Sync_Time 12'd44
`define H_Data_Time 12'd1920
`define H_Back_Porch 12'd148
`define H_Left_Border 12'd0
`define V_Total_Time 12'd1125
`define V_Bottom_Border 12'd0
`define V_Front_Porch 12'd4
`define V_Sync_Time 12'd5
`define V_Data_Time 12'd1080
`define V_Back_Porch 12'd36
`define V_Top_Border 12'd0
`endif
七、clk_IP和RAM_IP配置
具体IP配置根据所需选择,可参考我之前文章,自行根据所需配置。
八、仿真文件展示
`timescale 1ns / 1ps
module UART_RAM_TFT_tb(
);
reg Clk;
reg Reset_n;
reg uart_rx;
wire [15:0]VGA_RGB;
wire VGA_HS;
wire VGA_VS;
wire VGA_BLK; //VGA 场消隐信号
wire VGA_CLK;
wire TFT_BL;
UART_RAM_TFT UART_RAM_TFT(
Clk,
Reset_n,
uart_rx,
VGA_RGB,//TFT数据输出
VGA_HS, //TFT行同步信号
VGA_VS, //TFT场同步信号
VGA_BLK, //VGA 场消隐信号
VGA_CLK,
TFT_BL //背光
);
initial Clk = 1;
always#10 Clk = ~Clk;
initial begin
Reset_n = 0;
#201;
Reset_n = 1;
#2000;
#2000000;
$stop;
end
endmodule
九、上板演示
串口传图展示:
【附件:】链接:https://pan.baidu.com/s/1wWgnTeCfzlFzGWUCQe7ANg?pwd=mnz3
提取码:mnz3