FPGA VGA显示协议
协议原理简述
协议包含的信号:HS行同步信号,VS场同步信号,RGB三种颜色信号一共5种信号。
RGB分为RGB888,RGB565和RGB332三种不同长度的颜色信号,RGB888表示红绿蓝三个信号各占8个比特,RGB则红绿蓝分别占5,6,5个比特。
VGA协议扫描屏幕的顺序是从左到右,从上到下,从左上到右下。
根据VGA协议的定义,VGA的行同步信号HSYNC,在每一行开始时产生一个高电平脉冲,场同步信号在每一帧开始时产生一个高电平脉冲。
行同步(横轴),场同步(纵轴),以及它们会产生的脉冲信号:
VGA协议各种信号的参数来源,根据数据手册(640x480,60HZ和75HZ):
程序实现
时钟分频模块
640x480像素的VGA协议所需时钟频率25MHZ,使用clk IP核进行时钟分频
代码语言:javascript复制//端口定义
module vga_colorbar_top(
input wire sys_clk ,
input wire sys_rst,
output wire vga_hs, // 输出到vga接口的行同步信号
output wire vga_vs, // 输出到vga接口的场同步信号
output wire [15:0] vga_rgb // 输出到vga接口的像素数据
);
// 25MHZ时钟信号定义
wire clk_w ;
pll_clk pll_25MHz(
.inclk0 (sys_clk), // (输入)50MHZ基准时钟
.c3 (clk_w) // (输出)25MHZ分频时钟
);
VGA驱动模块
计数信号:
pixel_hpos用来计算当前刷新到了哪一行,pixel_vpos用来计算当前刷新到了哪一列。当刷新到640列时vga_vs产生脉冲1,当刷新倒480行时vga_hs产生脉冲1。
图像数据(加载外部图像数据):pixel_data[15:0],其中[4:0]是蓝色,[10:5]是绿色,[15:11]是红色
vga_rgb[15:0]表示输出RGB颜色信号,接入到显示器的信号
代码语言:javascript复制// 对变量的声明
module vga_driver(
input wire clk_25MHz, //时钟,25MHZ
input wire rst, // 复位信号,低电平有效
input wire [15:0] pixel_data, //RGB--565,即pixel_data[15:11]控制R、pixel_data[10:5]控制G、pixel_data[4:0]控制B
output wire [ 9:0] pixel_hpos, //pixel_data的行坐标
output wire [ 9:0] pixel_vpos, //pixel_data的列坐标
output wire vga_hs, //行同步信号
output wire vga_vs, //列同步信号
output wire [15:0] vga_rgb //输出到VGA接口的颜色数据
);
//内部参量定义,vga时序参数60HZ
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; // 列周期总长度
//内部参量定义,vga时序参数75HZ
//parameter H_SYNC = 10'd64; // 同步期
//parameter H_BACK = 10'd120; // 显示后沿
//parameter H_DISP = 10'd640; // 显示区域
//parameter H_FRONT = 10'd16; // 显示前沿
//parameter H_PRIOD = 10'd840; // 行周期总长度
//
//parameter V_SYNC = 10'd3; // 同步期
//parameter V_BACK = 10'd16; // 显示后沿
//parameter V_DISP = 10'd480; // 显示区域
//parameter V_FRONT = 10'd1; // 显示前沿
//parameter V_PRIOD = 10'd500; // 列周期总长度
// 确定像素当前坐标
assign pixel_hpos = pixel_data_require ? (cnt_h - (H_SYNC H_BACK - 1'b1)) : 10'd0;
assign pixel_vpos = pixel_data_require ? (cnt_v - (V_SYNC V_BACK - 1'b1)) : 10'd0;
// 确定像素数据
assign vga_rgb = vga_en ? pixel_data:16'd0;
输出颜色竖条
代码语言:javascript复制// 判断当前坐标的像素数据
always @ (posedge clk_25MHz or negedge rst)begin
if(!rst)begin
// pixel_data <= 16'd0;
pixel_data <= BLACK;
end
else begin
if(pic_area == 1'b0)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
end
写入图片,显示姓名
利用RAM存储图片数据,读取成VGA像素数据,并显示到显示器上
第一步,利用matlab或者软件将图片转化为mif文件
然后利用RAM IP核加载mif图片:
RAM IP核需要根据图片大小配置参数,RAM IP核具体参数配置如下:https://blog.csdn.net/qq_37899920/article/details/123594833
实例化RAM核,并且将图片显示出来
代码语言:javascript复制// 例化RAM
reg [13:0]wraddr;
reg [12:0]rdaddr;
reg rden;
reg wren;
reg [7:0]wrdata;
wire [15:0]rddata;
RAM RAM1(
.data(wrdata),
.rdaddress(rdaddr),
.rdclock(clk_25MHz),
.rden(rden),
.wraddress(wraddr),
.wrclock(clk_25MHz),
.wren(wren),
.q(rddata)
);
// 确定RAM读使能信号
always@(posedge clk_25MHz or negedge rst)begin
if(!rst)begin
rden <= 1'b0;
end
else begin
rden <= 1'b1;
end
end
// 确定RAM读地址信号
always@(posedge clk_25MHz or negedge rst)begin
if(!rst)begin
rdaddr <= 13'd0;
end
else begin
if(pic_area == 1'b1)begin
rdaddr <=(pixel_hpos - PIC_HPOS) ((pixel_vpos == 10'd0)?16'd0:((pixel_vpos - 1'b1)* PIC_WIDTH));
end
end
end
// 确定RAM写端口的各信号
always@(*)begin
wraddr <= 14'd0;
wren <= 1'b0;
wrdata <= 8'd0;
end
// 判断当前坐标的像素数据
always @ (posedge clk_25MHz or negedge rst)begin
if(!rst)begin
pixel_data <= BLACK;
end
else begin
if(pic_area == 1'b0)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
else begin
pixel_data <= rddata; // 显示图片
end
end
end
若加入延时信号
行同步信号控制电子束从右侧返回起点,而场同步信号控制电子束从底部返回起点。
在两个信号的回扫时间内,不传输图像,此时显示器不显示图像。
在640 * 480,25MHZ显示周期中,每一行扫描所需周期约为800 * 1/(25 * 10^6)s = 32us,每一帧所需扫描周期约为800 * 525 * 1 /(25 * 10^6)s = 16.8ms。
行消隐信号
每次行或列扫描完成后,是同步信号的回扫时间,分别在行列方向产生一条移动的同步带,这条带内,显示器不显示图像(全黑)。
为了让同步带消失,要让它叠加在消隐信号之上。
当不同步时,行同步带出现在下一行的开始,场同步带出现在下一帧的开始。行同步带从右移动到左边,表示行同步信号回扫完毕,列同步带从下边移动到上边,表示场同步信号回扫完毕。
延时设计
设计行延时1.2us,场延时1ms,各占行场周期的一部分。
通过节拍计数器延时:
代码语言:javascript复制// VGA行场同步信号计数器
reg [9:0] cnt_h;
reg [9:0] cnt_v;
reg [9:0] cnt_h2; // 用于延时
reg [9:0] cnt_v2; // 用于延时
// 节拍器,手动延时
reg [14:0] jiepai = 0;
reg [14:0] jiepai2 = 0;
reg startyanshi = 0;
reg startyanshi2 = 0;
// 行列计数
always @ (posedge clk_25MHz or negedge rst) begin
if (!rst) begin
cnt_h <= 10'd0;
cnt_h2 <= 10'd0;
jiepai <= 10'd0;
startyanshi <= 1;
end
else begin
if(startyanshi==1) begin
if(jiepai==1) begin // 前半段延时30*(1/(25*10^6)) = 1.2*10^-6s = 1.2us
jiepai <= 0;
startyanshi <= 0; // 一次延时结束(一行的开头的延时结束)
// 节拍器和cnt_h2计数之间会有一个错误的上升沿的延时,消去它
if (cnt_h2 <= H_PRIOD - 1'b1) begin
cnt_h2 <= cnt_h2 1'b1;
end
else begin
cnt_h2 <= 10'd0;
end
end
else jiepai <= jiepai 1;
end
else begin // 后半段
if (cnt_h2 <= H_PRIOD - 1'b1) begin
cnt_h2 <= cnt_h2 1'b1;
end
else begin
cnt_h2 <= 10'd0;
jiepai <= 10'd0;
startyanshi <= 1; // 每行开头(结束)重置延时
end
end
if (cnt_h <= H_PRIOD - 1'b1) begin
cnt_h <= cnt_h 1'b1;
end
else begin
cnt_h <= 10'd0;
end
end
end
always @ (posedge clk_25MHz or negedge rst) begin
if(!rst)begin
cnt_v <= 10'd0;
cnt_v2 <= 10'd0;
jiepai2 <= 10'd0;
startyanshi2 <= 1;
end
else begin
if(startyanshi2==1) begin
if(jiepai2==2500) begin // 前半段延时25000*(1/(25*10^6)) = 1*10^-3s = 1ms
jiepai2 <= 0;
startyanshi2 <= 0; // 一次延时结束(一行的开头的延时结束)
end
else jiepai2 <= jiepai2 1;
end
else begin
if (cnt_h == H_PRIOD - 1'b1) begin
if(cnt_v2 <= V_PRIOD - 1'b1)
cnt_v2 <= cnt_v2 1'b1;
else begin
cnt_v2 <= 10'd0;
jiepai2 <= 10'd0;
startyanshi2 <= 1; // 每行开头(结束)重置延时
end
end
end
if (cnt_h == H_PRIOD - 1'b1) begin
if(cnt_v <= V_PRIOD - 1'b1)
cnt_v <= cnt_v 1'b1;
else begin
cnt_v <= 10'd0;
end
end
end
end
显示效果:
给场同步信号添加延时1ms:
给场同步信号添加延时1ms,行同步信号添加延时1us:
参考
[1] 咸鱼FPGA.VGA协议 cnblogs
[2] Kevin.VGA显示器驱动
[3] 星河带悦流.FPGA实现VGA接口——保存图片至ROM/RAM显示 csdn