FPGA——基于VGA协议显示彩条、图片、字符

这篇具有很好参考价值的文章主要介绍了FPGA——基于VGA协议显示彩条、图片、字符。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言

本篇博客主要是深入了解VGA协议,理解不同显示模式下的VGA控制时序参数(行频、场频、水平/垂直同步时钟周期、显示后沿/前沿等概念和计算方式)。并通过Verilog编程,实现以下VGA显示:1、屏幕上显示彩色条纹;2、显示自定义的汉字字符;3、输出一幅彩色图像。

一、VGA协议

VGA(Video Graphics Array)视频图形阵列是 IBM 于1987年提出的一个使用模拟信号的电脑显示标准。VGA具有分辨率高、显示速率快、颜色丰富等优点。VGA 接口不但是CRT 显示设备的标准接口,同样也是 LCD 液晶显示设备的标准接口,具有广泛的应用范围。
使用原理:显示器扫描方式分为逐行扫描和隔行扫描:逐行扫描是扫描从屏幕左上角一点开始,从左向右逐点扫描,每扫描完一行,电子束回到屏幕的左边下一行的起始位置,在这期间,每行结束时,用行同步信号进行同步;当扫描完所有的行,形成一帧,用场同步信号进行场同步,并使扫描回到屏幕左上方,开始下一帧。隔行扫描是指电子束扫描时每隔一行扫一线,完成一屏后在返回来扫描剩下的线,隔行扫描的显示器闪烁的厉害,会让使用者的眼睛疲劳。在此我们选择逐行扫描的方式。
VGA时序图:
fpga中 vga控制器是怎么控制不同区域显示不同颜色的?,fpga开发
而通过FPGA芯片驱动VGA显示,需要先产生模拟信号,这就要借助数模转换器D/A,利用D/A产生模拟信号,输出至VGA的RED、GREEN、BLUE基色数据线。另一种方法是利用电阻网络分流模拟D/A实现的。

二、VGA显示字符

1、取模

该部分需要使用汉字点阵,对汉字进行取模,与之前的OLED的汉字显示同理,可以参考:STM32完成基于SPI协议的0.96OLED屏显
使用取模软件提取汉字点阵:
fpga中 vga控制器是怎么控制不同区域显示不同颜色的?,fpga开发
说明:这里使用点阵显示时,汉字是16x16点阵,而符号或是数字或是英文字母是32x16点阵。
点阵拼接时,用第一个字的前4位拼上下一个字的前4位…一直到最后一个字,它们构成VGA显示的一行。

2、代码实现

