尝试用Verilog驱动VGA

本文有些地方是从
http://www.eepw.com.cn/article/275552.htm
http://www.cnblogs.com/spartan/archive/2011/08/16/2140546.html
直接搬运过来的,这些博客都写的很详细,可以去看下。

VGA原理

VGA驱动显示器用的是扫描的方式,一般是逐行扫描
逐行扫描是扫描从屏幕左上角一点开始,从左像右逐点扫描,每扫描完一行,电子束回到屏幕的左边下一行的起始位置,在这期间,CRT对电子束进行消隐,每行结束时,用行同步信号进行同步
当扫描完所有的行,形成一帧后,用场同步信号进行场同步,并使扫描回到屏幕左上方,同时进行场消隐,开始下一帧。
以下是vs 和 hs的时序简图。
这里写图片描述
可以看出,VGA一直在扫描,每一场的扫描包括了若干行扫描,如此循环。

综上我们可以知道,一个标准的VGA接口应该有以下的端口:

红色信号 r
绿色信号 g
蓝色信号 b
行同步信号 hs
场同步信号 vs
以及很多的地屏蔽

信号线及其定义

需要注意的是,r g b信号都是模拟信号,而 hs 和 vs 则是数字信号
一般而言如果要用数字电路驱动rgb模拟信号端口的话,可以有如下几种办法:

  • 直接用0/1数字信号驱动
    这里写图片描述
    因为虽说rgb都是模拟信号驱动的,但是数字信号也是有电压的,可以看做是一个要么恒定不变,要么就跳变的模拟信号。我用的开发板就是这样做的,因为开发板的核心甚至不是FPGA,只是个CPLD,不过胜在不要钱→_→,直接找师兄要的。
    这么做的好处是,不需要在数字电路输出端和VGA接口之间加入模拟电路;
    坏处则是,这样的输出只能有8种(2种电压,3个端口,共8个组合)。

  • 在数字信号输出和VGA接口之间加入一段简单的DA电路
    这里写图片描述
    利用不同的阻值电阻分压,把数字信号转化为模拟信号,然后再输入到VGA的端口。上图是Digilent公司的Basys3 FPGA开发板原理图的VGA部分。
    这样做的好处是,可以用多个数字信号端口输出,组合出更多的模拟信号,增加可输出的颜色种类;
    而这样做的坏处是,……反正除了要多搭几个电阻之外我想不到有什么麻烦的地方。

说完颜色信号,就该是同步信号了。

完成一行扫描的时间称为水平扫描时间,其倒数称为行频率;完成一帧(整屏)扫描的时间称为垂直扫描时间,其倒数称为场频率,即刷新一屏的频率,常见的有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.

所以并不是分辨率多高,我们就需要扫描多少行/场;实际上我们需要扫描的比分辨率数值大,示意图见下图:
这里写图片描述

最坑的是。。。我并没有找到消隐区大小跟分辨率之间有什么数学关系,虽然每个分辨率都有对应唯一的消隐区大小,但是总觉得很坑。。。如果要改变分辨率的话怎么办?

Verilog代码

第一次接触VGA,并没有任何头绪,于是先跑板子的例程,结果发现我那1440*900的联想屏幕并不能显示,倒是感应到了有数据,证明确实有输出,但是一直提示“输入信号超出范围”。结果后来换了个1024*768的BENQ的小屏幕,虽然还是提示“输入信号超出范围”,但是能看到提示框后面显示出了例程输出的色块,但是输出不全。现在想来应该也是消隐区的问题,例程代码并不能看懂,但是貌似并没有考虑消隐的问题,一直在做分频处理,分出好一些时钟驱动同步端口和数据端口。一开始我以为是板子上的12MHz晶振跟例程里写的50MHz频率不符,后来发现板子自带PLL,输入的原始时钟确实是50MHz,反正一直调参数,调了一整天,调得也很盲目,现在想想自己简直就是个傻缺= =||
后来上github找了很多人的开源代码,好不容易终于找到了一个能用的,readme里说这个分辨率是800*600,但是在1440*900屏下也没有出现问题,可能是屏幕能自动适应分辨率吧。代码如下:
效果是在屏幕上显示8个宽度相等的竖条色块,颜色各不相同,反正直接驱动能输出的也就8种颜色。

module Main(
    CLK,
    VGA_HS,
    VGA_VS,
    VGA_R,
    VGA_G,
    VGA_B
    );

    input CLK;
    output VGA_HS,VGA_VS, VGA_R, VGA_G, VGA_B;

    reg[10:0] x_counter;
    reg[10:0] y_counter;

    reg [3:1] GRBX;

    initial begin
        x_counter = 0;
        y_counter = 0;
    end

