文章目录
第40讲:基于ROM的VGA图像显示
实现图标:将存储在ROM的图片显示出来
系统框图
各模块
vga_pic
`timescale 1ns/1ns
module vga_pic
(
input wire vga_clk , //输入工作时钟,频率25MHz
input wire sys_rst_n , //输入复位信号,低电平有效
input wire [9:0] pix_x , //输入有效显示区域像素点X轴坐标
input wire [9:0] pix_y , //输入有效显示区域像素点Y轴坐标
output wire [15:0] pix_data_out //输出VGA显示图像数据
);
parameter H_VALID = 10'd640 , //行有效数据
V_VALID = 10'd480 ; //场有效数据
parameter H_PIC = 10'd100 , //图片长度
W_PIC = 10'd100 , //图片宽度
PIC_SIZE= 14'd10000 ; //图片像素个数
parameter RED = 16'hF800 , //红色
ORANGE = 16'hFC00 , //橙色
YELLOW = 16'hFFE0 , //黄色
GREEN = 16'h07E0 , //绿色
CYAN = 16'h07FF , //青色
BLUE = 16'h001F , //蓝色
PURPPLE = 16'hF81F , //紫色
BLACK = 16'h0000 , //黑色
WHITE = 16'hFFFF , //白色
GRAY = 16'hD69A ; //灰色
//wire define
wire rd_en ; //ROM读使能
wire [15:0] pic_data ; //自ROM读出的图片数据
//reg define
reg [13:0] rom_addr ; //读ROM地址
reg pic_valid ; //图片数据有效信号
reg [15:0] pix_data ; //背景色彩信息
//rd_en:ROM读使能
assign rd_en = (((pix_x >= (((H_VALID - H_PIC)/2) - 1'b1))
&& (pix_x < (((H_VALID - H_PIC)/2) + H_PIC - 1'b1)))
&&((pix_y >= ((V_VALID - W_PIC)/2))
&& ((pix_y < (((V_VALID - W_PIC)/2) + W_PIC)))));
//pic_valid:图片数据有效信号
always@(posedge vga_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
pic_valid <= 1'b1;
else
pic_valid <= rd_en;
//pix_data_out:输出VGA显示图像数据
assign pix_data_out = (pic_valid == 1'b1) ? pic_data : pix_data;
//根据当前像素点坐标指定当前像素点颜色数据,在屏幕上显示彩条
always@(posedge vga_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
pix_data <= 16'd0;
else if((pix_x >= 0) && (pix_x < (H_VALID/10)*1))
pix_data <= RED;
else if((pix_x >= (H_VALID/10)*1) && (pix_x < (H_VALID/10)*2))
pix_data <= ORANGE;
else if((pix_x >= (H_VALID/10)*2) && (pix_x < (H_VALID/10)*3))
pix_data <= YELLOW;
else if((pix_x >= (H_VALID/10)*3) && (pix_x < (H_VALID/10)*4))
pix_data <= GREEN;
else if((pix_x >= (H_VALID/10)*4) && (pix_x < (H_VALID/10)*5))
pix_data <= CYAN;
else if((pix_x >= (H_VALID/10)*5) && (pix_x < (H_VALID/10)*6))
pix_data <= BLUE;
else if((pix_x >= (H_VALID/10)*6) && (pix_x < (H_VALID/10)*7))
pix_data <= PURPPLE;
else if((pix_x >= (H_VALID/10)*7) && (pix_x < (H_VALID/10)*8))
pix_data <= BLACK;
else if((pix_x >= (H_VALID/10)*8) && (pix_x < (H_VALID/10)*9))
pix_data <= WHITE;
else if((pix_x >= (H_VALID/10)*9) && (pix_x < H_VALID))
pix_data <= GRAY;
else
pix_data <= BLACK;
//rom_addr:读ROM地址
always@(posedge vga_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
rom_addr <= 14'd0;
else if(rom_addr == (PIC_SIZE - 1'b1))
rom_addr <= 14'd0;
else if(rd_en == 1'b1)
rom_addr <= rom_addr + 1'b1;
//------------- rom_pic_inst -------------
rom_pic rom_pic_inst
(
.address (rom_addr ), //输入读ROM地址,14bit
.clock (vga_clk ), //输入读时钟,vga_clk,频率25MHz,1bit
.rden (rd_en ), //输入读使能,1bit
.q (pic_data ) //输出读数据,16bit
);
endmodule
vga_ctrl
`timescale 1ns/1ns
module vga_ctrl
(
input wire vga_clk , //输入工作时钟,频率25MHz
input wire sys_rst_n , //输入复位信号,低电平有效
input wire [15:0] pix_data , //输入像素点色彩信息
output wire [9:0] pix_x , //输出有效显示区域像素点X轴坐标
output wire [9:0] pix_y , //输出有效显示区域像素点Y轴坐标
output wire hsync , //输出行同步信号
output wire vsync , //输出场同步信号
output wire [15:0] rgb //输出像素点色彩信息
);
//parameter define
parameter H_SYNC = 10'd96 , //行同步
H_BACK = 10'd40 , //行时序后沿
H_LEFT = 10'd8 , //行时序左边框
H_VALID = 10'd640 , //行有效数据
H_RIGHT = 10'd8 , //行时序右边框
H_FRONT = 10'd8 , //行时序前沿
H_TOTAL = 10'd800 ; //行扫描周期
parameter V_SYNC = 10'd2 , //场同步
V_BACK = 10'd25 , //场时序后沿
V_TOP = 10'd8 , //场时序左边框
V_VALID = 10'd480 , //场有效数据
V_BOTTOM = 10'd8 , //场时序右边框
V_FRONT = 10'd2 , //场时序前沿
V_TOTAL = 10'd525 ; //场扫描周期
parameter LENGTH_W = 10'd200 , //白框长度
WIDE_W = 10'd200 ; //白框宽度
//wire define
wire rgb_valid ; //VGA有效显示区域
wire pix_data_req ; //像素点色彩信息请求信号
//reg define
reg [9:0] cnt_h ; //行同步信号计数器
reg [9:0] cnt_v ; //场同步信号计数器
//cnt_h:行同步信号计数器
always@(posedge vga_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_h <= 10'd0 ;
else if(cnt_h == H_TOTAL - 1'd1)
cnt_h <= 10'd0 ;
else
cnt_h <= cnt_h + 1'd1 ;
//hsync:行同步信号
assign hsync = (cnt_h <= H_SYNC - 1'd1) ? 1'b1 : 1'b0 ;
//cnt_v:场同步信号计数器
always@(posedge vga_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_v <= 10'd0 ;
else if((cnt_v == V_TOTAL - 1'd1) && (cnt_h == H_TOTAL-1'd1))
cnt_v <= 10'd0 ;
else if(cnt_h == H_TOTAL - 1'd1)
cnt_v <= cnt_v + 1'd1 ;
else
cnt_v <= cnt_v ;
//vsync:场同步信号
assign vsync = (cnt_v <= V_SYNC - 1'd1) ? 1'b1 : 1'b0 ;
//rgb_valid:VGA有效显示区域
assign rgb_valid = (((cnt_h >= H_SYNC + H_BACK + H_LEFT)
&& (cnt_h < H_SYNC + H_BACK + H_LEFT + H_VALID))
&&((cnt_v >= V_SYNC + V_BACK + V_TOP)
&& (cnt_v < V_SYNC + V_BACK + V_TOP + V_VALID)))
? 1'b1 : 1'b0;
//pix_data_req:像素点色彩信息请求信号,超前rgb_valid信号一个时钟周期
assign pix_data_req = (((cnt_h >= H_SYNC + H_BACK + H_LEFT - 1'b1)
&& (cnt_h < H_SYNC + H_BACK + H_LEFT + H_VALID - 1'b1))
&&((cnt_v >= V_SYNC + V_BACK + V_TOP)
&& (cnt_v < V_SYNC + V_BACK + V_TOP + V_VALID)))
? 1'b1 : 1'b0;
//pix_x,pix_y:VGA有效显示区域像素点坐标
assign pix_x = (pix_data_req == 1'b1)
? (cnt_h - (H_SYNC + H_BACK + H_LEFT - 1'b1)) : 10'h3ff;
assign pix_y = (pix_data_req == 1'b1)
? (cnt_v - (V_SYNC + V_BACK + V_TOP)) : 10'h3ff;
//rgb:输出像素点色彩信息
assign rgb = (rgb_valid == 1'b1) ? pix_data : 16'b0 ;
endmodule
vga_rom_pic
`timescale 1ns/1ns
module vga_rom_pic
(
input wire sys_clk , //输入工作时钟,频率50MHz
input wire sys_rst_n , //输入复位信号,低电平有效
output wire hsync , //输出行同步信号
output wire vsync , //输出场同步信号
output wire [15:0] rgb //输出像素信息
);
//wire define
wire vga_clk ; //VGA工作时钟,频率25MHz
wire locked ; //PLL locked信号
wire rst_n ; //VGA模块复位信号
wire [9:0] pix_x ; //VGA有效显示区域X轴坐标
wire [9:0] pix_y ; //VGA有效显示区域Y轴坐标
wire [15:0] pix_data; //VGA像素点色彩信息
//rst_n:VGA模块复位信号
assign rst_n = (sys_rst_n & locked);
//------------- clk_gen_inst -------------
clk_gen clk_gen_inst
(
.areset (~sys_rst_n ), //输入复位信号,高电平有效,1bit
.inclk0 (sys_clk ), //输入50MHz晶振时钟,1bit
.c0 (vga_clk ), //输出VGA工作时钟,频率25Mhz,1bit
.locked (locked ) //输出pll locked信号,1bit
);
//------------- vga_ctrl_inst -------------
vga_ctrl vga_ctrl_inst
(
.vga_clk (vga_clk ), //输入工作时钟,频率25MHz,1bit
.sys_rst_n (rst_n ), //输入复位信号,低电平有效,1bit
.pix_data (pix_data ), //输入像素点色彩信息,16bit
.pix_x (pix_x ), //输出VGA有效显示区域像素点X轴坐标,10bit
.pix_y (pix_y ), //输出VGA有效显示区域像素点Y轴坐标,10bit
.hsync (hsync ), //输出行同步信号,1bit
.vsync (vsync ), //输出场同步信号,1bit
.rgb (rgb ) //输出像素点色彩信息,16bit
);
//------------- vga_pic_inst -------------
vga_pic vga_pic_inst
(
.vga_clk (vga_clk ), //输入工作时钟,频率25MHz,1bit
.sys_rst_n (rst_n ), //输入复位信号,低电平有效,1bit
.pix_x (pix_x ), //输入VGA有效显示区域像素点X轴坐标,10bit
.pix_y (pix_y ), //输入VGA有效显示区域像素点Y轴坐标,10bit
.pix_data_out (pix_data ) //输出像素点色彩信息,16bit
);
endmodule
第41讲:基于ROM的VGA图像显示(弹跳特效)
实验目标:图像运动显示
各模块
vga_ctrl
`timescale 1ns/1ns
module vga_ctrl
(
input wire vga_clk , //输入工作时钟,频率25MHz
input wire sys_rst_n , //输入复位信号,低电平有效
input wire [15:0] pix_data , //输入像素点色彩信息
output wire [9:0] pix_x , //输出有效显示区域像素点X轴坐标
output wire [9:0] pix_y , //输出有效显示区域像素点Y轴坐标
output wire hsync , //输出行同步信号
output wire vsync , //输出场同步信号
output wire [15:0] rgb //输出像素点色彩信息
);
//parameter define
parameter H_SYNC = 10'd96 , //行同步
H_BACK = 10'd40 , //行时序后沿
H_LEFT = 10'd8 , //行时序左边框
H_VALID = 10'd640 , //行有效数据
H_RIGHT = 10'd8 , //行时序右边框
H_FRONT = 10'd8 , //行时序前沿
H_TOTAL = 10'd800 ; //行扫描周期
parameter V_SYNC = 10'd2 , //场同步
V_BACK = 10'd25 , //场时序后沿
V_TOP = 10'd8 , //场时序左边框
V_VALID = 10'd480 , //场有效数据
V_BOTTOM = 10'd8 , //场时序右边框
V_FRONT = 10'd2 , //场时序前沿
V_TOTAL = 10'd525 ; //场扫描周期
parameter LENGTH_W = 10'd200 , //白框长度
WIDE_W = 10'd200 ; //白框宽度
//wire define
wire rgb_valid ; //VGA有效显示区域
wire pix_data_req ; //像素点色彩信息请求信号
//reg define
reg [9:0] cnt_h ; //行同步信号计数器
reg [9:0] cnt_v ; //场同步信号计数器
//cnt_h:行同步信号计数器
always@(posedge vga_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_h <= 10'd0 ;
else if(cnt_h == H_TOTAL - 1'd1)
cnt_h <= 10'd0 ;
else
cnt_h <= cnt_h + 1'd1 ;
//hsync:行同步信号
assign hsync = (cnt_h <= H_SYNC - 1'd1) ? 1'b1 : 1'b0 ;
//cnt_v:场同步信号计数器
always@(posedge vga_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_v <= 10'd0 ;
else if((cnt_v == V_TOTAL - 1'd1) && (cnt_h == H_TOTAL-1'd1))
cnt_v <= 10'd0 ;
else if(cnt_h == H_TOTAL - 1'd1)
cnt_v <= cnt_v + 1'd1 ;
else
cnt_v <= cnt_v ;
//vsync:场同步信号
assign vsync = (cnt_v <= V_SYNC - 1'd1) ? 1'b1 : 1'b0 ;
//rgb_valid:VGA有效显示区域
assign rgb_valid = (((cnt_h >= H_SYNC + H_BACK + H_LEFT)
&& (cnt_h < H_SYNC + H_BACK + H_LEFT + H_VALID))
&&((cnt_v >= V_SYNC + V_BACK + V_TOP)
&& (cnt_v < V_SYNC + V_BACK + V_TOP + V_VALID)))
? 1'b1 : 1'b0;
//pix_data_req:像素点色彩信息请求信号,超前rgb_valid信号一个时钟周期
assign pix_data_req = (((cnt_h >= H_SYNC + H_BACK + H_LEFT - 1'b1)
&& (cnt_h < H_SYNC + H_BACK + H_LEFT + H_VALID - 1'b1))
&&((cnt_v >= V_SYNC + V_BACK + V_TOP)
&& (cnt_v < V_SYNC + V_BACK + V_TOP + V_VALID)))
? 1'b1 : 1'b0;
//pix_x,pix_y:VGA有效显示区域像素点坐标
assign pix_x = (pix_data_req == 1'b1)
? (cnt_h - (H_SYNC + H_BACK + H_LEFT - 1'b1)) : 10'h3ff;
assign pix_y = (pix_data_req == 1'b1)
? (cnt_v - (V_SYNC + V_BACK + V_TOP)) : 10'h3ff;
//rgb:输出像素点色彩信息
assign rgb = (rgb_valid == 1'b1) ? pix_data : 16'b0 ;
endmodule
vga_pic
`timescale 1ns/1ns
module vga_pic
(
input wire vga_clk , //输入工作时钟,频率25MHz
input wire sys_rst_n , //输入复位信号,低电平有效
input wire [9:0] pix_x , //输入VGA有效显示区域像素点X轴坐标
input wire [9:0] pix_y , //输入VGA有效显示区域像素点Y轴坐标
output wire [15:0] pix_data_out //输出VGA显示图像数据
);
parameter H_VALID = 10'd640 , //行有效数据
V_VALID = 10'd480 ; //场有效数据
parameter H_PIC = 10'd100 , //图片长度
W_PIC = 10'd100 , //图片宽度
PIC_SIZE= 14'd10000 ; //图片像素个数
parameter RED = 16'hF800 , //红色
ORANGE = 16'hFC00 , //橙色
YELLOW = 16'hFFE0 , //黄色
GREEN = 16'h07E0 , //绿色
CYAN = 16'h07FF , //青色
BLUE = 16'h001F , //蓝色
PURPPLE = 16'hF81F , //紫色
BLACK = 16'h0000 , //黑色
WHITE = 16'hFFFF , //白色
GRAY = 16'hD69A ; //灰色
//wire define
wire rd_en ; //ROM读使能
wire [15:0] pic_data ; //自ROM读出的图片数据
//reg define
reg [13:0] rom_addr ; //读ROM地址
reg pic_valid ; //图片数据有效信号
reg [15:0] pix_data ; //背景色彩信息
reg [9:0] x_move ; //图片横向移动量
reg [9:0] y_move ; //图片纵向移动量
reg x_flag ; //图片左右移动标志
reg y_flag ; //图片上下移动标志
//x_flag:图片左右移动标志
always@(posedge vga_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
x_flag <= 1'b0;
else if(x_move == 10'd0)
x_flag <= 1'b0;
else if((x_move == (H_VALID - H_PIC - 1'b1))
&& (pix_x == (H_VALID - 1'b1))
&& (pix_y == (V_VALID - 1'b1)))
x_flag <= 1'b1;
//x_move:图片横向移动量
always@(posedge vga_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
x_move <= 10'd0;
else if((x_flag == 1'b0) && (pix_x == (H_VALID - 1'b1))
&& (pix_y == (V_VALID -1'b1)))
x_move <= x_move + 1'b1;
else if((x_flag == 1'b1) && (pix_x == (H_VALID - 1'b1))
&& (pix_y == (V_VALID -1'b1)))
x_move <= x_move - 1'b1;
//y_flag:图片上下移动标志
always@(posedge vga_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
y_flag <= 1'b0;
else if(y_move == 0)
y_flag <= 1'b0;
else if((y_move == (V_VALID - W_PIC - 1'b1))
&& (pix_x == (H_VALID - 1'b1))
&& (pix_y == (V_VALID - 1'b1)))
y_flag <= 1'b1;
//y_move:图片纵向移动量
always@(posedge vga_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
y_move <= 10'd0;
else if((y_flag == 1'b0) && (pix_x == (H_VALID - 1'b1))
&& (pix_y == (V_VALID -1'b1)))
y_move <= y_move + 1'b1;
else if((y_flag == 1'b1) && (pix_x == (H_VALID - 1'b1))
&& (pix_y == (V_VALID -1'b1)))
y_move <= y_move - 1'b1;
//rd_en:ROM读使能
assign rd_en = (((pix_x >= x_move) && (pix_x < (x_move + H_PIC)))
&&((pix_y >= y_move) && (pix_y < (y_move + W_PIC))));
//pic_valid:图片数据有效信号
always@(posedge vga_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
pic_valid <= 1'b1;
else
pic_valid <= rd_en;
//pix_data_out:输出VGA显示图像数据
assign pix_data_out = (pic_valid == 1'b1) ? pic_data : pix_data;
//根据当前像素点坐标指定当前像素点颜色数据,在屏幕上显示彩条
always@(posedge vga_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
pix_data <= 16'd0;
else if((pix_x >= 0) && (pix_x < (H_VALID/10)*1))
pix_data <= RED;
else if((pix_x >= (H_VALID/10)*1) && (pix_x < (H_VALID/10)*2))
pix_data <= ORANGE;
else if((pix_x >= (H_VALID/10)*2) && (pix_x < (H_VALID/10)*3))
pix_data <= YELLOW;
else if((pix_x >= (H_VALID/10)*3) && (pix_x < (H_VALID/10)*4))
pix_data <= GREEN;
else if((pix_x >= (H_VALID/10)*4) && (pix_x < (H_VALID/10)*5))
pix_data <= CYAN;
else if((pix_x >= (H_VALID/10)*5) && (pix_x < (H_VALID/10)*6))
pix_data <= BLUE;
else if((pix_x >= (H_VALID/10)*6) && (pix_x < (H_VALID/10)*7))
pix_data <= PURPPLE;
else if((pix_x >= (H_VALID/10)*7) && (pix_x < (H_VALID/10)*8))
pix_data <= BLACK;
else if((pix_x >= (H_VALID/10)*8) && (pix_x < (H_VALID/10)*9))
pix_data <= WHITE;
else if((pix_x >= (H_VALID/10)*9) && (pix_x < H_VALID))
pix_data <= GRAY;
else
pix_data <= BLACK;
//rom_addr:读ROM地址
always@(posedge vga_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
rom_addr <= 14'd0;
else if(rom_addr == (PIC_SIZE - 1'b1))
rom_addr <= 14'd0;
else if(rd_en == 1'b1)
rom_addr <= rom_addr + 1'b1;
//-------------rom_pic_inst-------------
rom_pic rom_pic_inst
(
.address (rom_addr ), //输入读ROM地址,14bit
.clock (vga_clk ), //输入读时钟,vga_clk,频率25MHz,1bit
.rden (rd_en ), //输入读使能,1bit
.q (pic_data ) //输出读数据,16bit
);
endmodule
vga_rom_pic_jump
`timescale 1ns/1ns
module vga_rom_pic_jump
(
input wire sys_clk , //输入工作时钟,频率50MHz
input wire sys_rst_n , //输入复位信号,低电平有效
output wire hsync , //输出行同步信号
output wire vsync , //输出场同步信号
output wire [15:0] rgb //输出像素信息
);
//wire define
wire vga_clk ; //VGA工作时钟,频率25MHz
wire locked ; //PLL locked信号
wire rst_n ; //VGA模块复位信号
wire [9:0] pix_x ; //VGA有效显示区域X轴坐标
wire [9:0] pix_y ; //VGA有效显示区域Y轴坐标
wire [15:0] pix_data; //VGA像素点色彩信息
//rst_n:VGA模块复位信号
assign rst_n = (sys_rst_n & locked);
//------------- clk_gen_inst -------------
clk_gen clk_gen_inst(
.areset (~sys_rst_n ), //输入复位信号,高电平有效,1bit
.inclk0 (sys_clk ), //输入50MHz晶振时钟,1bit
.c0 (vga_clk ), //输出VGA工作时钟,频率25Mhz,1bit
.locked (locked ) //输出pll locked信号,1bit
);
//------------- vga_ctrl_inst -------------
vga_ctrl vga_ctrl_inst(
.vga_clk (vga_clk ), //输入工作时钟,频率25MHz,1bit
.sys_rst_n (rst_n ), //输入复位信号,低电平有效,1bit
.pix_data (pix_data ), //输入像素点色彩信息,16bit
.pix_x (pix_x ), //输出VGA有效显示区域像素点X轴坐标,10bit
.pix_y (pix_y ), //输出VGA有效显示区域像素点Y轴坐标,10bit
.hsync (hsync ), //输出行同步信号,1bit
.vsync (vsync ), //输出场同步信号,1bit
.rgb (rgb ) //输出像素点色彩信息,16bit
);
//------------- vga_pic_inst -------------
vga_pic vga_pic_inst(
.vga_clk (vga_clk ), //输入工作时钟,频率25MHz,1bit
.sys_rst_n (rst_n ), //输入复位信号,低电平有效,1bit
.pix_x (pix_x ), //输入VGA有效显示区域像素点X轴坐标,10bit
.pix_y (pix_y ), //输入VGA有效显示区域像素点Y轴坐标,10bit
.pix_data_out (pix_data ) //输出像素点色彩信息,16bit
);
endmodule
tb_vga_rom_pic_jump
`timescale 1ns/1ns
module tb_vga_rom_pic_jump();
//wire define
wire hsync ;
wire [15:0] rgb ;
wire vsync ;
//reg define
reg sys_clk ;
reg sys_rst_n ;
//sys_clk,sys_rst_n初始赋值
initial
begin
sys_clk = 1'b1;
sys_rst_n <= 1'b0;
#200
sys_rst_n <= 1'b1;
end
//sys_clk:产生时钟
always #10 sys_clk = ~sys_clk ;
//------------- vga_rom_pic_jump_inst -------------
vga_rom_pic_jump vga_rom_pic_jump_inst
(
.sys_clk (sys_clk ), //输入晶振时钟,频率50MHz,1bit
.sys_rst_n (sys_rst_n ), //输入复位信号,低电平有效,1bit
.hsync (hsync ), //输出行同步信号,1bit
.vsync (vsync ), //输出场同步信号,1bit
.rgb (rgb ) //输出RGB图像信息,16bit
);
endmodule
第42讲:基于RS232的VGA图像显示
系统框图绘制
vga_pic
`timescale 1ns/1ns
module vga_pic
(
input wire vga_clk , //输入工作时钟,频率25MHz
input wire sys_clk , //输入RAM写时钟,频率50MHz
input wire sys_rst_n , //输入复位信号,低电平有效
input wire [7:0] pi_data , //输入RAM写数据
input wire pi_flag , //输入RAM写使能
input wire [9:0] pix_x , //输入有效显示区域像素点X轴坐标
input wire [9:0] pix_y , //输入有效显示区域像素点Y轴坐标
output wire [7:0] pix_data_out //输出VGA显示图像数据
);
//parameter define
parameter H_VALID = 10'd640 , //行有效数据
V_VALID = 10'd480 ; //场有效数据
parameter H_PIC = 10'd100 , //图片长度
W_PIC = 10'd100 , //图片宽度
PIC_SIZE= 14'd10000 ; //图片像素个数
parameter RED = 8'b1110_0000, //红色
GREEN = 8'b0001_1100, //绿色
BLUE = 8'b0000_0011, //蓝色
BLACK = 8'b0000_0000, //黑色
WHITE = 8'b1111_1111; //白色
//wire define
wire rd_en ; //ROM读使能
wire [7:0] pic_data ; //自ROM读出的图片数据
//reg define
reg [13:0] wr_addr ; //ram写地址
reg [13:0] rd_addr ; //ram读地址
reg pic_valid ; //图片数据有效信号
reg [7:0] pix_data ; //背景色彩信息
//wr_addr:ram写地址
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
wr_addr <= 14'd0;
else if((wr_addr == (PIC_SIZE - 1'b1)) && (pi_flag == 1'b1))
wr_addr <= 14'd0;
else if(pi_flag == 1'b1)
wr_addr <= wr_addr + 1'b1;
//rd_addr:ram读地址
always@(posedge vga_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
rd_addr <= 14'd0;
else if(rd_addr == (PIC_SIZE - 1'b1))
rd_addr <= 14'd0;
else if(rd_en == 1'b1)
rd_addr <= rd_addr + 1'b1;
else
rd_addr <= rd_addr;
//rd_en:ROM读使能
assign rd_en = (((pix_x >= (((H_VALID - H_PIC)/2) - 1'b1))
&& (pix_x < (((H_VALID - H_PIC)/2) + H_PIC - 1'b1)))
&&((pix_y >= ((V_VALID - W_PIC)/2))
&& ((pix_y < (((V_VALID - W_PIC)/2) + W_PIC)))));
//pic_valid:图片数据有效信号
always@(posedge vga_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
pic_valid <= 1'b1;
else
pic_valid <= rd_en;
//pix_data_out:输出VGA显示图像数据
assign pix_data_out = (pic_valid == 1'b1) ? pic_data : pix_data;
//根据当前像素点坐标指定当前像素点颜色数据,在屏幕上显示彩条
always@(posedge vga_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
pix_data <= 8'd0;
else if((pix_x >= 0) && (pix_x < (H_VALID/10)*1))
pix_data <= RED;
else if((pix_x >= (H_VALID/10)*1) && (pix_x < (H_VALID/10)*2))
pix_data <= GREEN;
else if((pix_x >= (H_VALID/10)*2) && (pix_x < (H_VALID/10)*3))
pix_data <= BLUE;
else if((pix_x >= (H_VALID/10)*3) && (pix_x < (H_VALID/10)*4))
pix_data <= BLACK;
else if((pix_x >= (H_VALID/10)*4) && (pix_x < (H_VALID/10)*5))
pix_data <= WHITE;
else if((pix_x >= (H_VALID/10)*5) && (pix_x < (H_VALID/10)*6))
pix_data <= RED;
else if((pix_x >= (H_VALID/10)*6) && (pix_x < (H_VALID/10)*7))
pix_data <= GREEN;
else if((pix_x >= (H_VALID/10)*7) && (pix_x < (H_VALID/10)*8))
pix_data <= BLUE;
else if((pix_x >= (H_VALID/10)*8) && (pix_x < (H_VALID/10)*9))
pix_data <= BLACK;
else if((pix_x >= (H_VALID/10)*9) && (pix_x < H_VALID))
pix_data <= WHITE;
else
pix_data <= BLACK;
//-------------ram_pic_inst-------------
ram_pic ram_pic_inst
(
.inclock (sys_clk ), //输入RAM写时钟,50MHz,1bit
.wren (pi_flag ), //输入RAM写使能,1bit
.wraddress (wr_addr ), //输入RAM写地址,15bit
.data (pi_data ), //输入写入RAM的图片数据,8bit
.outclock (vga_clk ), //输入RAM读时钟,25MHz,1bit
.rdaddress (rd_addr ), //输入RAM读地址,15bit
.q (pic_data ) //输出读取RAM的图片数据,8bit
);
endmodule
vga_ctrl
`timescale 1ns/1ns
module vga_ctrl
(
input wire vga_clk , //输入工作时钟,频率25MHz
input wire sys_rst_n , //输入复位信号,低电平有效
input wire [7:0] pix_data , //输入像素点色彩信息
output wire [9:0] pix_x , //输出VGA有效显示区域像素点X轴坐标
output wire [9:0] pix_y , //输出VGA有效显示区域像素点Y轴坐标
output wire hsync , //输出行同步信号
output wire vsync , //输出场同步信号
output wire [7:0] rgb //输出像素点色彩信息
);
//parameter define
parameter H_SYNC = 10'd96 , //行同步
H_BACK = 10'd40 , //行时序后沿
H_LEFT = 10'd8 , //行时序左边框
H_VALID = 10'd640 , //行有效数据
H_RIGHT = 10'd8 , //行时序右边框
H_FRONT = 10'd8 , //行时序前沿
H_TOTAL = 10'd800 ; //行扫描周期
parameter V_SYNC = 10'd2 , //场同步
V_BACK = 10'd25 , //场时序后沿
V_TOP = 10'd8 , //场时序左边框
V_VALID = 10'd480 , //场有效数据
V_BOTTOM = 10'd8 , //场时序右边框
V_FRONT = 10'd2 , //场时序前沿
V_TOTAL = 10'd525 ; //场扫描周期
//wire define
wire rgb_valid ; //VGA有效显示区域
wire pix_data_req ; //像素点色彩信息请求信号
//reg define
reg [9:0] cnt_h ; //行同步信号计数器
reg [9:0] cnt_v ; //场同步信号计数器
//cnt_h:行同步信号计数器
always@(posedge vga_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_h <= 10'd0 ;
else if(cnt_h == H_TOTAL - 1'd1)
cnt_h <= 10'd0 ;
else
cnt_h <= cnt_h + 1'd1 ;
//hsync:行同步信号
assign hsync = (cnt_h <= H_SYNC - 1'd1) ? 1'b1 : 1'b0 ;
//cnt_v:场同步信号计数器
always@(posedge vga_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_v <= 10'd0 ;
else if((cnt_v == V_TOTAL - 1'd1) && (cnt_h == H_TOTAL-1'd1))
cnt_v <= 10'd0 ;
else if(cnt_h == H_TOTAL - 1'd1)
cnt_v <= cnt_v + 1'd1 ;
else
cnt_v <= cnt_v ;
//vsync:场同步信号
assign vsync = (cnt_v <= V_SYNC - 1'd1) ? 1'b1 : 1'b0 ;
//rgb_valid:VGA有效显示区域
assign rgb_valid = (((cnt_h >= H_SYNC + H_BACK + H_LEFT)
&& (cnt_h < H_SYNC + H_BACK + H_LEFT + H_VALID))
&&((cnt_v >= V_SYNC + V_BACK + V_TOP)
&& (cnt_v < V_SYNC + V_BACK + V_TOP + V_VALID)))
? 1'b1 : 1'b0;
//pix_data_req:像素点色彩信息请求信号,超前rgb_valid信号一个时钟周期
assign pix_data_req = (((cnt_h >= H_SYNC + H_BACK + H_LEFT - 1'b1)
&& (cnt_h < H_SYNC + H_BACK + H_LEFT + H_VALID - 1'b1))
&&((cnt_v >= V_SYNC + V_BACK + V_TOP)
&& (cnt_v < V_SYNC + V_BACK + V_TOP + V_VALID)))
? 1'b1 : 1'b0;
//pix_x,pix_y:VGA有效显示区域像素点坐标
assign pix_x = (pix_data_req == 1'b1)
? (cnt_h - (H_SYNC + H_BACK + H_LEFT - 1'b1)) : 10'h3ff;
assign pix_y = (pix_data_req == 1'b1)
? (cnt_v - (V_SYNC + V_BACK + V_TOP)) : 10'h3ff;
//rgb:输出像素点色彩信息
assign rgb = (rgb_valid == 1'b1) ? pix_data : 8'b0 ;
endmodule
uart_rx
`timescale 1ns/1ns
module uart_rx
#(
parameter UART_BPS = 'd9600, //串口波特率
parameter CLK_FREQ = 'd50_000_000 //时钟频率
)
(
input wire sys_clk , //系统时钟50MHz
input wire sys_rst_n , //全局复位
input wire rx , //串口接收数据
output reg [7:0] po_data , //串转并后的8bit数据
output reg po_flag //串转并后的数据有效标志信号
);
//localparam define
localparam BAUD_CNT_MAX = CLK_FREQ/UART_BPS ;
//reg define
reg rx_reg1 ;
reg rx_reg2 ;
reg rx_reg3 ;
reg start_nedge ;
reg work_en ;
reg [12:0] baud_cnt ;
reg bit_flag ;
reg [3:0] bit_cnt ;
reg [7:0] rx_data ;
reg rx_flag ;
//插入两级寄存器进行数据同步,用来消除亚稳态
//rx_reg1:第一级寄存器,寄存器空闲状态复位为1
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
rx_reg1 <= 1'b1;
else
rx_reg1 <= rx;
//rx_reg2:第二级寄存器,寄存器空闲状态复位为1
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
rx_reg2 <= 1'b1;
else
rx_reg2 <= rx_reg1;
//rx_reg3:第三级寄存器和第二级寄存器共同构成下降沿检测
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
rx_reg3 <= 1'b1;
else
rx_reg3 <= rx_reg2;
//start_nedge:检测到下降沿时start_nedge产生一个时钟的高电平
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
start_nedge <= 1'b0;
else if((~rx_reg2) && (rx_reg3))
start_nedge <= 1'b1;
else
start_nedge <= 1'b0;
//work_en:接收数据工作使能信号
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
work_en <= 1'b0;
else if(start_nedge == 1'b1)
work_en <= 1'b1;
else if((bit_cnt == 4'd8) && (bit_flag == 1'b1))
work_en <= 1'b0;
//baud_cnt:波特率计数器计数,从0计数到BAUD_CNT_MAX - 1
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
baud_cnt <= 13'b0;
else if((baud_cnt == BAUD_CNT_MAX - 1) || (work_en == 1'b0))
baud_cnt <= 13'b0;
else if(work_en == 1'b1)
baud_cnt <= baud_cnt + 1'b1;
//bit_flag:当baud_cnt计数器计数到中间数时采样的数据最稳定,
//此时拉高一个标志信号表示数据可以被取走
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
bit_flag <= 1'b0;
else if(baud_cnt == BAUD_CNT_MAX/2 - 1)
bit_flag <= 1'b1;
else
bit_flag <= 1'b0;
//bit_cnt:有效数据个数计数器,当8个有效数据(不含起始位和停止位)
//都接收完成后计数器清零
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
bit_cnt <= 4'b0;
else if((bit_cnt == 4'd8) && (bit_flag == 1'b1))
bit_cnt <= 4'b0;
else if(bit_flag ==1'b1)
bit_cnt <= bit_cnt + 1'b1;
//rx_data:输入数据进行移位
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
rx_data <= 8'b0;
else if((bit_cnt >= 4'd1)&&(bit_cnt <= 4'd8)&&(bit_flag == 1'b1))
rx_data <= {
rx_reg3, rx_data[7:1]};
//rx_flag:输入数据移位完成时rx_flag拉高一个时钟的高电平
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
rx_flag <= 1'b0;
else if((bit_cnt == 4'd8) && (bit_flag == 1'b1))
rx_flag <= 1'b1;
else
rx_flag <= 1'b0;
//po_data:输出完整的8位有效数据
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
po_data <= 8'b0;
else if(rx_flag == 1'b1)
po_data <= rx_data;
//po_flag:输出数据有效标志(比rx_flag延后一个时钟周期,为了和po_data同步)
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
po_flag <= 1'b0;
else
po_flag <= rx_flag;
endmodule
vga_uart_pic
`timescale 1ns/1ns
module vga_uart_pic
(
input wire sys_clk , //输入工作时钟,频率50MHz
input wire sys_rst_n , //输入复位信号,低电平有效
input wire rx , //输入串口的图片数据
output wire hsync , //输出行同步信号
output wire vsync , //输出场同步信号
output wire [7:0] rgb //输出像素信息
);
//parameter define
parameter UART_BPS = 14'd9600 , //比特率
CLK_FREQ = 26'd50_000_000 ; //时钟频率
//wire define
wire po_flag ; //串口拼接好的图片数据
wire [7:0] po_data ; //数据标志信号
wire vga_clk ; //VGA工作时钟
wire clk_50m ; //串口工作时钟
wire locked ; //PLL locked信号
wire rst_n ; //VGA模块复位信号
wire [9:0] pix_x ; //VGA有效显示区域X轴坐标
wire [9:0] pix_y ; //VGA有效显示区域Y轴坐标
wire [7:0] pix_data ; //VGA像素点色彩信息
//rst_n:VGA模块复位信号
assign rst_n = (sys_rst_n & locked);
//------------- clk_gen_inst -------------
clk_gen clk_gen_inst
(
.areset (~sys_rst_n ), //输入复位信号,高电平有效,1bit
.inclk0 (sys_clk ), //输入50MHz晶振时钟,1bit
.c0 (vga_clk ), //输出VGA工作时钟,频率25Mhz,1bit
.c1 (clk_50m ), //输出串口工作时钟,频率50Mhz,1bit
.locked (locked ) //输出pll locked信号,1bit
);
//-------------uart_rx_inst-------------
uart_rx
#(
.UART_BPS (UART_BPS), //串口波特率
.CLK_FREQ (CLK_FREQ) //时钟频率
)
uart_rx_inst
(
.sys_clk (clk_50m ), //输入工作时钟,频率50MHz,1bit
.sys_rst_n (rst_n ), //输入复位信号,低电平有效,1bit
.rx (rx ), //输入串口的图片数据,1bit
.po_data (po_data ), //输出拼接好的图片数据
.po_flag (po_flag ) //输出数据标志信号
);
//------------- vga_ctrl_inst -------------
vga_ctrl vga_ctrl_inst
(
.vga_clk (vga_clk ), //输入工作时钟,频率25MHz,1bit
.sys_rst_n (rst_n ), //输入复位信号,低电平有效,1bit
.pix_data (pix_data ), //输入像素点色彩信息,8bit
.pix_x (pix_x ), //输出VGA有效显示区域像素点X轴坐标,10bit
.pix_y (pix_y ), //输出VGA有效显示区域像素点Y轴坐标,10bit
.hsync (hsync ), //输出行同步信号,1bit
.vsync (vsync ), //输出场同步信号,1bit
.rgb (rgb ) //输出像素点色彩信息,16bit
);
//------------- vga_pic_inst -------------
vga_pic vga_pic_inst
(
.vga_clk (vga_clk ), //输入工作时钟,频率25MHz,1bit
.sys_clk (clk_50m ), //输入RAM写时钟,1bit
.sys_rst_n (rst_n ), //输入复位信号,低电平有效,1bit
.pi_flag (po_flag ), //输入RAM写使能,1bit
.pi_data (po_data ), //输入RAM写数据,8bit
.pix_x (pix_x ), //输入VGA有效显示区域像素点X轴坐标,10bit
.pix_y (pix_y ), //输入VGA有效显示区域像素点Y轴坐标,10bit
.pix_data_out (pix_data ) //输出像素点色彩信息,8bit
);
endmodule
tb_vga_uart_pic
`timescale 1ns/1ns
module tb_vga_uart_pic();
//wire define
wire hsync ;
wire vsync ;
wire [7:0] rgb ;
//reg define
reg sys_clk ;
reg sys_rst_n ;
reg rx ;
reg [7:0] data_mem [9999:0] ; //data_mem是一个存储器,相当于一个ram
//读取sim文件夹下面的data.txt文件,并把读出的数据定义为data_mem
initial
$readmemh("F:/GitLib/Altera/EP4CE10F17C8/ZT_Pro/A/4_base_code/30_vga_uart_pic/matlab/data_test.txt",data_mem);
//时钟、复位信号
initial
begin
sys_clk = 1'b1 ;
sys_rst_n <= 1'b0 ;
#200
sys_rst_n <= 1'b1 ;
end
always #10 sys_clk = ~sys_clk;
//rx
initial
begin
rx <= 1'b1;
#200
rx_byte();
end
//rx_byte
task rx_byte();
integer j;
for(j=0;j<10000;j=j+1)
rx_bit(data_mem[j]);
endtask
//rx_bit
task rx_bit(input[7:0] data); //data是data_mem[j]的值。
integer i;
for(i=0;i<10;i=i+1)
begin
case(i)
0: rx <= 1'b0 ; //起始位
1: rx <= data[0];
2: rx <= data[1];
3: rx <= data[2];
4: rx <= data[3];
5: rx <= data[4];
6: rx <= data[5];
7: rx <= data[6];
8: rx <= data[7]; //上面8个发送的是数据位
9: rx <= 1'b1 ; //停止位
endcase
#1040; //一个波特时间=sys_clk周期*波特计数器
end
endtask
//重定义defparam,用于修改参数,缩短仿真时间
defparam vga_uart_pic_inst.uart_rx_inst.BAUD_CNT_END = 52;
defparam vga_uart_pic_inst.uart_rx_inst.BAUD_CNT_END_HALF = 26;
//------------- vga_uart_pic_jump -------------
vga_uart_pic vga_uart_pic_inst
(
.sys_clk (sys_clk ), //输入工作时钟,频率50MHz,1bit
.sys_rst_n (sys_rst_n ), //输入复位信号,低电平有效,1bit
.rx (rx ), //输入串口的图片数据,1bit
.hsync (hsync ), //输出行同步信号,1bit
.vsync (vsync ), //输出场同步信号,1bit
.rgb (rgb ) //输出像素信息,8bit
);
endmodule