VGA显示&游戏状态控制模块 game_show
1. VGA时序分析
VGA 时序的关键信号是行同步信号(HSync)和场同步信号(VSync),共有 6 个参数,分别为
Sync
Back Porch
Left Border(针对行同步信号)/Top Border(针对场同步信号)
Addr Time(数据有效时间,分辨率就是看这个参数)
Right Border(针对行同步信号)/Bottom Border(针对场同步信号)
Front Porch
VGA 真正显示的区域是 VSync 的 Addr Time 和 HSync 的 Addr Time 同时有效区域(低有效)
单帧时序图
2.VGA参数设定
选用 640*480@60Hz 分辨率, 像素时钟频率为 25MHz
`define VGA_640_480_60Hz
//=====================================================================\
// ********** Define Parameter and Internal Signals *************
//=====================================================================/
`ifdef VGA_640_480_60Hz
parameter Hor_Total_Time = 800 ;
parameter Hor_Addr_Time = 640 ;
parameter Hor_Sync_Time = 96 ;
parameter Hor_Back_Porch = 40 ;
parameter Hor_Left_Border = 8 ;
parameter Hor_Start = Hor_Sync_Time + Hor_Back_Porch + Hor_Left_Border;
parameter Hor_End = Hor_Start + Hor_Addr_Time ;
parameter Ver_Total_Time = 525 ;
parameter Ver_Addr_Time = 480 ;
parameter Ver_Sync_Time = 2 ;
parameter Ver_Back_Porch = 25 ;
parameter Ver_Top_Border = 8 ;
parameter Ver_Start = Ver_Sync_Time + Ver_Back_Porch + Ver_Top_Border;
parameter Ver_End = Ver_Start + Ver_Addr_Time ;
`endif
parameter Red_Wall = 16 ;//边界墙壁厚度
parameter NONE = 2'd0 ;
parameter HEAD = 2'd1 ;
parameter BODY = 2'd2
3. Main code
(1)h_cnt&v_cnt行列计数
//h_cnt
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
h_cnt <= 0;
end
else if(add_h_cnt)begin
if(end_h_cnt)
h_cnt <= 0;
else
h_cnt <= h_cnt + 1;
end
end
assign add_h_cnt = 1'b1;
assign end_h_cnt = add_h_cnt && h_cnt== Hor_Total_Time-1;
//v_cnt
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
v_cnt <= 0;
end
else if(add_v_cnt)begin
if(end_v_cnt)
v_cnt <= 0;
else
v_cnt <= v_cnt + 1;
end
end
assign add_v_cnt = end_h_cnt;
assign end_v_cnt = add_v_cnt && v_cnt== Ver_Total_Time-1;
(2)vga_hs&vga_vs信号产生
//vga_hs
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
vga_hs <= 1'b0;
end
else if(add_h_cnt && h_cnt == Hor_Sync_Time-1)begin
vga_hs <= 1'b0;
end
else if(end_h_cnt)begin
vga_hs <= 1'b1;
end
end
//vga_vs
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
vga_vs <= 1'b0;
end
else if(add_v_cnt && v_cnt == Ver_Sync_Time-1)begin
vga_vs <= 1'b0;
end
else if(end_v_cnt)begin
vga_vs <= 1'b1;
end
end
(3)vga显示有效区域设定
//vga显示有效区域,由于vga_r/vga_g/vga_b获取数据需要消耗一拍,所以这里要提前一拍
//v_cnt是多周期的,所以不用提前
assign display_area = (h_cnt >= Hor_Start - 1 && h_cnt < Hor_End - 1 && v_cnt >= Ver_Start && v_cnt < Ver_End);
//定义墙壁区域
assign wall_area = ((h_cnt >= Hor_Start - 1 && h_cnt < Hor_Start - 1 + Red_Wall) || (h_cnt >= Hor_End - 1 - Red_Wall && h_cnt < Hor_End - 1)) || ((v_cnt >= Ver_Start && v_cnt < Ver_Start + Red_Wall) || (v_cnt >= Ver_End - Red_Wall && v_cnt < Ver_End));
(4)有效区内像素点的坐标扫描
//pixel_x,pixel_y vga扫描坐标
always @(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)begin
pixel_x <= 10'd0;
pixel_y <= 10'd0;
end
else if(display_area)begin
pixel_x <= h_cnt - Hor_Start + 2;//这里提前了两个时钟,因为前后判断各耽搁一个时钟,具体自己画一下时序图
pixel_y <= v_cnt - Ver_Start;//因为v_cnt数据是多个时钟保持的,持续时间长,所以不会耽搁,故不能加2
end
else begin
pixel_x <= 10'd0;
pixel_y <= 10'd0;
end
end
(5)子模块play_vga的像素点数据输出显示(RGB565)
//vga_r,vga_g,vga_b
always @(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)begin
vga_r <= 5'd0;
vga_g <= 6'd0;
vga_b <= 5'd0;
end
else if(wall_area)begin//扫描到墙
vga_r <= 5'h1f;
vga_g <= 6'h00;
vga_b <= 5'h00;
end
else if(pixel_x[9:4] == apple_x && pixel_y[9:4] == apple_y)begin//扫描到苹果
vga_r <= 5'h1f;
vga_g <= 6'h3f;
vga_b <= 5'h00;
end
else if(object == HEAD)begin//扫描到蛇头
vga_r <= 5'h00;
vga_g <= 6'h3f;
vga_b <= 5'h00;
end
else if(object == BODY)begin//扫描到蛇身体
case({pixel_x[3:0],pixel_y[3:0]})//蛇身四个角做特效处理,
8'h00,8'h0f,8'hf0,8'hff:begin
vga_r <= 5'h00;
vga_g <= 6'h00;
vga_b <= 5'h00;
end
default:begin
vga_r <= 5'h00;
vga_g <= 6'h3f;
vga_b <= 5'h1f;
end
endcase
end//其他显示区域为黑色
else begin
vga_r <= 5'd0;
vga_g <= 6'd0;
vga_b <= 5'd0;
end
end