基于FPGA的VGA显示综合实验

对前一阶段的VGA显示实验做一个小小的学习总结,具体的实验内容包括:VGA 彩条显示实验、VGA方块移动实验(由此联想到是否可以实现贪吃蛇游戏的设计)、VGA字符显示实验、VGA图片显示实验。

1. VGA 显示原理

  • 图示
     16位VGA实物图
    如图所示为RGB-565的VGA接口的实物图,下面介绍一下VGA各个接口的定义。

  • 接口定义
    在这里插入图片描述
    16个接口中,常用的接口分别是1、2、3、13(行同步)、14(场同步)这五个接口。

  • 原理图
    众所周知,任何一种颜色,都可以用红、绿、蓝三种元素表示出来,VGA显示便是利用了这一巧妙的原理。
    通常情况下,可以使用ADV7123芯片进行数模转换,但在这里,本实验使用了更为简便的电阻权值匹配网络来进行数模转换。图(b)简单讲解了2位权值网络,根据不同的{D1,D0}组合(数字信号),得到不同的电流输出值(介于0-3(Vcc/2R)之间),同理,图(a)中红、绿、蓝各由5位、6位、5位信号来控制(故得名RGB-565)。
    在这里插入图片描述
    同理,可以计算推导,得到红、绿、蓝输出的电流介于0-0.714V之间,例如,当D11-D15全为1时(高电平),RED输出0.714v这时的红色分量占比重最大。
    在这里插入图片描述

  • VGA的时序
    在说VGA时序之前,先讲一下VGA显示是怎么实现的。以你面前的电脑屏幕为例,在显示时,从电脑屏幕的最左上角开始,进行一行扫描,在扫描的过程中给这一行的每一个像素点赋值(颜色),扫描完一行之后,接着会到下一行的起始位置,再次开始扫描和赋值,直到扫描到电脑屏幕最右下角为止,此时称完成了一帧图像的扫描。每扫描完一行称为行扫描周期,每扫描完一帧图像称为一个场扫描周期。 在行扫描周期中,用行同步时序进行同步,在场扫描周期中用场同步时序进行同步。
    在这里插入图片描述
    一个行扫描周期分为a b c d四个阶段,同步和显示前沿均为准备阶段,数据线上不需要有任何数据,有效数据段为显示一行图像的数据,显示后沿也是数据线上无需传输数据。场扫描时序与之类似,不再做具体说明。
    在这里插入图片描述
    下面来看一下啊常用的分辨率,我们以640x480@60为例,做一下讲解在这里插入图片描述
    640表示有640基准时钟周期数(像素数),480表示一帧有480行 ,60表示每秒钟刷新60帧。注意,640不等于行同步周期,因为一个行同步周期还要加上同步、显示前沿、显示后沿,场同步周期同理。
    因此,在计算时钟频率时应该使用表达式800x525x60=25.2MHZ这与表格中的时钟频率非常接近。至于为什么要使用25.175MHZ是因为时钟在取用时,有一定的限制很难精确取到25.2MHZ,这里不做深入讲解。

2.VGA彩条显示
通过FPGA开发板上的VGA接口与电脑VGA接口连接,显示的实验效果如下。
在这里插入图片描述

  • 程序框图和顶层模块图
    在这里插入图片描述
    在这里插入图片描述
    实验中,时钟分频模块通过PPL锁相环进行时钟的分频,采用Quartus ii自带的ip核。
    VGA驱动模块与显示器相连接(输出有效信息:行同步信号、场同步信号、颜色信息)

