最近会复习之前的知识,用正点原子的开拓者开发板做一些以前实验的复现,目的是为了
锻练在只提供目的和条件的情况下,独立设计编写模块的能力,及找出复现过程中出现的问题,最后做一个备忘日记的作用。
本次实验目的:VGA接口在显示器上显示彩条,要求分辨率为640*480, 刷新速率为60hz。
实验介绍:
VGA的全称是Video Graphics Array, 即视频图形阵列, 是一个使用模拟信号进行视频传输的标准。
所用到的引脚:
VGA时序:
不同分辨率的VGA时序参数 :
实验设计:由一个pll模块,VGA驱动模块,VGA显示模块,及顶层模块一共是四个模块。
pll:提供25MHZ时钟;
vga_dri:通过计数器得出有效数据及行场同步信号的时钟周期,输出从显示模块传来的数据中的有效数据,并输出行场同步信号;
vga_disp:按像素地址输出设置好的RGB图像数据.
顶层模块:
//VGA彩条显示
//端口定义
module vga_colorbar(
sys_clk,
sys_rst,
vga_hs,
vga_vs,
vga_rgb
);
//I/O说明
input sys_clk;
input sys_rst;
output vga_hs;
output vga_vs;
output [15:0] vga_rgb;
//内部信号定义
wire clk_w;
wire locked_w;
wire [15:0] pixel_data_w;
wire [9:0] pixel_hpos_w;
wire [9:0] pixel_vpos_w;
wire sys_rst_n;
assign sys_rst_n = sys_rst && locked_w;
//例化pll
vga_pll u_vga_pll(
.areset (~sys_rst),//对复位信号取反
.inclk0 (sys_clk),
.c0 (clk_w),
.locked (locked_w)
);
//vga驱动模块
vga_driver u_vga_driver(
//pll与系统时钟复位输入信号
.clk_dri (clk_w),
.rst_dri (sys_rst_n),
//驱动模块输出信号
.vga_hs (vga_hs),
.vga_vs (vga_vs),
.vga_rgb (vga_rgb),
//显示模块输入像素坐标数据信号
.pixel_hpos (pixel_hpos_w),
.pixel_vpos (pixel_vpos_w),
.pixel_data (pixel_data_w)
);
//vga显示模块
vga_disp u_vga_disp(
.clk_disp (clk_w),
.rst_disp (sys_rst_n),
.pixel_hpos (pixel_hpos_w),
.pixel_vpos (pixel_vpos_w),
.pixel_data (pixel_data_w)
);
endmodule
驱动模块:
//vga驱动模块
module vga_driver(
clk_dri,
rst_dri,
pixel_hpos,
pixel_vpos,
pixel_data,
vga_hs,
vga_vs,
vga_rgb
);
//I/O定义
input clk_dri;
input rst_dri;
output [9:0] pixel_hpos;//一行像素点800
output [9:0] pixel_vpos;//一帧525行
input [15:0] pixel_data;//RGB565
output vga_hs;
output vga_vs;
output [15:0] vga_rgb;
//内部参量定义,vga时序参数
parameter H_SYNC = 10'd96;
parameter H_BACK = 10'd48;
parameter H_DISP = 10'd640;
parameter H_FRONT = 10'd16;
parameter H_PRIOD = 10'd800;
parameter V_SYNC = 10'd2;
parameter V_BACK = 10'd33;
parameter V_DISP = 10'd480;
parameter V_FRONT = 10'd10;
parameter V_PRIOD = 10'd525;
//使能信号(用于区分有效数据),请求信号(用于定位像素点坐标)
wire vga_en;
wire data_req;
//行场同步信号计数器
reg [9:0] cnt_h;
reg [9:0] cnt_v;
//VGA行场同步信号
assign vga_hs = (cnt_h <= H_SYNC - 1'b1) ? 1'b0 : 1'b1;
assign vga_vs = (cnt_v <= V_SYNC - 1'b1) ? 1'b0 : 1'b1;
//选定有效数据区域
assign vga_en = (((cnt_h >= H_SYNC + H_BACK ) && (cnt_h < H_SYNC + H_BACK + H_DISP )) &&
((cnt_v >= V_SYNC + V_BACK ) && (cnt_v < V_SYNC + V_BACK + V_DISP))) ? 1'b1 : 1'b0;
assign vga_rgb = vga_en ? pixel_data:16'd0;
//请求信号比行有效数据先来一个周期,确定有效数据即将到来和即将结束
assign data_req = (((cnt_h >= H_SYNC + H_BACK - 1'b1) && (cnt_h < H_SYNC + H_BACK + H_DISP - 1'b1)) &&
((cnt_v>= V_SYNC + V_BACK) && (cnt_v < V_SYNC + V_BACK + V_DISP))) ? 1'b1 : 1'b0;
//确定像素当前坐标
assign pixel_hpos = data_req ? (cnt_h -(H_SYNC + H_BACK - 1'b1)) : 10'd0;
assign pixel_vpos = data_req ? (cnt_h -(V_SYNC + V_BACK - 1'b1)) : 10'd0;
always @(posedge clk_dri or negedge rst_dri)begin
if(!rst_dri)begin
cnt_h <= 10'd0;
end
else begin
if(cnt_h < H_PRIOD -1'b1)begin
cnt_h <= cnt_h + 1'd1;
end
else begin
cnt_h <= 10'd0;
end
end
end
always @(posedge clk_dri or negedge rst_dri)begin
if(!rst_dri)begin
cnt_v <= 10'd0;
end
else if(cnt_h == H_PRIOD - 1'b1)begin
if(cnt_v < V_PRIOD - 1'b1)
cnt_v <= cnt_v + 1'd1;
else
cnt_v <= 10'd0;
end
end
endmodule
显示模块:
module vga_disp(
clk_disp,
rst_disp,
pixel_hpos,
pixel_vpos,
pixel_data
);
input clk_disp;
input rst_disp;
input [9:0] pixel_hpos;
input [9:0] pixel_vpos;
output reg [15:0] pixel_data;
parameter H_DISP =10'd640;
parameter V_DISP =10'd480;
localparam WHITE = 16'b11111_111111_11111;
localparam BLACK = 16'b00000_000000_00000;
localparam RED = 16'b11111_000000_00000;
localparam GREEN = 16'b00000_111111_00000;
localparam BLUE = 16'b00000_000000_11111;
always @(posedge clk_disp or negedge rst_disp)begin
if(!rst_disp)
pixel_data <= 16'd0;
else begin
if (pixel_hpos >= 0 && pixel_hpos <= (H_DISP / 5) * 1)
pixel_data <= WHITE;
else if (pixel_hpos >= (H_DISP / 5) * 1 && pixel_hpos < (H_DISP / 5) * 2)
pixel_data <= BLACK;
else if (pixel_hpos >= (H_DISP / 5) * 2 && pixel_hpos < (H_DISP / 5) * 3)
pixel_data <= RED;
else if (pixel_hpos >=(H_DISP / 5) * 3 && pixel_hpos < (H_DISP / 5) * 4)
pixel_data <= GREEN;
else
pixel_data <= BLUE;
end
end
endmodule
时序:
问题总结:
(1)编写的时候pll的默认时钟是100MHZ,我忘记改成50MHZ的外部晶振时钟,排查了好久才想到;
(2)像素坐标到来前要使用数据请求信号来区分有效数据的真正坐标位置,当计数器没在有效信号区域时,像素坐标置零,我一开始没有用数据请求信号,如下:
assign pixel_hpos =cnt_h -(H_SYNC + H_BACK - 1'b1);
当计数器在没有有效的区域内,像素时钟为不定值,造成混乱;
(3)使用gvim并没有想象中的好用,可能还没用熟练。
VGA显示实验总结:
重点在于驱动模块有效数据区域的选择上,围绕有效数据的位置可以定位像素地址等,若有不同的显示需求,根据不同的分辨率要求更改相应参数即可。