一,什么是VGA
VGA(Video Graphics Array)视频图形阵列是IBM于1987年提出的一个使用模拟信号的电脑显示标准。VGA接口即电脑采用VGA标准输出数据的专用接口。VGA接口共有15针,分成3排,每排5个孔,显卡上应用最为广泛的接口类型,绝大多数显卡都带有此种接口。它传输红、绿、蓝模拟信号以及同步信号(水平和垂直信号)。
DE15母接座(显卡端) | |||
引脚1 | RED | 红色视频 | |
---|---|---|---|
引脚2 | GREEN | 绿色视频 | |
引脚3 | BLUE | 蓝色视频 | |
引脚4 | ID2/RES | 过去为屏幕ID比特2;自DDC2起保留 | |
引脚5 | GND | 接地(水平同步) | |
引脚6 | RED_RTN | 红色接回 | |
引脚7 | GREEN_RTN | 绿色接回 | |
引脚8 | BLUE_RTN | 蓝色接回 | |
引脚9 | KEY/PWR | 过去为key;现为 +5V DC | |
引脚10 | GND | 接地(垂直同步,DDC) | |
引脚11 | ID0/RES | 过去为屏幕ID比特0;自E-DDC起保留 | |
引脚12 | ID1/SDA | 过去为屏幕ID比特1;自DDC2起为I²C数据 | |
引脚13 | HSync | 水平同步 | |
引脚14 | VSync | 垂直同步 | |
引脚15 | ID3/SCL | 过去为屏幕ID比特3;自DDC2起为I²C时钟 | |
图表中详叙了较新的15针VESA DDC2连接头。图标中的针脚编号是显卡常见的母接头;在公接头上的针脚相当于图例的左右镜像。 VGA的显示效果取决于RGB三个分量的位数,最高24位(即RGB各8位),16位,12位也都存在。 |
具有分辨率高、显示速率快、颜色丰富等优点。
VGA接口不但是CRT显示设备的标准接口,同样也是LCD液晶显示设备的标准接口,具有广泛的应用范围。在FGPA中,常广泛用于图像处理等领域。
在一般应用中就是使用VGA显示器去显示图像传感器实时采集到的图像
比如用摄像头采集数据通过VGA显示器显示,
我们只需要去控制图像数据流的存储和传输就可以了。无需对图像数据进行加工处理,不需要FPGA主动生成数据。
二,VGA成像原理
在 VGA 标准兴起的时候,常见的彩色显示器一般由 CRT(阴极射线管)构成,色彩是由 RGB 三基色组成。显示是用逐行扫描的方式解决。阴极射线枪发出的电子束打在涂有荧光粉的荧光屏上,产生 RGB 三基色,合成一个彩色像素。
显示器扫描方式分为逐行扫描和隔行扫描:逐行扫描是扫描从屏幕左上角一点开始,从左像右逐点扫描,每扫描完一行,电子束回到屏幕的左边下一行的起始位置,在这期间,CRT对电子束进行消隐,每行结束时,用行同步信号进行同步;当扫描完所有的行,形成一帧,用场同步信号进行场同步,并使扫描回到屏幕左上方,同时进行场消隐,开始下一帧。隔行扫描是指电子束扫描时每隔一行扫一线,完成一屏后在返回来扫描剩下的线,隔行扫描的显示器闪烁的厉害,会让使用者的眼睛疲劳。
完成一行扫描的时间称为水平扫描时间,其倒数称为行频率;完成一帧(整屏)扫描的时间称为垂直扫描时间,其倒数称为场频率,即刷新一屏的频率,常见的有60Hz,75Hz等等。标准的VGA显示的场频60Hz,行频31.5KHz。
行场消隐信号:是针对老式显像管的成像扫描电路而言的。电子枪所发出的电子束从屏幕的左上角开始向右扫描,一行扫完需将电子束从右边移回到左边以便扫描第二行。在移动期间就必须有一个信号加到电路上,使得电子束不能发出。不然这个回扫线会破坏屏幕图像的。这个阻止回扫线产生的信号就叫作消隐信号,场信号的消隐也是一个道理。
显示带宽:带宽指的显示器可以处理的频率范围。如果是60Hz刷新频率的VGA,其带宽达640x480x60=18.4MHz,70Hz的刷新频率1024x768分辨率的SVGA,其带宽达1024x768x70=55.1MHz。
时钟频率:以640x480@59.94Hz(60Hz)为例,每场对应525个行周期(525=10+2+480+33),其中480为显示行。每场有场同步信号,该脉冲宽度为2个行周期的负脉冲,每显示行包括800点时钟,其中640点为有效显示区,每一行有一个行同步信号,该脉冲宽度为96个点时钟。由此可知:行频为525*59.94=31469Hz,需要点时钟频率:525*800*59.94约25MHz.
三,VGA时序分析
相关概念
VGA时序标准是指视频图像在VGA显示系统中的时序规范。它定义了图像信号的各种时序参数,包括水平同步信号、垂直同步信号、行频率、场频率等,以确保正确的图像显示。VGA扫描完一行所需要的时间为水平扫描时间,水平扫描时间的倒成为行频;完成一帧(全屏)扫描所需要的时间为垂直扫描时间,垂直扫描时间的倒数成为场频,即刷新一次显示屏的频率。在CRT显示中,为避免电子束扫描线的回归线产生的信号称为消隐信号,用于控制电子束的移动以避免破坏屏幕画面,有行消隐信号和场消隐信号两种,其存在的时间称为行消隐期和场消隐期[9]。
在行扫描完成后,需要信号对下一行的数据进行同步,所以称为行同步信号,同理,每完成一顿画面的扫描需要信号对下一帧的数据进行同步,这就是场同步信号。用来产生基准频率信号叫做时钟信号,在显示系统中用于同步VGA接口的时序操作,其频率取决于显示分辨率和刷新率的需求。
根据视频电子标准协会(VESA)的标准定义,行时序和场时序都必须包含同步脉冲、显示后沿、显示时序段和显示前沿四个部分。同时VGA工业标准显示模式对其进行了进步的规范:行同步信号、场同步信号都必须为负极性,也就是说同步脉冲要求必须是负脉冲。
时序分析
从时序图中可以看出,行扫描和场扫描都包括四个主要阶段:同步、消隐(前消隐和后消隐)、以及扫描阶段。同步阶段的目的是防止连续叠加、滞后或超前扫描;消隐阶段主要是为了老式阴极射线管显示而设计,以避免回扫描线干扰正常显示;扫描阶段就是对显示器上的实际像素单元进行显示操作。
行扫描周期由a、b、c、d四部分组成,其中行同步是最先进行的,然后才开始数据扫描,具体如下:
VGA时序标准(行扫描)
行扫描具体过程:
序号 | 步骤 | 名称 | 描述 |
---|---|---|---|
1 | H_SYNC | 行同步时期 | 进行行扫描地址的复位 |
2 | H_BACK | 行消隐后肩 | 扫描地址转移后稳定等待/准备期 |
3 | H_DISP | 行显示时期 | 行地址扫描/显示期,此时数据有效 |
4 | H_FRONT | 行消隐前肩 | 扫描地址转移的准备 |
5 | H_TQTAL | 行扫描总时间 | 一行扫描的总时间(5个过程) |
场扫描时序与行扫描时序相似,但场扫描周期由多个行扫描周期组成,每个场扫描都有自己的时序,包括场消隐前肩、场同步时期、场消隐后肩和场总显示时间。在场消隐期间,H_SYNC时序保持不变,但数据输入被屏蔽。尽管显示器具有不同的分辨率,但扫描时序保持一致。场扫描周期也由a、b、c、d四部分组成,首先进行行同步操作,然后才开始数据扫描,具体如下:
VGA时序标准(场扫描)
场扫描具体过程
序号 | 步骤 | 名称 | 描述 |
---|---|---|---|
1 | V_SYNC | 场同步时期 | 进行场扫描地址的复位 |
2 | V_BACK | 场消隐后肩 | 扫描地址转移后稳定等待/准备期 |
3 | V_DISP | 场显示时期 | 场地址扫描/显示期,此时数据有效 |
4 | V_FRONT | 场消隐前肩 | 扫描地址转移的准备 |
5 | V_TQTAL | 场扫描总时间 | 一场扫描的总时间(5个过程) |
时序标准表
在VGA 接口协议中,不同的显示模式因为有不同的分辨率或不同的刷新频率,所以其时序也不相同。对于每种显示模式的时序,VGA 都有严格的工业标准,下表所示即为Xilinx公司制定的VGA时序标准。
VGA时序工业标准(Xilinx Inc.)
显示模式 | 时钟信号 /MHz | 水平参数(单位:像素) | 垂直参数(单位:行) | ||||||
---|---|---|---|---|---|---|---|---|---|
有效区城 | 前肩脉冲 | 同步脉冲 | 后肩脉冲 | 有效区城 | 前肩脉冲 | 同步脉冲 | 后肩脉冲 | ||
640× 480,60 Hz | 25 | 640 | 16 | 96 | 48 | 480 | 10 | 2 | 33 |
640× 480,72 Hz | 31 | 640 | 24 | 40 | 128 | 480 | 9 | 3 | 28 |
640×480,75 Hz | 31 | 640 | 16 | 96 | 48 | 480 | 11 | 2 | 32 |
640×480,85 Hz | 36 | 640 | 42 | 48 | 112 | 480 | 1 | 3 | 25 |
800×600,56 Hz | 38 | 800 | 42 | 128 | 128 | 600 | 1 | 4 | 14 |
800×600,60 Hz | 40 | 800 | 40 | 128 | 88 | 600 | 1 | 4 | 23 |
800×600,72 Hz | 50 | 800 | 56 | 120 | 64 | 600 | 37 | 6 | 23 |
800×600,75 Hz | 49 | 800 | 16 | 80 | 160 | 600 | 1 | 2 | 21 |
800×600,85 Hz | 56 | 800 | 32 | 64 | 152 | 600 | 1 | 3 | 27 |
1024×768,60 Hz | 65 | 1024 | 24 | 136 | 160 | 768 | 3 | 6 | 29 |
1024×768,70 Hz | 75 | 1024 | 24 | 136 | 144 | 768 | 3 | 6 | 29 |
1024×768,75 Hz | 78 | 1024 | 16 | 96 | 176 | 768 | 1 | 3 | 28 |
1024×768,85 Hz | 94 | 1024 | 48 | 96 | 208 | 768 | 1 | 3 | 36 |
这里还有VGA协议标准
链接:百度网盘 请输入提取码 提取码:igee
四,VGA控制器Verilog设计与实现
以640× 480,60 Hz显示模式为例。
序号
步骤
名称
描述
1
H_SYNC
行同步时期
进行行扫描地址的复位
2
H_BACK
行消隐后肩
扫描地址转移后稳定等待/准备期
3
H_DISP
行显示时期
行地址扫描/显示期,此时数据有效
4
H_FRONT
行消隐前肩
扫描地址转移的准备
5
H_TQTAL
行扫描总时间
一行扫描的总时间(5个过程)
显示模式
时钟信号 /MHz
水平参数(单位:像素)
垂直参数(单位:行)
有效区城
前肩脉冲
同步脉冲
后肩脉冲
有效区城
前肩脉冲
同步脉冲
后肩脉冲
640× 480,60 Hz
25
640
16
96
48
480
10
2
33
行同步脉冲的开始位置(序号1的开始) HS_Begin = 0
行同步脉冲的结束位置(序号2的开始) HS_End = 96
行数据开始输出的位置(序号3的开始) Hdat_Begin = 96 + 48
行数据停止输出的位置(序号4的开始) Hdat_End = 96 + 48 + 640
行同步信号的结束位置(序号5) Hsync_End = 96 + 48 + 640 + 16
同理:
场同步脉冲的开始位置(序号1的开始) VS_Begin = 0
场同步脉冲的结束位置(序号2的开始) VS_End = 2
场数据开始输出的位置(序号3的开始) Vdat_Begin = 2 + 33
场数据停止输出的位置(序号4的开始) Vdat_End = 2 + 33 + 480
场同步信号的结束位置(序号5) Vsync_End = 2 + 33 + 480 +10
采用线性序列的方法:首先定义以下参数
parameter VGA_HS_end = 11'd95 , //HS_End - 1
hdat_begin = 11'd144 , //Hdat_Begin
hdat_end = 11'd784, //Hdat_End
hpixel_end = 11'd799, //Hsync_End - 1
VGA_VS_end = 11'd1 , //VS_End - 1
vdat_begin = 11'd35 , //Vdat_Begin
vdat_end = 11'd515 , //Vdat_End
vline_end = 11'd524 ; //Vsync_End - 1
信号时序条件:
assign VGA_CLK = ~sys_clk_25M ;
assign VGA_BLK = ((hcount_r >= hdat_begin)&&(hcount_r < hdat_end)
&&(vcount_r >= vdat_begin)&&(vcount_r < vdat_end))?1'b1:1'b0;
assign hcount = VGA_BLK ? (hcount_r - hdat_begin) : 10'd0;
assign vcount = VGA_BLK ? (vcount_r - vdat_begin) : 10'd0;
assign VGA_HS = (hcount_r > VGA_HS_end) ? 1'b1 : 1'b0 ;
assign VGA_VS = (vcount_r > VGA_VS_end) ? 1'b1 : 1'b0 ;
assign VGA_DATA= (VGA_BLK)?Data_in : 24'h000000;
verilog代码:
module VGA_CTRL(
input sys_clk_25M, // 系统时钟输入
input sys_rst_n, // 系统复位信号输入
input [23:0] Data_in, // 数据输入
output [10:0] hcount, // 水平计数输出
output [10:0] vcount, // 垂直计数输出
output VGA_HS, // 水平同步信号输出
output VGA_VS, // 垂直同步信号输出
output VGA_BLK, // 背景信号输出
output VGA_CLK, // VGA时钟输出
output [23:0] VGA_DATA // 数据输出
);
// 时序参数
parameter VGA_HS_end = 11'd95, // 水平同步结束位置 (HS_End - 1)
hdat_begin = 11'd144, // 水平数据开始位置 (Hdat_Begin)
hdat_end = 11'd784, // 水平数据结束位置 (Hdat_End)
hpixel_end = 11'd799, // 水平像素结束位置 (Hsync_End - 1)
VGA_VS_end = 11'd1, // 垂直同步结束位置 (VS_End - 1)
vdat_begin = 11'd35, // 垂直数据开始位置 (Vdat_Begin)
vdat_end = 11'd515, // 垂直数据结束位置 (Vdat_End)
vline_end = 11'd524; // 垂直行结束位置 (Vsync_End - 1)
reg [10:0] hcount_r; // 水平计数器寄存器
reg [10:0] vcount_r; // 垂直计数器寄存器
// 水平计数器逻辑
always @(posedge sys_clk_25M or negedge sys_rst_n)
begin
if (!sys_rst_n)
hcount_r <= 11'd0;
else if (hcount_r == hpixel_end)
hcount_r <= 11'd0;
else
hcount_r <= hcount_r + 1'd1;
end
// 垂直计数器逻辑
always @(posedge sys_clk_25M or negedge sys_rst_n)
begin
if (!sys_rst_n)
vcount_r <= 11'd0;
else if (hcount_r == hpixel_end)
begin
if (vcount_r == vline_end)
vcount_r <= 11'd0;
else
vcount_r <= vcount_r + 1'd1;
end
else
vcount_r <= vcount_r;
end
// 输出信号赋值
assign VGA_CLK = ~sys_clk_25M; // VGA时钟为系统时钟的反相
assign VGA_BLK = ((hcount_r >= hdat_begin) && (hcount_r < hdat_end) &&
(vcount_r >= vdat_begin) && (vcount_r < vdat_end)) ? 1'b1 : 1'b0;
assign hcount = VGA_BLK ? (hcount_r - hdat_begin) : 10'd0;
assign vcount = VGA_BLK ? (vcount_r - vdat_begin) : 10'd0;
assign VGA_HS = (hcount_r > VGA_HS_end) ? 1'b1 : 1'b0;
assign VGA_VS = (vcount_r > VGA_VS_end) ? 1'b1 : 1'b0;
assign VGA_DATA = VGA_BLK ? Data_in : 24'h000000;
endmodule
textbench:
`timescale 1ns / 1ps // 定义时间单位和精度
module VGA_CTRL_tb();
reg sys_clk_25M; // 系统时钟信号
reg sys_rst_n; // 系统复位信号
reg [23:0] Data_in; // 数据输入信号
wire [10:0] hcount; // 水平计数信号
wire [10:0] vcount; // 垂直计数信号
wire VGA_HS; // 水平同步信号
wire VGA_VS; // 垂直同步信号
wire VGA_BLK; // 背景信号
wire VGA_CLK; // VGA时钟信号
wire [23:0] VGA_DATA; // 数据输出信号
VGA_CTRL VGA_CTRL(
.sys_clk_25M(sys_clk_25M),
.sys_rst_n(sys_rst_n),
.Data_in(Data_in),
.hcount(hcount),
.vcount(vcount),
.VGA_HS(VGA_HS),
.VGA_VS(VGA_VS),
.VGA_BLK(VGA_BLK),
.VGA_CLK(VGA_CLK),
.VGA_DATA(VGA_DATA)
);
initial sys_clk_25M = 1; // 初始化系统时钟信号为高电平
always #20 sys_clk_25M = ~sys_clk_25M; // 生成25MHz的时钟信号
initial begin
sys_rst_n = 0; // 将系统复位信号置为低电平
#201; // 等待一段时间
sys_rst_n = 1; // 将系统复位信号置为高电平
#200000000; // 等待一段时间进行仿真测试
$stop; // 停止仿真
end
always @(posedge sys_clk_25M or negedge sys_rst_n)
if (!sys_rst_n)
Data_in <= 0; // 复位时将数据输入信号置为零
else if (!VGA_BLK)
Data_in <= Data_in; // 在显示区域外保持数据输入信号不变
else
Data_in <= Data_in + 1; // 在显示区域内,数据输入信号递增1
endmodule
首先简单计算一下VGA_HS的时间:3840/40 = 96 满足给定条件 其余同理皆满足条件
模拟验证完成。
五,VGA控制器板级验证
以640× 480,60 Hz显示模式为例。
颜色定义:
localparam BLACK =24'h000000,//黑色 BLUE =24'h0000FF,//蓝色 RED =24'hFF0000,//红色 PURPPLE =24'hFF00FF,//紫色 GREEN =24'h00FF00,//绿色 CYAN =24'h00FFFF,//青色 YELLOW =24'hFFFF00,//黄色 WHITE =24'hFFFFFF;//白色
对屏幕进行相应划分(4×2):
wire C0_act = hcount >= 0 && hcount < 320;//正在扫描第0列 wire C1_act = hcount >= 320 && hcount < 640;//正在扫描第1列 wire R0_act = vcount >= 0 && vcount < 120;//正在扫描第0行 wire R1_act = vcount >= 120 && vcount < 240;//正在扫描第1行 wire R2_act = vcount >= 240 && vcount < 360;//正在扫描第2行 wire R3_act = vcount >= 360 && vcount < 480;//正在扫描第3行 wire R0_C0_act = R0_act & C0_act;//第0行0列像素块被扫描中 wire R0_C1_act = R0_act & C1_act;//第0行1列像素块被扫描中 wire R1_C0_act = R1_act & C0_act;//第1行0列像素块被扫描中 wire R1_C1_act = R1_act & C1_act;//第1行1列像素块被扫描中 wire R2_C0_act = R2_act & C0_act;//第2行0列像素块被扫描中 wire R2_C1_act = R2_act & C1_act;//第2行1列像素块被扫描中 wire R3_C0_act = R3_act & C0_act;//第3行0列像素块被扫描中 wire R3_C1_act = R3_act & C1_act;//第3行1列像素块被扫描中
对屏幕进行相应划分(2×4):
wire H_C0_act = hcount >= 0 && hcount < 160;//正在扫描第0列 wire H_C1_act = hcount >= 160 && hcount < 320;//正在扫描第1列 wire H_C2_act = hcount >= 320 && hcount < 480;//正在扫描第2列 wire H_C3_act = hcount >= 480 && hcount < 640;//正在扫描第3列 wire V_R0_act = vcount >= 0 && vcount < 240;//正在扫描第0行 wire V_R1_act = vcount >= 240 && vcount < 480;//正在扫描第1行 wire H_R0_C0_act = H_C0_act & V_R0_act;//第0行0列像素块被扫描中 wire H_R0_C1_act = H_C1_act & V_R0_act;//第0行1列像素块被扫描中 wire H_R0_C2_act = H_C2_act & V_R0_act;//第0行2列像素块被知描中 wire H_R0_C3_act = H_C3_act & V_R0_act;//第0行3列像素块被扫描中 wire H_R1_C0_act = H_C0_act & V_R1_act;//第1行0列像素块被扫描中 wire H_R1_C1_act = H_C1_act & V_R1_act;//第1行1列像素块被扫描中 wire H_R1_C2_act = H_C2_act & V_R1_act;//第1行2列像素块被扫描中 wire H_R1_C3_act = H_C3_act & V_R1_act;//第1行3列像素块被扫描中
下面是实现屏幕每一秒切换上述彩图的verilg(VGA_TEXT) (配合上面的VGA_CTRL模块使用)
module VGA_TEXT( input sys_clk_50M , input sys_rst_n , output VGA_HS , output VGA_VS , output VGA_BLK , output VGA_CLK , output [23:0] VGA_DATA ); reg [23:0] Data_in ; wire sys_clk_25M ; wire locked ; reg [11:0] hcount_r; reg [11:0] vcount_r; reg [31:0] cnt ; reg en ; always @(posedge sys_clk_50M or negedge sys_rst_n) if(!sys_rst_n) cnt <= 0; else if(cnt == 99_999_999) cnt <= 0 ; else cnt <= cnt + 1'd1; always @(posedge sys_clk_50M or negedge sys_rst_n) if(!sys_rst_n) en <= 0; else if(cnt >= 99_999_999) en <= 1 ; else en <= 0 ; localparam BLACK =24'h000000,//黑色 BLUE =24'h0000FF,//蓝色 RED =24'hFF0000,//红色 PURPPLE =24'hFF00FF,//紫色 GREEN =24'h00FF00,//绿色 CYAN =24'h00FFFF,//青色 YELLOW =24'hFFFF00,//黄色 WHITE =24'hFFFFFF;//白色 pll_25M pll_25M ( // Clock out ports .clk_out1(sys_clk_25M), // output clk_out1 // Status and control signals .reset(sys_rst_n), // input reset .locked(locked), // output locked // Clock in ports .clk_in1(sys_clk_50M)); // input clk_in1 VGA_CTRL VGA_CTRL( . sys_clk_25M (sys_clk_25M) , . sys_rst_n (sys_rst_n) , . Data_in (Data_in) , . hcount (hcount) , . vcount (vcount) , . VGA_HS (VGA_HS) , . VGA_VS (VGA_VS) , . VGA_BLK (VGA_BLK) , . VGA_CLK (VGA_CLK) , . VGA_DATA (VGA_DATA) ); wire C0_act = hcount >= 0 && hcount < 320;//正在扫描第0列 wire C1_act = hcount >= 320 && hcount < 640;//正在扫描第1列 wire R0_act = vcount >= 0 && vcount < 120;//正在扫描第0行 wire R1_act = vcount >= 120 && vcount < 240;//正在扫描第1行 wire R2_act = vcount >= 240 && vcount < 360;//正在扫描第2行 wire R3_act = vcount >= 360 && vcount < 480;//正在扫描第3行 wire R0_C0_act = R0_act & C0_act;//第0行0列像素块被扫描中 wire R0_C1_act = R0_act & C1_act;//第0行1列像素块被扫描中 wire R1_C0_act = R1_act & C0_act;//第1行0列像素块被扫描中 wire R1_C1_act = R1_act & C1_act;//第1行1列像素块被扫描中 wire R2_C0_act = R2_act & C0_act;//第2行0列像素块被扫描中 wire R2_C1_act = R2_act & C1_act;//第2行1列像素块被扫描中 wire R3_C0_act = R3_act & C0_act;//第3行0列像素块被扫描中 wire R3_C1_act = R3_act & C1_act;//第3行1列像素块被扫描中 wire H_C0_act = hcount >= 0 && hcount < 160;//正在扫描第0列 wire H_C1_act = hcount >= 160 && hcount < 320;//正在扫描第1列 wire H_C2_act = hcount >= 320 && hcount < 480;//正在扫描第2列 wire H_C3_act = hcount >= 480 && hcount < 640;//正在扫描第3列 wire V_R0_act = vcount >= 0 && vcount < 240;//正在扫描第0行 wire V_R1_act = vcount >= 240 && vcount < 480;//正在扫描第1行 wire H_R0_C0_act = H_C0_act & V_R0_act;//第0行0列像素块被扫描中 wire H_R0_C1_act = H_C1_act & V_R0_act;//第0行1列像素块被扫描中 wire H_R0_C2_act = H_C2_act & V_R0_act;//第0行2列像素块被知描中 wire H_R0_C3_act = H_C3_act & V_R0_act;//第0行3列像素块被扫描中 wire H_R1_C0_act = H_C0_act & V_R1_act;//第1行0列像素块被扫描中 wire H_R1_C1_act = H_C1_act & V_R1_act;//第1行1列像素块被扫描中 wire H_R1_C2_act = H_C2_act & V_R1_act;//第1行2列像素块被扫描中 wire H_R1_C3_act = H_C3_act & V_R1_act;//第1行3列像素块被扫描中 always @(posedge sys_clk_50M) if (en == 1) begin case({R3_C1_act, R3_C0_act, R2_C1_act,R2_C0_act,R1_C1_act, R1_C0_act, R0_C1_act,R0_C0_act} ) 8'b0000_0001: Data_in <= BLACK; 8'b0000_0010: Data_in <= BLUE; 8'b0000_0100: Data_in <= RED; 8'b0000_1000: Data_in <= PURPPLE; 8'b0001_0000: Data_in <= GREEN; 8'b0010_0000: Data_in <= CYAN; 8'b0100_0000: Data_in <= YELLOW; 8'b1000_0000: Data_in <= WHITE; endcase end else if (en == 0) begin case ({ H_R1_C3_act,H_R1_C2_act,H_R1_C1_act,H_R1_C0_act,H_R0_C3_act,H_R0_C2_act,H_R0_C1_act,H_R0_C0_act}) 8'b0000_0001: Data_in <= BLACK; 8'b0000_0010: Data_in <= BLUE; 8'b0000_0100: Data_in <= RED; 8'b0000_1000: Data_in <= PURPPLE; 8'b0001_0000: Data_in <= GREEN; 8'b0010_0000: Data_in <= CYAN; 8'b0100_0000: Data_in <= YELLOW; 8'b1000_0000: Data_in <= WHITE; endcase end else Data_in <= 24'h000000; endmodule
(条件不允许 没做实践 不知可行否,)故无实践图片
六,多分辨率适配VGA控制器设计
应用条件编译来实现要求
了解条件编译:
参数定义关系:
parameter VGA_HS_end = `H_Sync_Time - 1 , //H_Sync_Time -1 hdat_begin = `H_Sync_Time + `H_Back_Porch + `H_Left_Border , //H_Sync_Time + H_Back_Porch + H_Left_Border hdat_end = `H_Sync_Time + `H_Back_Porch + `H_Left_Border + `H_Data_Time , //H_Sync_Time + H_Back_Porch + H_Left_Border + H_Data_Time hpixel_end = `H_Total_Time - 1 , //hdat_end +H_Right_Border + H_Front_Porch - 1 VGA_VS_end = `V_Sync_Time - 1 , //V_Sync_Time -1 vdat_begin = `V_Sync_Time + `V_Back_Porch + `V_Top_Border , //V_Sync_Time + V_Back_Porch + V_Top_Border vdat_end = `V_Sync_Time + `V_Back_Porch + `V_Top_Border + `V_Data_Time , //vdat_begin + V_Data_Time vline_end = `V_Total_Time - 1 ; //vdat_end + V_Bottom_Border + V_Front_Porch - 1
参数定义文件:(disp_parameter_cfg.v)根据自己的需求选择分辨率
//`define Resolution_480x272 1 //时钟为9MHz// //`define Resolution_640x480 1 //时钟为25MHz //`define Resolution_800x480 1 //时钟为33MHz// //`define Resolution_800x600 1 //时钟为40MHz// //`define Resolution_1024x600 1 //时钟为51MHz// //`define Resolution_1024x768 1 //时钟为65MHz// //`define Resolution_1280x720 1 //时钟为74.25MHz// `define Resolution_1920x1080 1//时钟为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_Data_Time 12'd640 `define H_Right_Border 12'd8 `define H_Front_Porch 12'd8 `define H_Sync_Time 12'd96 `define H_Back_Porch 12'd40 `define H_Left_Border 12'd8 `define V_Total_Time 12'd525 `define V_Data_Time 12'd480 `define V_Bottom_Border 12'd8 `define V_Front_Porch 12'd2 `define V_Sync_Time 12'd2 `define V_Back_Porch 12'd25 `define V_Top_Border 12'd8 `elsif Resolution_800x480 `define H_Total_Time 12'd1056 `define H_Data_Time 12'd800 `define H_Right_Border 12'd0 `define H_Front_Porch 12'd40 `define H_Sync_Time 12'd128 `define H_Back_Porch 12'd88 `define H_Left_Border 12'd0 `define V_Total_Time 12'd525 `define V_Data_Time 12'd480 `define V_Bottom_Border 12'd8 `define V_Front_Porch 12'd2 `define V_Sync_Time 12'd2 `define V_Back_Porch 12'd25 `define V_Top_Border 12'd8 `elsif Resolution_800x600 `define H_Total_Time 12'd1056 `define H_Data_Time 12'd800 `define H_Right_Border 12'd0 `define H_Front_Porch 12'd40 `define H_Sync_Time 12'd128 `define H_Back_Porch 12'd88 `define H_Left_Border 12'd0 `define V_Total_Time 12'd628 `define V_Data_Time 12'd600 `define V_Bottom_Border 12'd0 `define V_Front_Porch 12'd1 `define V_Sync_Time 12'd4 `define V_Back_Porch 12'd23 `define V_Top_Border 12'd0 `elsif Resolution_1024x600 `define H_Total_Time 12'd1344 `define H_Data_Time 12'd1024 `define H_Right_Border 12'd0 `define H_Front_Porch 12'd24 `define H_Sync_Time 12'd136 `define H_Back_Porch 12'd160 `define H_Left_Border 12'd0 `define V_Total_Time 12'd628 `define V_Data_Time 12'd600 `define V_Bottom_Border 12'd0 `define V_Front_Porch 12'd1 `define V_Sync_Time 12'd4 `define V_Back_Porch 12'd23 `define V_Top_Border 12'd0 `elsif Resolution_1024x768 `define H_Total_Time 12'd1344 `define H_Data_Time 12'd1024 `define H_Right_Border 12'd0 `define H_Front_Porch 12'd24 `define H_Sync_Time 12'd136 `define H_Back_Porch 12'd160 `define H_Left_Border 12'd0 `define V_Total_Time 12'd806 `define V_Data_Time 12'd768 `define V_Bottom_Border 12'd0 `define V_Front_Porch 12'd3 `define V_Sync_Time 12'd6 `define V_Back_Porch 12'd29 `define V_Top_Border 12'd0 `elsif Resolution_1280x720 `define H_Total_Time 12'd1650 `define H_Data_Time 12'd1280 `define H_Right_Border 12'd0 `define H_Front_Porch 12'd110 `define H_Sync_Time 12'd40 `define H_Back_Porch 12'd220 `define H_Left_Border 12'd0 `define V_Total_Time 12'd750 `define V_Data_Time 12'd720 `define V_Bottom_Border 12'd0 `define V_Front_Porch 12'd5 `define V_Sync_Time 12'd5 `define V_Back_Porch 12'd20 `define V_Top_Border 12'd0 `elsif Resolution_1920x1080 `define H_Total_Time 12'd2200 `define H_Data_Time 12'd1920 `define H_Right_Border 12'd0 `define H_Front_Porch 12'd88 `define H_Sync_Time 12'd44 `define H_Back_Porch 12'd148 `define H_Left_Border 12'd0 `define V_Total_Time 12'd1125 `define V_Data_Time 12'd1080 `define V_Bottom_Border 12'd0 `define V_Front_Porch 12'd4 `define V_Sync_Time 12'd5 `define V_Back_Porch 12'd36 `define V_Top_Border 12'd0 `endif
对VGA_CTRL进行更改后为:
`include "disp_parameter_cfg.v" module VGA_CTRL( input sys_clk_25M , input sys_rst_n , input [23:0] Data_in , output [11:0] hcount , output [11:0] vcount , output VGA_HS , output VGA_VS , output VGA_BLK , output VGA_CLK , output [23:0] VGA_DATA ); parameter VGA_HS_end = `H_Sync_Time - 1 , //H_Sync_Time -1 hdat_begin = `H_Sync_Time + `H_Back_Porch + `H_Left_Border , //H_Sync_Time + H_Back_Porch + H_Left_Border hdat_end = `H_Sync_Time + `H_Back_Porch + `H_Left_Border + `H_Data_Time , //H_Sync_Time + H_Back_Porch + H_Left_Border + H_Data_Time hpixel_end = `H_Total_Time - 1 , //hdat_end +H_Right_Border + H_Front_Porch - 1 VGA_VS_end = `V_Sync_Time - 1 , //V_Sync_Time -1 vdat_begin = `V_Sync_Time + `V_Back_Porch + `V_Top_Border , //V_Sync_Time + V_Back_Porch + V_Top_Border vdat_end = `V_Sync_Time + `V_Back_Porch + `V_Top_Border + `V_Data_Time , //vdat_begin + V_Data_Time vline_end = `V_Total_Time - 1 ; //vdat_end + V_Bottom_Border + V_Front_Porch - 1 reg [11:0] hcount_r; reg [11:0] vcount_r; always @(posedge sys_clk_25M or negedge sys_rst_n) if(!sys_rst_n) hcount_r <= 11'd0; else if(hcount_r == hpixel_end) hcount_r <= 11'd0 ; else hcount_r <= hcount_r + 1'd1; always @(posedge sys_clk_25M or negedge sys_rst_n) if(!sys_rst_n) vcount_r <= 11'd0; else if(hcount_r == hpixel_end) begin if(vcount_r == vline_end) vcount_r <= 11'd0; else vcount_r <= vcount_r + 1'd1; end else vcount_r <= vcount_r ; assign VGA_CLK = ~sys_clk_25M ; assign VGA_BLK = ((hcount_r >= hdat_begin)&&(hcount_r < hdat_end)&&(vcount_r >= vdat_begin)&&(vcount_r < vdat_end))?1'b1:1'b0; assign hcount = VGA_BLK ? (hcount_r - hdat_begin) : 10'd0; assign vcount = VGA_BLK ? (vcount_r - vdat_begin) : 10'd0; assign VGA_HS = (hcount_r > VGA_HS_end) ? 1'b1 : 1'b0 ; assign VGA_VS = (vcount_r > VGA_VS_end) ? 1'b1 : 1'b0 ; assign VGA_DATA= (VGA_BLK)?Data_in : 24'h000000; endmodule
如选择1920×1080的分辨率其仿真为:
满足理想要求。。
如要实现上述彩色条纹切换只需将划分区域该为:
wire C0_act = hcount >= 0 && hcount < `H_Data_Time/2; //正在扫描第0列 wire C1_act = hcount >= `H_Data_Time/2 && hcount < `H_Data_Time; //正在扫描第1列 wire R0_act = vcount >= 0 && vcount < `V_Data_Time/4; //正在扫描第o行 wire R1_act = vcount >= `V_Data_Time/4 && vcount < `V_Data_Time/2; //正在扫描第1行 wire R2_act = vcount >= `V_Data_Time/2 && vcount < `V_Data_Time/4*3;//正在扫描第2行 wire R3_act = vcount >= `V_Data_Time/4*3 && vcount <`V_Data_Time; //正在扫描第3行 wire R0_C0_act = R0_act & C0_act;//第0行0列像素块被扫描中 wire R0_C1_act = R0_act & C1_act;//第0行1列像素块被扫描中 wire R1_C0_act = R1_act & C0_act;//第1行0列像素块被扫描中 wire R1_C1_act = R1_act & C1_act;//第1行1列像素块被扫描中 wire R2_C0_act = R2_act & C0_act;//第2行0列像素块被扫描中 wire R2_C1_act = R2_act & C1_act;//第2行1列像素块被扫描中 wire R3_C0_act = R3_act & C0_act;//第3行0列像素块被扫描中 wire R3_C1_act = R3_act & C1_act;//第3行1列像素块被扫描中 wire H_C0_act = hcount >= 0 && hcount< `H_Data_Time/4; //正在扫描第0列 wire H_C1_act = hcount >= `H_Data_Time/4 && hcount < `H_Data_Time/2; //正在扫描第1列 wire H_C2_act = hcount >= `H_Data_Time/2 && hcount < `H_Data_Time/4*3; //正在扫描第2列 wire H_C3_act = hcount >= `H_Data_Time/4*3 && hcount < `H_Data_Time; //正在扫描第3列 wire V_R0_act = vcount >= 0 && vcount< `V_Data_Time/2; //正在扫描第0行 wire V_R1_act = vcount >= `V_Data_Time/2 && vcount < `V_Data_Time; //正在扫描第1行 wire H_R0_C0_act = H_C0_act & V_R0_act;//第0行0列像素块被扫描中 wire H_R0_C1_act = H_C1_act & V_R0_act;//第0行1列像素块被扫描中 wire H_R0_C2_act = H_C2_act & V_R0_act;//第0行2列像素块被知描中 wire H_R0_C3_act = H_C3_act & V_R0_act;//第0行3列像素块被扫描中 wire H_R1_C0_act = H_C0_act & V_R1_act;//第1行0列像素块被扫描中 wire H_R1_C1_act = H_C1_act & V_R1_act;//第1行1列像素块被扫描中 wire H_R1_C2_act = H_C2_act & V_R1_act;//第1行2列像素块被扫描中 wire H_R1_C3_act = H_C3_act & V_R1_act;//第1行3列像素块被扫描中
注意:disp_parameter_cfg.v 与VGA_CTRL等等应在同一目录下
上述代码可能有大小写错误
板机验证待完成...
参考:
https://zh.wikipedia.org/wiki/VGA%E7%AB%AF%E5%AD%90
https://www.baike.com/wikiid/7289585183740198931?ug_source=seo_juhe&anchor=lnrlbiwrkht
B站小梅哥视频
等等.........