module VGA_test(
OSC_50,     //原CLK2_50时钟信号
VGA_CLK,    //VGA自时钟
VGA_HS,     //行同步信号
VGA_VS,     //场同步信号
VGA_BLANK,  //复合空白信号控制信号  当BLANK为低电平时模拟视频输出消隐电平,此时从R9~R0,G9~G0,B9~B0输入的所有数据被忽略
VGA_SYNC,   //符合同步控制信号      行时序和场时序都要产生同步脉冲
VGA_R,      //VGA绿色
VGA_B,      //VGA蓝色
VGA_G);     //VGA绿色
 input OSC_50;     //外部时钟信号CLK2_50
 output VGA_CLK,VGA_HS,VGA_VS,VGA_BLANK,VGA_SYNC;
 output [7:0] VGA_R,VGA_B,VGA_G;
 parameter H_FRONT = 16;     //行同步前沿信号周期长
 parameter H_SYNC = 96;      //行同步信号周期长
 parameter H_BACK = 48;      //行同步后沿信号周期长
 parameter H_ACT = 640;      //行显示周期长
 parameter H_BLANK = H_FRONT+H_SYNC+H_BACK;        //行空白信号总周期长
 parameter H_TOTAL = H_FRONT+H_SYNC+H_BACK+H_ACT;  //行总周期长耗时
 parameter V_FRONT = 11;     //场同步前沿信号周期长
 parameter V_SYNC = 2;       //场同步信号周期长
 parameter V_BACK = 31;      //场同步后沿信号周期长
 parameter V_ACT = 480;      //场显示周期长
 parameter V_BLANK = V_FRONT+V_SYNC+V_BACK;        //场空白信号总周期长
 parameter V_TOTAL = V_FRONT+V_SYNC+V_BACK+V_ACT;  //场总周期长耗时
 reg [10:0] H_Cont;        //行周期计数器
 reg [10:0] V_Cont;        //场周期计数器
 wire [7:0] VGA_R;         //VGA红色控制线
 wire [7:0] VGA_G;         //VGA绿色控制线
 wire [7:0] VGA_B;         //VGA蓝色控制线
 reg VGA_HS;
 reg VGA_VS;
 reg [10:0] X;             //当前行第几个像素点
 reg [10:0] Y;             //当前场第几行
 reg CLK_25;
 always@(posedge OSC_50)
    begin 
      CLK_25=~CLK_25;         //时钟
    end 
    assign VGA_SYNC = 1'b0;   //同步信号低电平
    assign VGA_BLANK = ~((H_Cont<H_BLANK)||(V_Cont<V_BLANK));  //当行计数器小于行空白总长或场计数器小于场空白总长时,空白信号低电平
    assign VGA_CLK = ~CLK_to_DAC;  //VGA时钟等于CLK_25取反
    assign CLK_to_DAC = CLK_25;
 always@(posedge CLK_to_DAC)
    begin
        if(H_Cont<H_TOTAL)           //如果行计数器小于行总时长
            H_Cont<=H_Cont+1'b1;      //行计数器+1
        else H_Cont<=0;              //否则行计数器清零
        if(H_Cont==H_FRONT-1)        //如果行计数器等于行前沿空白时间-1
            VGA_HS<=1'b0;             //行同步信号置0
        if(H_Cont==H_FRONT+H_SYNC-1) //如果行计数器等于行前沿+行同步-1
            VGA_HS<=1'b1;             //行同步信号置1
        if(H_Cont>=H_BLANK)          //如果行计数器大于等于行空白总时长
            X<=H_Cont-H_BLANK;        //X等于行计数器-行空白总时长   (X为当前行第几个像素点)
        else X<=0;                   //否则X为0
    end
 always@(posedge VGA_HS)
    begin
        if(V_Cont<V_TOTAL)           //如果场计数器小于行总时长
            V_Cont<=V_Cont+1'b1;      //场计数器+1
        else V_Cont<=0;              //否则场计数器清零
        if(V_Cont==V_FRONT-1)       //如果场计数器等于场前沿空白时间-1
            VGA_VS<=1'b0;             //场同步信号置0
        if(V_Cont==V_FRONT+V_SYNC-1) //如果场计数器等于行前沿+场同步-1
            VGA_VS<=1'b1;             //场同步信号置1
        if(V_Cont>=V_BLANK)          //如果场计数器大于等于场空白总时长
            Y<=V_Cont-V_BLANK;        //Y等于场计数器-场空白总时长    (Y为当前场第几行)  
        else Y<=0;                   //否则Y为0
    end
    reg valid_yr;
 always@(posedge CLK_to_DAC)
    if(V_Cont == 10'd32)         //场计数器=32时
        valid_yr<=1'b1;           //行输入激活
    else if(V_Cont==10'd512)     //场计数器=512时
        valid_yr<=1'b0;           //行输入冻结
    wire valid_y=valid_yr;       //连线   
    reg valid_r;            
 always@(posedge CLK_to_DAC)   
    if((H_Cont == 10'd32)&&valid_y)     //行计数器=32时
        valid_r<=1'b1;                   //像素输入激活
    else if((H_Cont==10'd512)&&valid_y) //行计数器=512时 
        valid_r<=1'b0;                   //像素输入冻结
    wire valid = valid_r;               //连线
    wire[10:0] x_dis;     //像素显示控制信号
    wire[10:0] y_dis;     //行显示控制信号
    assign x_dis=X;       //连线X
    assign y_dis=Y;       //连线Y
        parameter

    char_line00=240'h010010400000000000000000000000000000000000000000000000000000,
    char_line01=240'h010010400000000000000000000000000000000000000000000000000000,
    char_line02=240'h7FFCFE780000000000000000000000000000000000000000000000000000,
    char_line03=240'h03801088000007F00FE000800FE007E01FFC07E007F007E00FE000800080,
    char_line04=240'h05407C100000081830180780301818183008181808181818301807800780,
    char_line05=240'h092011FC0000100038180180300C381C2010381C1000381C381801800180,
    char_line06=240'h3118FE240000300000180180700C300C0020300C3000300C001801800180,
    char_line07=240'hC10600247FFE37F000600180301C300C0040300C37F0300C006001800180,
    char_line08=240'h0FE07DFE0000380C01F00180382C300C0080300C380C300C01F001800180,
    char_line09=240'h004044240000300C001801800FCC300C0180300C300C300C001801800180,
    char_line0a=240'h00807C240000300C000C0180001C300C0300300C300C300C000C01800180,
    char_line0b=240'hFFFE45FC0000300C380C01800018381803003818300C3818380C01800180,
    char_line0c=240'h01007C24000018183018018038301C1003801C1018181C10301801800180,
    char_line0d=240'h01004420000007E00FE00FF80FC007E0030007E007E007E00FE00FF80FF8,
    char_line0e=240'h050054A00000000000000000000000000000000000000000000000000000,
    char_line0f=240'h020048400000000000000000000000000000000000000000000000000000;

    reg[7:0] char_bit;
    always@(posedge CLK_to_DAC)
        if(X==10'd180)char_bit<=9'd240;   //当显示到144像素时准备开始输出图像数据
        else if(X>10'd180&&X<10'd420)     //左边距屏幕144像素到416像素时    416=144+272(图像宽度)
            char_bit<=char_bit-1'b1;       //倒着输出图像信息
            
    reg[29:0] vga_rgb;                //定义颜色缓存
    always@(posedge CLK_to_DAC) 
        if(X>10'd180&&X<10'd420)    //X控制图像的横向显示边界:左边距屏幕左边144像素  右边界距屏幕左边界416像素
            begin case(Y)            //Y控制图像的纵向显示边界:从距离屏幕顶部160像素开始显示第一行数据
                10'd200:
                if(char_line00[char_bit])vga_rgb<=30'b1111111111_0000000000_0000000000;  //如果该行有数据 则颜色为红色
                else vga_rgb<=30'b0000000000_0000000000_0000000000;                      //否则为黑色
                10'd201:
                if(char_line01[char_bit])vga_rgb<=30'b1111111111_0000000000_0000000000;
                else vga_rgb<=30'b0000000000_0000000000_0000000000;
                10'd202:
                if(char_line02[char_bit])vga_rgb<=30'b1111111111_0000000000_0000000000;
                else vga_rgb<=30'b0000000000_0000000000_0000000000;
                10'd203:
                if(char_line03[char_bit])vga_rgb<=30'b1111111111_0000000000_0000000000;
                else vga_rgb<=30'b0000000000_0000000000_0000000000;
                10'd204:
                if(char_line04[char_bit])vga_rgb<=30'b1111111111_0000000000_0000000000;
                else vga_rgb<=30'b0000000000_0000000000_0000000000; 
                10'd205:
                if(char_line05[char_bit])vga_rgb<=30'b1111111111_0000000000_0000000000;
                else vga_rgb<=30'b0000000000_0000000000_0000000000;
                10'd206:
                if(char_line06[char_bit])vga_rgb<=30'b1111111111_0000000000_0000000000;
                else vga_rgb<=30'b0000000000_0000000000_0000000000; 
                10'd207:
                if(char_line07[char_bit])vga_rgb<=30'b1111111111_0000000000_0000000000;
                else vga_rgb<=30'b0000000000_0000000000_0000000000;
                10'd208:
                if(char_line08[char_bit])vga_rgb<=30'b1111111111_0000000000_0000000000;
                else vga_rgb<=30'b0000000000_0000000000_0000000000; 
                10'd209:
                if(char_line09[char_bit])vga_rgb<=30'b1111111111_0000000000_0000000000;
                else vga_rgb<=30'b0000000000_0000000000_0000000000;
                10'd210:
                if(char_line0a[char_bit])vga_rgb<=30'b1111111111_0000000000_0000000000;
                else vga_rgb<=30'b0000000000_0000000000_0000000000;
                10'd211:
                if(char_line0b[char_bit])vga_rgb<=30'b1111111111_0000000000_0000000000;
                else vga_rgb<=30'b0000000000_0000000000_0000000000;
                10'd212:
                if(char_line0c[char_bit])vga_rgb<=30'b1111111111_0000000000_0000000000;
                else vga_rgb<=30'b0000000000_0000000000_0000000000;
                10'd213:
                if(char_line0d[char_bit])vga_rgb<=30'b1111111111_0000000000_0000000000;
                else vga_rgb<=30'b0000000000_0000000000_0000000000;
                10'd214:
                if(char_line0e[char_bit])vga_rgb<=30'b1111111111_0000000000_0000000000;
                else vga_rgb<=30'b0000000000_0000000000_0000000000;
                10'd215:
                if(char_line0f[char_bit])vga_rgb<=30'b1111111111_0000000000_0000000000;
                else vga_rgb<=30'b0000000000_0000000000_0000000000;
                default:vga_rgb<=30'h0000000000;   //默认颜色黑色
            endcase 
        end
    else vga_rgb<=30'h000000000;             //否则黑色
    assign VGA_R=vga_rgb[23:16];
    assign VGA_G=vga_rgb[15:8];
    assign VGA_B=vga_rgb[7:0];
endmodule

三、VGA显示彩色条纹

采用模块化设计:
VGA驱动模块

`define vga_640_480

`include "vga_para.v"

module vga_ctrl(
    input                 clk         ,//时钟信号 //25.2MHZ
    input                 rst_n       ,//复位信号
    input         [23:0]  data_disp   ,

    output  reg   [10:0]  h_addr      ,//数据有效显示区域行地址
    output  reg   [10:0]  v_addr      ,//数据有效显示区域场地址

    output  reg           vsync       ,
    output  reg           hsync       ,

    output  reg   [7 :0]  vga_r       ,
    output  reg   [7 :0]  vga_b       ,
    output  reg   [7 :0]  vga_g       ,
    output  wire          vga_blk     ,
    output  wire          vga_sync    ,
    output  reg           vga_clk      //25.2MHZ
);

//参数定义
    parameter   H_SYNC_START = 1,
                H_SYNC_STOP  = `H_Sync_Time ,
                H_DATA_START = `H_Sync_Time + `H_Back_Porch + `H_Left_Border,
                H_DATA_STOP  = `H_Sync_Time + `H_Back_Porch + `H_Left_Border + `H_Data_Time,

                V_SYNC_START = 1,
                V_SYNC_STOP  = `V_Sync_Time,
                V_DATA_START = `V_Sync_Time + `V_Back_Porch + `V_Top_Border,
                V_DATA_STOP  = `V_Sync_Time + `V_Back_Porch + `V_Top_Border + `V_Data_Time;

//信号定义
    reg     [11:0]  cnt_h_addr  ;//行地址计数器
    wire            add_h_addr  ;
    wire            end_h_addr  ;

    reg     [11:0]  cnt_v_addr  ;//长地址计数器
    wire            add_v_addr  ;
    wire            end_v_addr  ;

    assign vga_sync = 1'b0;

    assign vga_blk = ~((cnt_h_addr<`H_Front_Porch + `H_Sync_Time + `H_Back_Porch)||(cnt_v_addr<`V_Front_Porch + `V_Sync_Time + `V_Back_Porch)); 

    always@(posedge vga_clk or negedge rst_n)begin
        if(!rst_n)begin
            cnt_h_addr <= 12'd0;
        end
        else if(add_h_addr)begin
            if(end_h_addr)begin
                cnt_h_addr <= 12'd0;
            end
            else begin
                cnt_h_addr <= cnt_h_addr + 12'd1;
            end
        end
        else begin
            cnt_h_addr <= 12'd0;
        end
    end

    assign add_h_addr = 1'b1;
    assign end_h_addr = add_h_addr && cnt_h_addr == `H_Total_Time - 1;

    always@(posedge vga_clk or negedge rst_n)begin
        if(!rst_n)begin
            cnt_v_addr <= 12'd0;
        end
        else if(add_v_addr)begin
            if(end_v_addr)begin
                cnt_v_addr <= 12'd0;
            end
            else begin
                cnt_v_addr <= cnt_v_addr + 12'd1;
            end
        end
        else begin
            cnt_v_addr <= cnt_v_addr;
        end
    end

    assign add_v_addr = end_h_addr;
    assign end_v_addr = add_v_addr && cnt_v_addr == `V_Total_Time - 1;

    //行场同步信号
    always@(posedge vga_clk or negedge rst_n)begin
        if(!rst_n)begin
            hsync <= 1'b1;
        end
        else if(cnt_h_addr == H_SYNC_START - 1)begin
            hsync <= 1'b0;
        end
        else if(cnt_h_addr == H_SYNC_STOP - 1)begin
            hsync <= 1'b1;
        end
        else begin
            hsync <= hsync;
        end
    end

    always@(posedge vga_clk or negedge rst_n)begin
        if(!rst_n)begin
            vsync <= 1'b1;
        end
        else if(cnt_v_addr == V_SYNC_START - 1)begin
            vsync <= 1'b0;
        end
        else if(cnt_v_addr == V_SYNC_STOP - 1)begin
            vsync <= 1'b1;
        end
        else begin
            vsync <= vsync;
        end
    end

    always@(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            vga_clk =0;
        end
        else begin
            vga_clk = ~vga_clk;
        end
    end 

    //数据有效显示区域定义
    always@(posedge vga_clk or negedge rst_n)begin
        if(!rst_n)begin
            h_addr <= 11'd0;
        end
        else if((cnt_h_addr >= H_DATA_START - 1) &&( cnt_h_addr <= H_DATA_STOP - 1))begin
            h_addr <= cnt_h_addr - H_DATA_START - 1;
        end
        else begin
            h_addr <= 11'd0;
        end
    end

    always@(posedge vga_clk or negedge rst_n)begin
        if(!rst_n)begin
            v_addr <= 11'd0;
        end
        else if((cnt_v_addr >= V_DATA_START - 1) && (cnt_v_addr <= V_DATA_STOP - 1))begin
            v_addr <= cnt_v_addr - V_DATA_START -1;
        end
        else begin
            v_addr <= 11'd0;
        end
    end

    //显示数据
    always@(posedge vga_clk or negedge rst_n)begin
        if(!rst_n)begin
            vga_r <= 8'b0;
            vga_g <= 8'b0;
            vga_b <= 8'b0;
        end
        else if((cnt_h_addr >= H_DATA_START - 1) &&( cnt_h_addr <= H_DATA_STOP - 1) 
                && (cnt_v_addr >= V_DATA_START - 1) && (cnt_v_addr <= V_DATA_STOP - 1))begin
            vga_r <= data_disp[23:16];
            vga_g <= data_disp[15: 8];
            vga_b <= data_disp[7 : 0];
        end
        else begin
            vga_r <= 8'b0;
            vga_g <= 8'b0;
            vga_b <= 8'b0;
        end
    end

endmodule

生成数据:

module data_gen(
    input                   clk     ,//时钟信号
    input                   rst_n   ,//复位信号

    input       [10:0]      h_addr  ,//数据有效显示区域地址
    input       [10:0]      v_addr  ,//数据有效显示区域地址
    
    output  reg [23:0]      data_disp        
);
//参数定义
    parameter   BLACK       = 24'h000000,
                RED         = 24'hFF0000,
                GREEN       = 24'h00FF00,
                BLUE        = 24'h0000FF,
                YELLOW      = 24'hFFFF00,
                SKY_BULE    = 24'h00FFFF,
                PURPLE      = 24'hFF00FF,
                GREY        = 24'hC0C0C0,
                WIGHT       = 24'hFFFFFF;

    always@(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            data_disp <= BLACK;
        end
        else begin
            case(h_addr)
                0  : data_disp <= RED;
                80 : data_disp <= GREEN;
                160: data_disp <= BLUE;
                240: data_disp <= YELLOW;
                320: data_disp <= SKY_BULE;
                400: data_disp <= PURPLE;
                480: data_disp <= GREY;
                560: data_disp <= WIGHT;
                default:data_disp <= data_disp;
            endcase
        end
    end


endmodule

顶层文件:

module vga_top(
    input                  clk         ,//时钟信号
    input                  rst_n       ,//复位信号
  
    output  wire           vsync       ,
    output  wire           hsync       ,
    output  wire   [7 :0]  vga_r       ,
    output  wire   [7 :0]  vga_b       ,
    output  wire   [7 :0]  vga_g       ,
    output                 vga_blk     ,
    output  wire           vga_sync    ,
    output                 vga_clk                  
);
    wire     [23:0]      data_disp   ;

    wire     [10:0]      h_addr      ;
    wire     [10:0]      v_addr      ;


data_gen u_data_gen(
    .clk        (vga_clk    ),//时钟信号
    .rst_n      (rst_n      ),//复位信号
    
    .h_addr     (h_addr     ),//数据有效显示区域地址
    .v_addr     (v_addr     ),//数据有效显示区域地址
        
    .data_disp  (data_disp  )        
);

vga_ctrl u_vga_ctrl(
    .clk         (clk       ),//时钟信号 25.2MHZ
    .rst_n       (rst_n     ),//复位信号
    .data_disp   (data_disp ),

    .h_addr      (h_addr    ),//数据有效显示区域行地址
    .v_addr      (v_addr    ),//数据有效显示区域场地址

    .vsync       (vsync     ),
    .hsync       (hsync     ),

    .vga_r       (vga_r     ),
    .vga_b       (vga_b     ),
    .vga_g       (vga_g     ),
    .vga_blk     (vga_blk   ),
    .vga_sync    (vga_sync  ),
    .vga_clk     (vga_clk   )
);

endmodule

运行效果:
fpga中 vga控制器是怎么控制不同区域显示不同颜色的?,fpga开发

四、VGA显示彩色图片

由于VGA的驱动程序显示的格式为RGB565,因此我们需要将图片转化为ROM可以存储的格式,然后VGA驱动程序从ROM中读取数据,输出到VGA显示屏显示。使用软件将bmp格式图片转换为hex文件。
fpga中 vga控制器是怎么控制不同区域显示不同颜色的?,fpga开发
建立Quartus工程,将生成的文件保存在ROM中。

取出数据:

module data_drive (
    input			wire						vga_clk,
    input			wire						rst_n,
    input			wire		[ 11:0 ]		addr_h,
    input			wire		[ 11:0 ]		addr_v,
    output			reg		    [ 15:0 ]		rgb_data
);

localparam	black  = 16'd0;

parameter	height = 48; // 图片高度
parameter	width  = 48; // 图片宽度

reg			[ 13:0 ]		rom_address				; // ROM地址
wire		[ 15:0 ]		rom_data				; // 图片数据

wire						flag_enable_out2			; // 图片有效区域
wire						flag_clear_rom_address		; // 地址清零
wire						flag_begin_h			    ; // 图片显示行
wire						flag_begin_v			    ; // 图片显示列


always @( posedge vga_clk or negedge rst_n) begin
    if(!rst_n)begin
        rgb_data = black;
    end
    else if ( flag_enable_out2 ) begin
        rgb_data = rom_data;
    end
    else begin
        rgb_data = black;
    end
end

//ROM地址计数器
always @( posedge vga_clk or negedge rst_n ) begin
    if ( !rst_n ) begin
        rom_address <= 0;
    end
    else if ( flag_clear_rom_address ) begin //计数满清零
        rom_address <= 0;
    end
        else if ( flag_enable_out2 ) begin  //在有效区域内+1
        rom_address <= rom_address + 1;
        end
    else begin  //无效区域保持
        rom_address <= rom_address;
    end
end
assign flag_clear_rom_address = rom_address == height * width - 1;
assign flag_begin_h     = addr_h > ( ( 640 - width ) / 2 ) && addr_h < ( ( 640 - width ) / 2 ) + width + 1;
assign flag_begin_v     = addr_v > ( ( 480 - height )/2 ) && addr_v <( ( 480 - height )/2 ) + height + 1;
assign flag_enable_out2 = flag_begin_h && flag_begin_v;

//实例化ROM
rom	rom_inst (
.address    ( rom_address   ),
.clock      ( vga_clk       ),
.q          ( rom_data      )
);
endmodule

VGA驱动:

module vga_display_pic (
    input			wire					clk,      //系统时钟
    input			wire					rst_n,    //复位
    input			wire	[ 15:0 ]		rgb_data, //16位RGB对应值
    output			wire					vga_clk,  //vga时钟 25M
    output			reg						h_sync,   //行同步信号
    output			reg						v_sync,   //场同步信号
    output			reg		[ 11:0 ]		addr_h,   //行地址
    output			reg		[ 11:0 ]		addr_v,   //列地址
    output			wire	[ 4:0 ]		    rgb_r,    //红基色
    output			wire	[ 5:0 ]			rgb_g,    //绿基色
    output			wire	[ 4:0 ]			rgb_b     //蓝基色
);

// 640 * 480 60HZ
localparam	 H_FRONT = 16; // 行同步前沿信号周期长
localparam	 H_SYNC  = 96; // 行同步信号周期长
localparam	 H_BLACK = 48; // 行同步后沿信号周期长
localparam	 H_ACT   = 640; // 行显示周期长
localparam	 V_FRONT = 11; // 场同步前沿信号周期长
localparam	 V_SYNC  = 2; // 场同步信号周期长
localparam	 V_BLACK = 31; // 场同步后沿信号周期长
localparam	 V_ACT   = 480; // 场显示周期长

localparam	H_TOTAL = H_FRONT + H_SYNC + H_BLACK + H_ACT; // 行周期
localparam	V_TOTAL = V_FRONT + V_SYNC + V_BLACK + V_ACT; // 列周期

reg			[ 11:0 ]			cnt_h			; // 行计数器
reg			[ 11:0 ]			cnt_v			; // 场计数器
reg			[ 15:0 ]			rgb			; // 对应显示颜色值

// 对应计数器开始、结束、计数信号
wire							flag_enable_cnt_h			;
wire							flag_clear_cnt_h			;
wire							flag_enable_cnt_v			;
wire							flag_clear_cnt_v			;
wire							flag_add_cnt_v  			;
wire							valid_area      			;


// 25M时钟
wire							clk_25			;
// 50M时钟
wire							clk_50			;
wire                            locked          ;
//PLL
pll	pll_inst (
	.areset     ( ~rst_n    ),
	.inclk0     ( clk       ),
	.c0         ( clk_50    ), //50M
	.c1         ( clk_25    ), //25M
    .locked     (locked     )
	);
//根据不同分配率选择不同频率时钟
assign vga_clk = clk_25;


// 行计数
always @( posedge vga_clk or negedge rst_n ) begin
    if ( !rst_n ) begin
        cnt_h <= 0;
    end
    else if ( flag_enable_cnt_h ) begin
        if ( flag_clear_cnt_h ) begin
            cnt_h <= 0;
        end
        else begin
            cnt_h <= cnt_h + 1;
        end
    end
    else begin
        cnt_h <= 0;
    end
end
assign flag_enable_cnt_h = 1;
assign flag_clear_cnt_h  = cnt_h == H_TOTAL - 1;

// 行同步信号
always @( posedge vga_clk or negedge rst_n ) begin
    if ( !rst_n ) begin
        h_sync <= 0;
    end
    else if ( cnt_h == H_SYNC - 1 ) begin // 同步周期时为1
        h_sync <= 1;
    end
        else if ( flag_clear_cnt_h ) begin // 其余为0
        h_sync <= 0;
        end
    else begin
        h_sync <= h_sync;
    end
end

// 场计数
always @( posedge vga_clk or negedge rst_n ) begin
    if ( !rst_n ) begin
        cnt_v <= 0;
    end
    else if ( flag_enable_cnt_v ) begin
        if ( flag_clear_cnt_v ) begin
            cnt_v <= 0;
        end
        else if ( flag_add_cnt_v ) begin
            cnt_v <= cnt_v + 1;
        end
        else begin
            cnt_v <= cnt_v;
        end
    end
    else begin
        cnt_v <= 0;
    end
end
assign flag_enable_cnt_v = flag_enable_cnt_h;
assign flag_clear_cnt_v  = cnt_v == V_TOTAL - 1;
assign flag_add_cnt_v    = flag_clear_cnt_h;

// 场同步信号
always @( posedge vga_clk or negedge rst_n ) begin
    if ( !rst_n ) begin
        v_sync <= 0;
    end
    else if ( cnt_v == V_SYNC - 1 ) begin
        v_sync <= 1;
    end
        else if ( flag_clear_cnt_v ) begin
        v_sync <= 0;
        end
    else begin
        v_sync <= v_sync;
    end
end

// 对应有效区域行地址 1-640
always @( posedge vga_clk or negedge rst_n ) begin
    if ( !rst_n ) begin
        addr_h <= 0;
    end
    else if ( valid_area ) begin
        addr_h <= cnt_h - H_SYNC - H_BLACK + 1;
    end
    else begin
        addr_h <= 0;
    end
end
// 对应有效区域列地址 1-480
always @( posedge vga_clk or negedge rst_n ) begin
    if ( !rst_n ) begin
        addr_v <= 0;
    end
    else if ( valid_area ) begin
        addr_v <= cnt_v -V_SYNC - V_BLACK + 1;
    end
    else begin
        addr_v <= 0;
    end
end
// 有效显示区域
assign valid_area = cnt_h >= H_SYNC + H_BLACK && cnt_h <= H_SYNC + H_BLACK + H_ACT && cnt_v >= V_SYNC + V_BLACK && cnt_v <= V_SYNC + V_BLACK + V_ACT;


// 显示颜色
always @( posedge vga_clk or negedge rst_n ) begin
    if ( !rst_n ) begin
        rgb <= 16'h0;
    end
    else if ( valid_area ) begin
        rgb <= rgb_data;
    end
    else begin
        rgb <= 16'b0;
    end
end
assign rgb_r = rgb[ 15:11 ];
assign rgb_g = rgb[ 10:5 ];
assign rgb_b = rgb[ 4:0 ];

endmodule

顶层模块:

module vga_top (
    input			wire						clk,
    input			wire						rst_n,
    output			wire						vga_clk,
    output			wire						h_sync,
    output			wire						v_sync,
    output			wire		[ 4:0 ]			rgb_r,
    output			wire		[ 5:0 ]			rgb_g,
    output			wire		[ 4:0 ]			rgb_b
);

wire		[ 11:0 ]		    addr_h              ;
wire		[ 11:0 ]		    addr_v              ;
wire		[ 15:0 ]			rgb_data			;

//模块例化
vga_display_pic (
    .clk        (clk        ),
    .rst_n      (rst_n      ),
    .rgb_data   (rgb_data   ),
    .vga_clk    (vga_clk    ),
    .h_sync     (h_sync     ),
    .v_sync     (v_sync     ),
    .addr_h     (addr_h     ),
    .addr_v     (addr_v     ),
    .rgb_r      (rgb_r      ),
    .rgb_g      (rgb_g      ),
    .rgb_b      (rgb_b      ) 
);

//数据模块
data_drive u_data_drive(
.vga_clk        ( vga_clk   ),
.rst_n          ( rst_n     ),
.addr_h         ( addr_h    ),
.addr_v         ( addr_v    ),
.rgb_data       ( rgb_data  )
);

endmodule

显示效果:
fpga中 vga控制器是怎么控制不同区域显示不同颜色的?,fpga开发

五、总结

学习VGA显示协议,首先要明白他的原理,可以把显示屏看为N*M大小的一个坐标系,为每个坐标分配一个RGB三通道的值,也就是每个像素,行场信号扫描的速度很快,就能连成一副完整的图像。图片显示只需要将数据存进ROM里,需要时取出来就能显示了。

六、参考资料

https://blog.csdn.net/junseven164/article/details/125165822
https://blog.csdn.net/qq_47281915/article/details/125134764文章来源地址https://www.toymoban.com/news/detail-766632.html

到了这里,关于FPGA——基于VGA协议显示彩条、图片、字符的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • FPGA_工程_基于Rom的VGA图像显示

    一 工程框图 框图中,CLK_in,Vga_ctrl,Vga_pic模块已有,只需要对顶层模块进行修改,并将rom ip例化添加到Vga_pic模块的.v文件中,对Vga_pic的.v文件进行一定修改。 二 理论补充 显示图像的方法:                           使用matlab将图像格式转化为,.mif数据文件,再使用.m

    2024年02月20日
    浏览(28)
  • 基于FPGA:运动目标检测(VGA显示,原理图+源码+硬件选择)

        话不多说,先上视频看效果。 基于FPGA:运动目标检测 开发板Altera:EP4CE10F17C8 摄像头:OV5640 缓存数据:SDRAM 板子是自己制作的     根据帧差法的实现流程,设计的双端口SDRAM控制器,一侧读写端口用做帧缓存,另一个端口用来缓存视频流,如图所示。     在使用

    2024年02月04日
    浏览(27)
  • 【FPGA实验】基于DE2-115平台的VGA显示

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

    2024年02月03日
    浏览(23)
  • FPGA--OV7725摄像头采集与VGA显示实验--1--OV7725使用与驱动协议

    目录        前言 OV7725引脚及功能框图 参数指标 引脚 功能框图 SCCB时序及读写操作  SCCB时序特点 读写实现 OV7725寄存器常用配置参数              摄像头采集是图像处理的第一步,本章节分为多部分,旨在让大家学会如何使用OV7725采集图像,并且使用VGA协议显示出来。

    2023年04月08日
    浏览(46)
  • FPGA学习——VGA显示

    VGA(Video Graphics Array)是IBM在1987年随PS/2机⼀起推出的⼀种视频,具有分辨率⾼、显⽰速率快、颜⾊丰富等优点,在彩 ⾊显⽰器领域得到了⼴泛的应⽤。不⽀持热插拔,不⽀持⾳频传输。对于⼀些嵌⼊式VGA显⽰系统,可以在不使⽤VGA显⽰卡和计算机的 情况下,实现VGA图像的显⽰

    2024年02月04日
    浏览(29)
  • 基于Quartus Prime平台FPGA关于VGA显示的模块化设计:VGA八种单色屏1s切换显示、横条纹、竖条纹、棋盘格显示、显示模式按键可调、数码管显示单色屏序号

    VGA(Video Graphics Array)是一种显示接口标准,它最初由IBM于1987年推出。VGA协议定义了计算机视频输出信号的格式和特性。它主要用于连接计算机和显示器之间的传输,实现图像和视频的显示。 VGA协议支持最高分辨率为640x480像素,色彩深度为16位色(即65,536种颜色)。它使用模

    2024年02月03日
    浏览(38)
  • 【FPGA开发】SDRAM的读写VGA显示

            实验的目的旨在使用sdram作为中间媒介,在sdram上读写以在显示器上显示图像的目的。 1.2.1 SDRAM型号介绍 这里我们使用的 sdram为 Hynix 公司生产的型号为 HY57V281620F 的 芯片,其存储空间可以分为4Bank x 2M x16Bit ,其中 4Bank 指的是该芯片有 4 个bank区间, 2M 指的是每个 ba

    2024年01月17日
    浏览(34)
  • 【FPGA】通俗理解从VGA显示到HDMI显示

    注:大部分参考内容来自“征途Pro《FPGA Verilog开发实战指南——基于Altera EP4CE10》2021.7.10(上)”  贴个下载地址: 野火FPGA-Altera-EP4CE10征途开发板_核心板 — 野火产品资料下载中心 文档 hdmi显示器驱动设计与验证 — [野火]FPGA Verilog开发实战指南——基于Altera EP4CE10 征途Pro开

    2024年02月09日
    浏览(36)
  • 记一次基于FPGA的VGA显示四操作数计算器工程的开发流程——(1)从顶层设计说起

    首先值得说明的是,在这个项目几乎完成之际,笔者才愈发体会到了硬件思维和软件思维的云泥之别。不幸的是,在此项目的实现过程中,绝大部分代码的思维仍然是软件思维,因此该项目主要模块的设计部分可能并不能体现硬件操作的独到之处,不符合硬件工程师的基本设

    2024年02月04日
    浏览(30)
  • FPGA_简单工程_VGA显示驱动器

    一 理论 使用640*480@60显示模式,将数字信号转换位模拟信号,经由VGA进行显示。 使用3GM723,3路高清视频编码芯片。 3GM7123编码芯片:                                该芯片的主要功能是将RGB888的颜色数据转换成模拟的电压信号,然后进入到VGA接口的3个RGB接口。例如RGB8

    2024年02月20日
    浏览(30)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包