FPGA VGA显示协议

2022-06-07 11:38:20 浏览数 (1)

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

0 人点赞