前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >FPGA VGA显示协议

FPGA VGA显示协议

原创
作者头像
AnieaLanie
修改2022-06-07 11:38:20
1.2K0
修改2022-06-07 11:38:20
举报
文章被收录于专栏:铁子的专栏铁子的专栏

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
给场同步信号添加延时1ms

给场同步信号添加延时1ms,行同步信号添加延时1us:

给场同步信号添加延时1ms,行同步信号添加延时1us
给场同步信号添加延时1ms,行同步信号添加延时1us

参考

[1] 咸鱼FPGA.VGA协议 cnblogs

[2] Kevin.VGA显示器驱动

[3] 星河带悦流.FPGA实现VGA接口——保存图片至ROM/RAM显示 csdn

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • FPGA VGA显示协议
    • 协议原理简述
      • 程序实现
        • 时钟分频模块
        • VGA驱动模块
        • 写入图片,显示姓名
      • 若加入延时信号
        • 行消隐信号
        • 延时设计
      • 参考
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档