```verilog代码块
	//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;

//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;

//使能RGB565数据输出
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;
                 
//RGB565数据输出                 
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_xpos = data_req ? (cnt_h - (H_SYNC + H_BACK - 1'b1)) : 10'd0;
assign pixel_ypos = data_req ? (cnt_v - (V_SYNC + V_BACK - 1'b1)) : 10'd0;

代码块中,设置了一个行技术器cnt_h,场计数器cnt_v,用于时钟计数,另外在模块内部还定义了像素点颜色输入使能和RGB565数据输出使能信号,这一块值得借鉴和学习。这个体现了VGA驱动模块的实质功能:提供数据输出,提供像素点坐标。

//行计数器对像素时钟计数
always @(posedge vga_clk or negedge sys_rst_n) begin         
    if (!sys_rst_n)
        cnt_h <= 10'd0;                                  
    else begin
        if(cnt_h < H_TOTAL - 1'b1)                                               
            cnt_h <= cnt_h + 1'b1;                               
        else 
            cnt_h <= 10'd0;  
    end
end

//场计数器对行计数
always @(posedge vga_clk or negedge sys_rst_n) begin         
    if (!sys_rst_n)
        cnt_v <= 10'd0;                                  
    else if(cnt_h == H_TOTAL - 1'b1) begin
        if(cnt_v < V_TOTAL - 1'b1)                                               
            cnt_v <= cnt_v + 1'b1;                               
        else 
            cnt_v <= 10'd0;  
    end
end

上面时计数器的代码块,没啥好说的,老常规了。

VGA显示模块根据VGA驱动模块给出的像素点坐标,给坐标赋值。本实验要求为将屏幕分成五个不同颜色的竖直区域(见实验效果图)

always @(posedge vga_clk or negedge sys_rst_n) begin         
    if (!sys_rst_n)
        pixel_data <= 16'd0;                                  
    else begin 
        if((pixel_xpos >= 0) && (pixel_xpos <= (H_DISP/5)*1))                                              
            pixel_data <= WHITE;                               
        else if((pixel_xpos >= (H_DISP/5)*1) && (pixel_xpos < (H_DISP/5)*2))
            pixel_data <= BLACK;  
        else if((pixel_xpos >= (H_DISP/5)*2) && (pixel_xpos < (H_DISP/5)*3))
            pixel_data <= RED;  
        else if((pixel_xpos >= (H_DISP/5)*3) && (pixel_xpos < (H_DISP/5)*4))
            pixel_data <= GREEN;  
        else 
            pixel_data <= BLUE;  
    end
end

各个颜色的RGB565数据代码如下

localparam WHITE  = 16'b11111_111111_11111;     //RGB565 白色
localparam BLACK  = 16'b00000_000000_00000;     //RGB565 黑色
localparam RED    = 16'b11111_000000_00000;     //RGB565 红色
localparam GREEN  = 16'b00000_111111_00000;     //RGB565 绿色
localparam BLUE   = 16'b00000_000000_11111;     //RGB565 蓝色

3.VGA方块移动实验
在这里插入图片描述
时钟分频模块和VGA驱动模块同上一个实验,下面来学习一下具体的VGA显示模块Verilog代码块内容。

//reg define
reg [ 9:0] block_x;                             //方块左上角横坐标
reg [ 9:0] block_y;                             //方块左上角纵坐标
reg [21:0] div_cnt;                             //时钟分频计数器
reg        h_direct;                            //方块水平移动方向,1:右移,0:左移
reg        v_direct;                            //方块竖直移动方向,1:向下,0:向上

这个是模块内部定义的参数,方块左上角的横纵坐标表示方块的位置,时钟分频计数器用于控制方块每次移动的的间隔时间,例如,需要每10ms移动一次,则需要计数250000次(时钟频率为25Mhz),h_direct和v_direct用于控制移动的水平和竖直的方向。

	//当方块移动到边界时,改变移动方向
always @(posedge vga_clk or negedge sys_rst_n) begin         
    if (!sys_rst_n) begin
        h_direct <= 1'b1;                       //方块初始水平向右移动
        v_direct <= 1'b1;                       //方块初始竖直向下移动
    end
    else begin
        if(block_x == SIDE_W - 1'b1)            //到达左边界时,水平向右
            h_direct <= 1'b1;               
        else                                    //到达右边界时,水平向左
        if(block_x == H_DISP - SIDE_W - BLOCK_W)
            h_direct <= 1'b0;               
        else
            h_direct <= h_direct;
            
        if(block_y == SIDE_W - 1'b1)            //到达上边界时,竖直向下
            v_direct <= 1'b1;                
        else                                    //到达下边界时,竖直向上
        if(block_y == V_DISP - SIDE_W - BLOCK_W)
            v_direct <= 1'b0;               
        else
            v_direct <= v_direct;
    end
end

根据左上角坐标的不同值,判断是否到达边界,当没有在边界时保持原方向

//根据方块移动方向,改变其纵横坐标
always @(posedge vga_clk or negedge sys_rst_n) begin         
    if (!sys_rst_n) begin
        block_x <= 22'd100;                     //方块初始位置横坐标
        block_y <= 22'd100;                     //方块初始位置纵坐标
    end
    else if(move_en) begin
        if(h_direct) 
            block_x <= block_x + 1'b1;          //方块向右移动
        else
            block_x <= block_x - 1'b1;          //方块向左移动
            
        if(v_direct) 
            block_y <= block_y + 1'b1;          //方块向下移动
        else
            block_y <= block_y - 1'b1;          //方块向上移动
    end
    else begin
        block_x <= block_x;
        block_y <= block_y;
    end
end

根据方块移动的方向,改变横纵坐标的值。

//给不同的区域绘制不同的颜色
always @(posedge vga_clk or negedge sys_rst_n) begin         
    if (!sys_rst_n) 
        pixel_data <= BLACK;
    else begin
        if((pixel_xpos < SIDE_W) || (pixel_xpos >= H_DISP - SIDE_W)
          || (pixel_ypos < SIDE_W) || (pixel_ypos >= V_DISP - SIDE_W))
            pixel_data <= BLUE;                 //绘制边框为蓝色
        else
        if((pixel_xpos >= block_x) && (pixel_xpos < block_x + BLOCK_W)
          && (pixel_ypos >= block_y) && (pixel_ypos < block_y + BLOCK_W))
            pixel_data <= BLACK;                //绘制方块为黑色
        else
            pixel_data <= WHITE;                //绘制背景为白色
    end
end

依据坐标,给不同的区域绘制不同的颜色,方块黑色,边框蓝色,背景为白色,如实验效果图所示。
以上便是VGA显示模块的的具体代码内容,本实验设计的巧妙之处便是用方块左上角一个点的坐标来追踪方块的移动,这种转化的思想值得借鉴。考虑是否可以由按键来控制h_direct和v_direct(本实验这两者一开始是默认了向右和向下的方向,由碰到边界来改变方向),只需要增加一些条件即可,这与贪吃蛇的游戏风格有点相似。

4.VGA图片显示实验
本实验是基于ROM(只读存储器)结构的,需要将图片转换为.mif文件,mif文件就是存储器初始化文件,即memory initialization file,用来配置RAM或ROM中的数据。

  • 下面介绍两种产生.mif文件的方法。

    ①利用Quartus自带的mif编辑器生成,其优点是对于小容量RAM可以快速方便的完成mif文件的编辑工作,不需要第三方软件的编辑,缺点是一旦数据量过大,必须要有充分的耐心。如图,根据需要填写每一个点的值
    在这里插入图片描述
    ②使用.mif转换器(有需要的可以私信我)
    在这里插入图片描述

  • 下面来看一下本次实验的系统框图和顶层模块

    在这里插入图片描述
    在这里插入图片描述
    可以看到本实验比前两次实验多了一个ROM模块,此模块为VGA显示模块内部定义的模块,ROM由Quartus自带的ip核来给出来,VGA显示模块根据VGA驱动模块提供的像素点坐标,从ROM中读出图片数据,并将图片数据输出给VGA驱动模块。下面介绍一下单端口ROM ip核的使用。
    在这里插入图片描述
    clock为时钟信号、rden为读使能信号(高有效)、address为地址信号,根据给出的地址,输出相应的数据,如果我们需要连续读取ROM内容,可使rden一直拉高。

  • 代码实现
    与前两个模块不同的是VGA显示模块,下面只介绍一下显示模块的代码

	//从ROM中读出的图像数据有效时,将其输出显示
assign pixel_data = rom_valid ? rom_data : BLACK; 

//当前像素点坐标位于图案显示区域内时,读ROM使能信号拉高
assign rom_rd_en = (pixel_xpos >= POS_X) && (pixel_xpos < POS_X + WIDTH)
                    && (pixel_ypos >= POS_Y) && (pixel_ypos < POS_Y + HEIGHT)
                     ? 1'b1 : 1'b0;
这是给输出的数据和rom读使能信号赋值,采用assign语句
//控制读地址
always @(posedge vga_clk or negedge sys_rst_n) begin         
    if (!sys_rst_n) begin
        rom_addr   <= 14'd0;
    end
    else if(rom_rd_en) begin
        if(rom_addr < TOTAL - 1'b1)
            rom_addr <= rom_addr + 1'b1;    //每次读ROM操作后,读地址加1
        else
            rom_addr <= 1'b0;               //读到ROM末地址后,从首地址重新开始读操作
    end
    else
        rom_addr <= rom_addr;
end

//从发出读使能到ROM输出有效数据存在一个时钟周期的延时
always @(posedge vga_clk or negedge sys_rst_n) begin         
    if (!sys_rst_n) 
        rom_valid <= 1'b0;
    else
        rom_valid <= rom_rd_en;
end

这两个always块是进行读地址控制和读使能信号的延迟(这个延迟可以自己细品一下),读控制中的total表示图片总的像素点(自己在转换器中设置),注意像素点的内存不能超过FPGA自带的内存量。以我自己的为例,我的是270kb的内存,那我可以选择100x100x16=160000bit<270x1024bit
在这里插入图片描述

pic_rom	pic_rom_inst(
	.clock   (vga_clk),
	.address (rom_addr),
	.rden    (rom_rd_en),
	.q       (rom_data)
	);

最后这个是ip核的例化
在这里插入图片描述
在选择ip核时,在File name中添加自己转换的.mif文件即可。

  • 10
    点赞
  • 112
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
基于FPGAVGA显示示波器是一种使用现场可编程门阵列(FPGA)芯片来实现图像显示的示波器。该示波器可以通过FPGA芯片进行信号的采样、处理和显示,从而将输入信号转化为可视化的波形图像。 首先,FPGA芯片可以通过高速的ADC(模数转换器)对输入的信号进行采样。采样的数据存储在FPGA的内部存储器中,可以实时地对输入信号进行数字化处理。 然后,FPGA芯片通过算法对采样数据进行处理。例如,可以使用离散傅里叶变换(DFT)或快速傅里叶变换(FFT)将时域信号转换为频域信号。通过这些处理,可以得到输入信号的频谱分析图。 最后,FPGA芯片将处理后的数据转换为适合VGA显示的格式,并输出到显示器上。VGA是一种常见的图像接口标准,可以将数字信号转换为可视化图像。通过FPGA芯片,示波器将处理后的数据转换为VGA信号,从而显示波形图像。 相比传统的示波器,基于FPGAVGA显示示波器具有更高的灵活性和可扩展性。FPGA芯片可以根据需要进行编程,可以实现不同的信号处理算法和显示效果。此外,FPGA芯片的并行计算能力可以提高示波器的实时性能和波形显示的精度。 总结起来,基于FPGAVGA显示示波器利用FPGA芯片进行信号的采样、处理和显示,将输入信号转化为可视化的波形图像。它具有高灵活性、可扩展性和实时性能优势,可以广泛应用于各种领域的信号分析和测试中。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值