// Always block to drive drawing, {front|back}-doors, and syncs.
    always @(posedge CLK) begin
        if(x_counter == 1055)
        begin
            x_counter = 0;

            if(y_counter == 627)
                y_counter = 0;
            else
                y_counter = y_counter + 1;
        end
        else
            x_counter = x_counter + 1;

    end

    always @(x_counter or y_counter)
    begin
        if (x_counter<100)  GRBX<=3'b111;
            else if (x_counter<200)   GRBX<=3'b110;
            else if (x_counter<300)   GRBX<=3'b101;
            else if (x_counter<400)  GRBX<=3'b100;
            else if (x_counter<500)   GRBX<=3'b011;
            else if (x_counter<600)   GRBX<=3'b010;
            else if (x_counter<700)   GRBX<=3'b001;
            else  GRBX<=3'b000 ; 
        end

    assign  VGA_R=GRBX[2];
    assign  VGA_G=GRBX[3];
    assign  VGA_B=GRBX[1];

    assign VGA_HS = x_counter > 839 && x_counter < 968;
    assign VGA_VS = y_counter > 600 && y_counter < 605;

endmodule

这是把https://github.com/shiirish/FPGA—VGA里面的main.v改了一下改出来的。令我非常惊喜的是,这个代码真的能动,而且并没有严格要求时钟是多少,也没有试图分频分到60Hz,这里就是考虑了消隐的问题。
后来本来还想用Matlab把一些图片的RGB二值化,生成一系列RGB值放进IP核的ROM里,用 x_counter 和 y_counter 作为地址,输出数据到屏幕,这样就可以显示出图片。
然而。。。CPLD并不能用IP核 T_T,就连ISE的core generator按钮都是灰色的。。。然后心血来潮,用Matlab写了个脚本,生成了四万多行的if。。。强行生成个ROM,就为了显示一张静态图= =||
然而。。。直到现在都还在Synthesize。。。诶都是泪啊。。。

