FPGA-VGA驱动

目录

前言

一、VGA的了解

二、VGA的参数

三、权电阻电路

四、代码实现部分

1、时钟分频

2、vga驱动模块

五、总结


前言

使用FPGA实现VGA显示器的驱动,仅以此篇博文记录自己的学习过程,如有错误,多多包涵。

一、VGA的了解

VGA(Video Graphics Array)视频图形阵列IBM于1987年提出的一个使用模拟信号的电脑显示标准。VGA接口即电脑采用VGA标准输出数据的专用接口。VGA接口共有15针,分成3排,每排5个孔,显卡上应用最为广泛的接口类型,绝大多数显卡都带有此种接口。它传输红、绿、蓝模拟信号以及同步信号(水平和垂直信号)。

   我们在用pga实现vga驱动的时候,只需要按照vga的时序生成R、G、B、行同步信号和场同步信号就可以实现vga的驱动,这里需要注意我们这里的rgb信号是模拟信号,但我们用FPGA生成的是数据信号,所以我们需要将数字信号转换成模拟信号。

二、VGA的参数


   那么既然我们要写VGA的驱动,就要对vga的参数非常清楚,那么我就简单介绍下vga的参数。以640x480@60这个为例,640代表我们每一行有个像素点,480代表每一页图像总共有480行,60代表每秒钟显示60帧图像,这三个要素其实决定了我们时钟的频率,这里需要注意我们需要用到的是行扫描周期、场扫描周期和频率,因为除了显示图像的时间之外,其他的扫描周期我们也要算在其中,所以f=800x525x60。每一行的图像显示完成后需要产生一个行同步信号(用来告诉换行),每一页图像显示完成完成之后需要产生一个场同步信号(告诉换页)。vga是使用扫描的模式来生成图像,扫描的方式是从左到右,从上到下,每页结束回到起始位置。

三、权电阻电路

  上面说过vga使用的rgb信号是模拟信号,所以需要将fpga产生的数字信号转换成模拟信号,那么这边有两种方法将数字信号转换成模拟信号,一种是使用专门的芯片,另一种就是权电阻信号,首先前者更加稳定,但后者成本更低而且电阻也比较简单。根据生成的数据长度,使用的数字信号也有不同的数字模式,比如数据位为16位,那么那么就是rgb565的数据模式,如果数据位为24,那么就是rgb888模式,如果数据位为8位,那么就是rgb332数据模式。这边使用的是rgb565的数据格式,就是16位数据来显示一个像素点,数据位越多,显示的图像就越细腻。144795

上面这幅图就是权电阻的转换电路,但起始我看到网上有的人使用的权电阻电路就用的500R

1K、2K、4K、8K的电阻,也可以实现,所以这边不做深究,只当做都可以,这边可以看到16位数据位的低5位是B,中间的6位是G,高五位是R,这就是RGB565的数据格式。 

四、代码实现部分

1、时钟分频

因为vga的每个显示模式下的时钟频率都不一样,在使用的时候我们需要生成相应的时钟频率,这里我们以640x480@60为例,它的时钟频率为25.175MHZ,这边可以使用两种方式来生成时钟频率,一是调用ip核,二是编写分频的rtl代码,这里选择后者,这里我的系统时钟频率为100MHZ,需生成的时钟频率取大概为25MHZ,所以进行四分频就可以了。

module vga_clk
(
    input   wire    sys_clk,
    input   wire    sys_rst,
    output  reg     clk
);
reg     [1:0]       cnt;

always@(posedge sys_clk)
begin
    if(sys_rst == 0)
        cnt <= 2'd0;
    else if(cnt == 2'd3)
        cnt <= 2'd0;
    else
        cnt <= cnt + 2'd1;
end
always(posedge sys_clk)
begin
    if(sys_rst == 0)
        clk <= 1'b0;
    else if(cnt == 2'd3)
        clk <= !clk;
    else
        clk <= clk;
end
endmodule

2、vga驱动模块

module      vga_ctrl
(
    input   wire            clk,
    input   wire            sys_rst,
    input   wire    [15:0]  pix_data,
    output  reg     [15:0]  rgb_565,
    output  reg             hsync,
    output  reg             vsync,
    output  reg     [9:0]   pix_x,
    output  reg     [9:0]   pix_y
);
parameter     CNT_HMAX = 10'd799,
parameter     CNT_VMAX = 10'd524
/
//定义两个计数器一个用于行同步的计数,一个用于场同步的计数
reg     [9:0]   cnt_h;
reg     [9:0]   cnt_v;
wire            valid;

always@(posedge clk)
begin
    if(sys_rst == 0)
        cnt_h <= 10'd0;
    else if(cnt_h == CNT_HMAX)
        cnt_h <= 10'd0;
    else
        cnt_h <= cnt_h + 10'd1;
end

always@(posedge clk)
begin
    if(sys_rst == 0)
        cnt_v <= 10'd0;
    else if((cnt_v == CNT_VMAX)&&(cnt_h == CNT_HMAX))
        cnt_v <=10'd0;
    else if(cnt_h == CNT_HMAX)
        cnt_v <= cnt_v + 10'd1;
    else
        cnt_v <= cnt_v;
end

//产生有效信息信号
assign valid = ((cnt_h >= 10'd144)
                &&(cnt_h <= 10'd784)
                &&(cnt_v >= 10'd35)
                &&(cnt_v <= 10'd515))
                ? 1'b1:1'b0;

//产生行同步信号和场同步信号
assign hsync = (cnt_h <= 10'd95)? 1'b1:1'b0;
assign vsync = (cnt_v <= 10'd1)? 1'd1:1'd0;
/
//产生x,y的坐标
assign pix_x = (valid == 1'b1)? cnt_h - 10'd144:10'd0;
assign pix_y = (valid == 1'b1)? cnt_v - 10'd35:10'd0;

//输出rgb565的数字信号
assign rgb_565 = (valid == 1'b1)? pix_data:16'b0;
endmodule

1、这边是对上面的代码进行解析,首先所有的操作都是严格按照vga的参数那张图来实现的,首先我们看看我们定义的两个计数器,这两个计数器是我们整个时序的重要的部分,我们从图中看到一个完整的行扫描周期为800个时间周期,一个完整的场扫描周期是525(这里需要注意完成一个完整的行扫描周期,我们的场计数器才会加一,这边是最容易混淆的地方)。

2、我们图像有效的信息是我们图中的行扫描周期的有效图像和场扫描周期的有效图像同时有效的时候,就是他们的公共部分才是我们图像的有效的地方。所以我们的valid的有效信号就通过判断这两个计数器的数值,当(144<=cnt_h<=784)&&(35<=cnt_v<=515)就判断为图像的有效区域,这时使能我们的valid信号。

3、当valid信号使能的时候,就将我们输入的数据信号px_data赋值给我们的rgb_565。

4、场同步信号和行同步信号,根据图中只有当同步时这两个信号才为高电平,所以我们对计数器进行判断,从而对场同步和行同步信号进行拉高。

5、pix_x和pix_y的信号,当valid信号有效时,对pix_x和pix_y信号进行计数,这边利用cnt_h和cnt_v减去前面的周期反而省了事。

五、总结

总而言之,一句话,看参数图再看代码,所有的都根据参数图得来的。

  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值