<think>我们正在解决FPGA驱动VGA显示器时出现的“超出范围”错误(VGA out of range error)。这个错误通常是由于VGA时序信号(水平同步、垂直同步以及像素时钟)与显示器的期望值不匹配造成的。我们需要检查并调整FPGA生成的VGA时序参数,确保它们符合显示器支持的规格。 根据引用[2]中提到的FPGA连接方式,我们虽然是在调试触摸屏,但VGA显示原理类似,都需要精确控制时序。因此,我们可以参考类似的思路来调整VGA时序。 解决步骤: 1. **确定显示器的规格**:首先需要知道你的显示器支持的分辨率、刷新率以及对应的时序参数(如水平同步脉冲宽度、后沿、前沿,垂直同步脉冲宽度、后沿、前沿等)。这些信息通常可以在显示器的数据手册中找到。 2. **检查当前FPGA的VGA时序设置**:在FPGA代码中,查找VGA时序生成模块,确认当前设置的分辨率刷新率是否在显示器支持的范围内。常见的VGA模式如640x480@60Hz,800x600@60Hz等。 3. **调整时序参数**:如果当前设置超出了显示器的范围,则需要调整时序参数。以下是一个典型的VGA时序参数表(以640x480@60Hz为例): - 像素时钟:25.175 MHz(实际常用25MHz) - 水平时序: - 显示区域:640像素 - 水平同步脉冲宽度:96像素 - 水平后沿:48像素 - 水平前沿:16像素 - 水平总像素数:800 (640+96+48+16) - 垂直时序: - 显示区域:480行 - 垂直同步脉冲宽度:2行 - 垂直后沿:33行 - 垂直前沿:10行 - 垂直总行数:525 (480+2+33+10) 4. **在FPGA中实现正确的时序**:使用状态机或计数器生成精确的时序。以下是一个Verilog示例代码片段: ```verilog module vga_sync ( input wire clk, // 假设为25MHz时钟 output reg hsync, vsync, output reg [9:0] pixel_x, pixel_y // 当前像素坐标 ); // 水平参数 parameter H_DISPLAY = 640; parameter H_FRONT_PORCH = 16; parameter H_SYNC_PULSE = 96; parameter H_BACK_PORCH = 48; parameter H_TOTAL = H_DISPLAY + H_FRONT_PORCH + H_SYNC_PULSE + H_BACK_PORCH; // 800 // 垂直参数 parameter V_DISPLAY = 480; parameter V_FRONT_PORCH = 10; parameter V_SYNC_PULSE = 2; parameter V_BACK_PORCH = 33; parameter V_TOTAL = V_DISPLAY + V_FRONT_PORCH + V_SYNC_PULSE + V_BACK_PORCH; // 525 reg [9:0] h_count = 0; reg [9:0] v_count = 0; // 水平计数器 always @(posedge clk) begin if (h_count < H_TOTAL - 1) h_count <= h_count + 1; else begin h_count <= 0; // 垂直计数器在水平计数器完成一行时递增 if (v_count < V_TOTAL - 1) v_count <= v_count + 1; else v_count <= 0; end end // 生成同步信号(负极性同步,有些显示器可能需要正极性,请根据显示器规格调整) assign hsync = (h_count >= H_DISPLAY + H_FRONT_PORCH && h_count < H_DISPLAY + H_FRONT_PORCH + H_SYNC_PULSE) ? 1'b0 : 1'b1; assign vsync = (v_count >= V_DISPLAY + V_FRONT_PORCH && v_count < V_DISPLAY + V_FRONT_PORCH + V_SYNC_PULSE) ? 1'b0 : 1'b1; // 生成当前像素坐标(仅在显示区域内有效) assign pixel_x = (h_count < H_DISPLAY) ? h_count : 10'd0; assign pixel_y = (v_count < V_DISPLAY) ? v_count : 10'd0; endmodule ``` 5. **使用正确的像素时钟**:确保FPGA提供给VGA的像素时钟与所选分辨率匹配。例如,640x480@60Hz需要大约25MHz的时钟。如果使用其他分辨率,请查阅标准VGA时序表。 6. **检查硬件连接**:确认FPGA与VGA接口的引脚连接正确,特别是同步信号(HSYNC, VSYNC)RGB信号。 如果调整后仍然出现超出范围的错误,请尝试以下方法: - 尝试更低的分辨率(如640x480@60Hz),因为几乎所有VGA显示器都支持这个模式。 - 检查同步信号的极性(正极性或负极性),有些显示器对同步信号的极性有特定要求。标准VGA通常使用负极性同步,但最好参考显示器手册。 相关问题: 1. 如何通过示波器测量VGA时序信号? 2. FPGA中如何生成不同分辨率的VGA时序? 3. VGA显示中颜色深度显存设计有哪些要点?</think>### FPGA VGA "Out of Range" 错误解决方法 当FPGA驱动VGA显示器出现"超出范围"错误时,通常是由于**时序信号不匹配**导致的。以下是系统性的解决步骤: #### 1. **检查基础时序参数** - **确认分辨率匹配**:显示器支持的刷新率分辨率必须与FPGA生成的时序一致 $$ \text{水平总时间} = \text{显示宽度} + \text{前沿} + \text{同步脉宽} + \text{后沿} $$ $$ \text{垂直总时间} = \text{显示高度} + \text{前沿} + \text{同步脉宽} + \text{后沿} $$ - **参考标准时序**:常用640x480@60Hz参数: ``` 像素时钟:25.175 MHz 水平:640显示 + 16前沿 + 96同步 + 48后沿 = 800像素 垂直:480显示 + 10前沿 + 2同步 + 33后沿 = 525行 ``` #### 2. **关键信号调试** - **同步信号极性**:检查HSYNC/VSYNC极性(正/负脉冲) - **时钟精度**:使用示波器测量像素时钟频率(误差需<±0.5%) - **信号完整性**: - RGB信号需加220Ω终端电阻 - 同步信号走线长度≤15cm(避免延迟差异) #### 3. **FPGA代码验证** 检查Verilog/VHDL时序生成模块: ```verilog // 示例:640x480时序生成 always @(posedge clk_25m) begin if (h_count == 799) h_count <= 0; else h_count <= h_count + 1; if (h_count == 799) begin if (v_count == 524) v_count <= 0; else v_count <= v_count + 1; end hsync <= (h_count >= 656 && h_count < 752); // 同步脉冲位置 vsync <= (v_count >= 490 && v_count < 492); valid <= (h_count < 640) && (v_count < 480); // 有效显示区域 end ``` #### 4. **硬件排查** - **连接器检查**:确认VGA接口的HSYNC(Pin13)、VSYNC(Pin14)接线正确 - **电源噪声**:测量3.3V电源纹波(应<50mV) - **信号交叉**:用示波器捕获同步信号与时钟的相位关系 #### 5. **显示器兼容性处理** - **EDID读取**:通过I²C读取显示器参数(SCL=Pin15,SDA=Pin12) - **自动适应**:实现动态时序切换逻辑 ```verilog i2c_master edid_read ( .scl(vga_scl), .sda(vga_sda), .address(8'hA0), // EDID默认地址 .data_out(edid_data) ); ``` > **注意**:某些工业显示器要求严格的前沿/后沿比例,需查阅具体型号手册[^2]。 ### 常见错误原因及修复 | 问题现象 | 解决方案 | |-------------------------|------------------------------| | 闪烁且显示"Out of Range"| 增加垂直后沿(+5~10行) | | 图像偏移 | 调整水平前沿(±5像素) | | 仅部分分辨率可用 | 检查EDID解析模块 | | 高温环境下故障 | 降低像素时钟(如24MHz) | ### 进阶调试工具 1. **SignalTap逻辑分析**:捕获HSYNC/VSYNC实际波形 2. **Xilinx Clock Wizard**:生成精确的像素时钟 3. **在线时序计算器**:使用[VESA时序生成器](https://www.tinyvga.com/vga-timing)验证参数 相关问题: 1. 如何通过FPGA读取显示器的EDID信息? 2. VGA时序中前沿/后沿参数对图像显示有哪些具体影响? 3. 在低端FPGA上实现1080p显示需要哪些优化技巧? 4. 如何用Verilog实现动态分辨率切换功能?
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值