VGA显示彩条和图片(FPGA)

如愿

  • 一、VGA协议
  • 二、使用设备
  • 三、模块设计
  • 四、代码
  • 五、总结

一、VGA协议

  • VGA(Video Graphics Array)视频图形阵列是 IBM 于1987年提出的一个使用模拟信号的电脑显示标准。VGA具有分辨率高、显示速率快、颜色丰富等优点。VGA 接口不但是CRT 显示设备的标准接口,同样也是 LCD 液晶显示设备的标准接口,具有广泛的应用范围。
  • 使用原理:显示器扫描方式分为逐行扫描和隔行扫描:逐行扫描是扫描从屏幕左上角一点开始,从左向右逐点扫描,每扫描完一行,电子束回到屏幕的左边下一行的起始位置,在这期间,每行结束时,用行同步信号进行同步;当扫描完所有的行,形成一帧,用场同步信号进行场同步,并使扫描回到屏幕左上方,开始下一帧。隔行扫描是指电子束扫描时每隔一行扫一线,完成一屏后在返回来扫描剩下的线,隔行扫描的显示器闪烁的厉害,会让使用者的眼睛疲劳。在此我们选择逐行扫描的方式。
  • 详细的时序关系请移步

FPGA零基础学习:VGA协议驱动设计

二、使用设备

设备为DE2-115

  • TIPS:

具体实现方式请根据开发板来,就比如DE2-115这块板子,需要传输rgb三原色的数据和行场同步信号,也需要传输时钟、和消隐信号,且RBG是888位宽的,其他板子可能不一样,需要更改
VGA显示彩条和图片(FPGA)_第1张图片

三、模块设计

  • 模块分为时钟模块、数据产生模块、和vga控制模块,时钟模块产生所需时钟,但这里并不是根据分辨率自动设置,而是写死在了640*480 @ 60HZ,所以为25M,如果要写其他的,自行更改
  • 数据产生模块是根据vga控制模块传出来的位置来控制所显示的输出,再把所显示的数据输出回传到vga控制模块
  • vga控制模块就是输出vga协议的一些控制信号
    VGA显示彩条和图片(FPGA)_第2张图片

四、代码

若显示彩条,可以直接复制下面代码,把数据生成模块厉的rom实例化和图片输出注释就行,再把彩条输出的输出取消注释

若是显示图片,需要调用rom核,并且需要将图片转成bmp文件,再把bmp转为mif文件,然后在调用rom的时候将图片转为的mif文件设为初始化文件,这样就可以通过地址来读取rom里存储的图片数据,当判断条件成立时,就可以把图片显示到显示屏上了。

  • counter25M.v:
module counter25M(
  input   wire          clk           , //VGA时
  input   wire          rst_n         , //复位
  output  reg         clk_25M         
);

always @(posedge clk or negedge rst_n) begin
     if (!rst_n) begin
         clk_25M <= 1'b0     ; 
     end else  begin
         clk_25M <=  ~clk_25M;
     end 
end
endmodule
  • data_generate.v:
module data_generate(
 input   wire         clk             , //VGA时钟,640*480 60hz 25.2MHz
 input   wire         rst_n           , //复位

 input   wire  [10:0] h_addr           , //数据有效显示区域,行地址
 input   wire  [10:0] v_addr           , //数据有效显示区域,列地址
 output  reg  [23:0] data_display       //显示数据
);
wire [23:0] q_sig;//从ip核里取出的图片数据
reg  [15:0] address_sig;//取图片的地址
wire        pic_area;//是否在图片显示的区域
parameter PIC_WIDTH = 9'd200;//图片长
parameter PIC_HEIGH = 9'd200;//图片宽
parameter BALCK    = 24'h000000,
          RED      = 24'hFF0000,
          GREEN    = 24'h00FF00,
          BLUE     = 24'h0000FF,
          YELLOW   = 24'hFFFF00,
          SKY_BULE = 24'h00FFFF,
          PURPLE   = 24'hFF00FF,
          GRAY     = 24'hC0C0C0,
          WHITE    = 24'hFFFFFF;

//调用ip核,取数据
//存入的图片为512*512640*480
rom	rom_inst (
	.address ( address_sig ),
	.clock ( clk ),
	.q ( q_sig )
	);

assign pic_area = h_addr <= PIC_WIDTH && v_addr <= PIC_HEIGH;
//位置变化
always @(posedge clk or negedge rst_n) begin
     if (!rst_n) begin
      address_sig <= 16'd0;
     end else if ( address_sig == 40000 ) begin
      address_sig <= 16'd0;
     end else if ( pic_area ) begin//在显示区域里随着地址变化自动变换
      address_sig <= h_addr + v_addr * 200 ;
     end
     else begin //大于图片显示区域,将保持地址
      address_sig <= address_sig ;
     end
end


//状态输出,图片输出
always @(posedge clk or negedge rst_n) begin
     if (!rst_n) begin
        data_display <= WHITE   ; 
     end else begin
        case (pic_area)
            0   : data_display <=   RED           ;
            1   : data_display <=   q_sig         ;  
            default: data_display <= data_display ;
        endcase
     end
end

/* //状态输出,彩条输出
always @(posedge clk or negedge rst_n) begin
     if (!rst_n) begin
        data_display <= WHITE   ; 
     end else begin
        case (h_addr)
            0   : data_display <=   BALCK         ;  
            80  : data_display <=   RED           ;
            160 : data_display <=   GREEN         ;  
            240 : data_display <=   BLUE          ; 
            320 : data_display <=   YELLOW        ;   
            400 : data_display <=   SKY_BULE      ;     
            480 : data_display <=   PURPLE        ;   
            560 : data_display <=   GRAY          ; 
            default: data_display <= data_display ;
        endcase
     end
end */

endmodule
  • vga_contro.v
`define vga_640_480
`include "vga_param.v"



module vga_contro(
 input   wire         clk            , //VGA时钟,640*480 60hz 25.2MHz
 input   wire         rst_n          , //复位
 input   wire  [23:0] data_display   , //显示数据

 output  reg   [10:0] h_addr         , //数据有效显示区域,行地址,只有当有效的时候,
 output  reg   [10:0] v_addr         , //数据有效显示区域,列地址
 output  reg           disp_vld      , //表示输入的是否是有效数据,即为显示区域内的数据
 output  wire          v_sync        , //场同步信号,有效数据的条件之一
 output  wire          h_sync        , //行同步信号,有效数据的条件之一,只有当行和场的同步信号都有效之后,后面输入的才是有效数据
 output  reg   [7:0]   vga_r         , //红色输出
 output  reg   [7:0]   vga_g         , //绿色输出
 output  reg   [7:0]   vga_b         , //蓝色输出
 output  wire          vga_clk         //显示时钟,
);
parameter H_SYNC_STA = 1;//行同步开始
parameter H_SYNC_END = `H_Sync_Time;//行同步结束
parameter H_DATA_STA = `H_Sync_Time+`H_Right_Border + `H_Front_Porch;//行数据开始
parameter H_DATA_END = `H_Sync_Time+`H_Right_Border + `H_Front_Porch + `H_Data_Time;//行数据结束

parameter V_SYNC_STA = 1;//场同步开始
parameter V_SYNC_END = `V_Sync_Time;//场同步结束
parameter V_DATA_STA = `V_Sync_Time +`V_Bottom_Border + `V_Front_Porch;//场数据开始
parameter V_DATA_END = `V_Sync_Time +`V_Bottom_Border + `V_Front_Porch+ `V_Data_Time;//场数据结束

reg [(12-1):0] cnt_h; //行地址的计数
wire add_cnt_h      ; //行地址开始标志
wire end_cnt_h      ; //行地址结束标志

reg [(12-1):0] cnt_v ; //场地址的计数
wire add_cnt_v       ; //场地址开始标志
wire end_cnt_v       ; //场地址结束标志



always @(posedge clk or negedge rst_n) begin
   if (!rst_n) begin                        
         cnt_h <= 12'b0;                 
   end else if(add_cnt_h) begin          
       if (end_cnt_h) begin              
           cnt_h <= 12'b0;               
       end else begin                       
           cnt_h <= cnt_h + 12'b1;     
       end                                  
   end                                      
end

assign add_cnt_h = 1'd1;
assign end_cnt_h = add_cnt_h && cnt_h == `H_Total_Time - 1;


always @(posedge clk or negedge rst_n) begin
   if (!rst_n) begin                        
         cnt_v <= 12'b0;                 
   end else if(add_cnt_v) begin          
       if (end_cnt_v) begin              
           cnt_v <= 12'b0;               
       end else begin                       
           cnt_v <= cnt_v + 12'b1;     
       end                                  
   end                                      
end

assign add_cnt_v = end_cnt_h;
assign end_cnt_v = add_cnt_v && cnt_v == `V_Total_Time - 1;


//同步信号
assign  h_sync = (cnt_h > ( H_SYNC_STA - 1) && cnt_h < (H_SYNC_END - 1) );
assign  v_sync = (cnt_v >= ( V_SYNC_STA - 1) && cnt_v <= (V_SYNC_END - 1) );

assign vga_clk = ~clk;


//定义有效显示区域
always @(posedge clk or negedge rst_n) begin
     if (!rst_n) begin
        h_addr <=  11'd0; 
     end else if(cnt_h >= (H_DATA_STA - 1) && cnt_h <= (H_DATA_END - 1)) begin
        h_addr <=  cnt_h - (H_DATA_STA - 1);
     end else begin
        h_addr <=  11'd0;
     end
end

//定义有效显示区域
always @(posedge clk or negedge rst_n) begin
     if (!rst_n) begin
        v_addr <=  11'd0; 
     end else if(cnt_v >= (V_DATA_STA - 1) && cnt_v <= (V_DATA_END - 1)) begin
        v_addr <=  cnt_v - (V_DATA_STA - 1);
     end else begin
        v_addr <=  11'd0;
     end
end


//有效区域输出
always @(posedge clk or negedge rst_n) begin
     if (!rst_n) begin
        vga_r <= 8'd0  ;
        vga_g <= 8'd0  ;
        vga_b <= 8'd0  ; 
        disp_vld <= 1'b0;
        //数据有效时输出
     end else if(cnt_h >= (H_DATA_STA - 1) && cnt_h <= (H_DATA_END - 1) && cnt_v >= (V_DATA_STA - 1) && cnt_v <= (V_DATA_END - 1)) begin
        vga_r <= data_display[23-:8]  ;
        vga_g <= data_display[15-:8]  ;
        vga_b <= data_display[07-:8]  ; 
        disp_vld <= 1'b1 ;
     end else begin
        vga_r <= 8'd0 ;
        vga_r <= 8'd0 ;
        vga_r <= 8'd0 ; 
        disp_vld <= 1'b0 ;
     end
end


endmodule
  • vga_param.v:
//`define vga_480_272
`define vga_640_480
//`define vga_800_480
//`define vga_800_600
//`define vga_1024_600
//`define vga_1024_768
//`define vga_1280_720
//`define vga_1920_1080

`ifdef vga_480_272
      `define H_Right_Border  0
	`define H_Front_Porch   2
	`define H_Sync_Time     41
	`define H_Back_Porch    2
	`define H_Left_Border   0
	`define H_Data_Time     480 
	`define H_Total_Time    525
	
	`define V_Bottom_Border 0
	`define V_Front_Porch   2
	`define V_Sync_Time     10
	`define V_Back_Porch    2 
	`define V_Top_Border    0
	`define V_Data_Time     272
	`define V_Total_Time    286
		
`elsif vga_640_480
      `define H_Right_Border  8
	`define H_Front_Porch   8
	`define H_Sync_Time     96
	`define H_Back_Porch    40
	`define H_Left_Border   8
	`define H_Data_Time     640
      `define H_Total_Time    800
	           
      `define V_Bottom_Border  8
      `define V_Front_Porch   2
      `define V_Sync_Time     2
	`define V_Back_Porch    25
	`define V_Top_Border    8
      `define V_Data_Time     480
      `define V_Total_Time    525
		
`elsif vga_800_480
      `define H_Right_Border  0
      `define H_Front_Porch   40
      `define H_Sync_Time     128
      `define H_Back_Porch    88
      `define H_Left_Border   0
      `define H_Data_Time     800
      `define H_Total_Time    1056
              
	`define V_Bottom_Border 8 
	`define V_Front_Porch   2
	`define V_Sync_Time     2
	`define V_Back_Porch    25
	`define V_Top_Border    8
	`define V_Data_Time     480
	`define V_Total_Time    525
		
`elsif vga_800_600
      `define H_Right_Border  0 
      `define H_Front_Porch   40 
      `define H_Sync_Time     128
      `define H_Back_Porch    88
      `define H_Left_Border   0
      `define H_Data_Time     800
      `define H_Total_Time    1056 
              
      `define V_Bottom_Border 0
      `define V_Front_Porch   1
      `define V_Sync_Time     4
      `define V_Back_Porch    23
      `define V_Top_Border    0
      `define V_Data_Time     600
      `define V_Total_Time    628


`elsif vga_1024_600
      `define H_Right_Border   0
      `define H_Front_Porch    24
      `define H_Sync_Time      136
      `define H_Back_Porch     160
      `define H_Left_Border    0
      `define H_Data_Time      1024
      `define H_Total_Time     1344
              
      `define V_Bottom_Border  0
      `define V_Front_Porch    1
      `define V_Sync_Time      4
      `define V_Back_Porch     23
      `define V_Top_Border     0
      `define V_Data_Time      600
      `define V_Total_Time     628

`elsif vga_1024_768
      `define H_Right_Border   0
      `define H_Front_Porch    24
      `define H_Sync_Time      136
      `define H_Back_Porch     160
      `define H_Left_Border    0
      `define H_Data_Time      1024
      `define H_Total_Time     1344
              
      `define V_Bottom_Border  0
      `define V_Front_Porch    3
      `define V_Sync_Time      6
      `define V_Back_Porch     29
      `define V_Top_Border     0
      `define V_Data_Time      768
      `define V_Total_Time     806

`elsif vga_1280_720
      `define H_Right_Border   0
      `define H_Front_Porch    110
      `define H_Sync_Time      40
      `define H_Back_Porch     220
      `define H_Left_Border    0
      `define H_Data_Time      1280
      `define H_Total_Time     1650
              
      `define V_Bottom_Border  0
      `define V_Front_Porch    5
      `define V_Sync_Time      5
      `define V_Back_Porch     20
      `define V_Top_Border     0
      `define V_Data_Time      720
      `define V_Total_Time     750
		
`elsif vga_1920_1080 
     `define H_Right_Border  0 
     `define H_Front_Porch   88
     `define H_Sync_Time     44
     `define H_Back_Porch    148
     `define H_Left_Border   0
     `define H_Data_Time     1920
     `define H_Total_Time    2200
             
     `define V_Bottom_Border 0
     `define V_Front_Porch   4
     `define V_Sync_Time     5
     `define V_Back_Porch    36
     `define V_Top_Border    0
     `define V_Data_Time     1080
     `define V_Total_Time    1125
	 
`endif
  • vga_protocol_top.v
module vga_protocol_top(
 input   wire          clk           , //VGA时钟,640*480 60hz 25.2MHz
 input   wire          rst_n         , //复位
 output  wire          v_sync        , //场同步信号
 output  wire          h_sync        , //行同步信号
 output  wire   [7:0]  vga_r         , //红色输出
 output  wire          disp_vld       , //是否是显示的时候
 output  wire   [7:0]  vga_g         , //绿色输出
 output  wire   [7:0]  vga_b         , //蓝色输出
 output  wire          vga_clk         //显示时钟,
);

wire  [23:0] data_display;
wire  [10:0] h_addr      ;
wire  [10:0] v_addr      ;
counter25M u_counter25M(
    .clk      ( clk      ),
    .rst_n    ( rst_n    ),
    .clk_25M  ( clk_25M  )
);

vga_contro u_vga_contro(
    .clk           ( clk_25M       ),
    .rst_n         ( rst_n         ),
    .data_display  ( data_display  ),
    .h_addr        ( h_addr        ),
    .v_addr        ( v_addr        ),
    .disp_vld      (disp_vld       ),
    .v_sync        ( v_sync        ),
    .h_sync        ( h_sync        ),
    .vga_r         ( vga_r         ),
    .vga_g         ( vga_g         ),
    .vga_b         ( vga_b         ),
    .vga_clk       ( vga_clk       )
);


data_generate u_data_generate(
    .clk     ( clk_25M ),
    .rst_n   ( rst_n   ),
    .h_addr  ( h_addr  ),
    .v_addr  ( v_addr  ),
    .data_display  ( data_display  )
);

endmodule
  • gitee : vga_protocol
  • 彩带
    VGA显示彩条和图片(FPGA)_第3张图片
  • 图片
    VGA显示彩条和图片(FPGA)_第4张图片

五、总结

  • vga协议初看很复杂,但理解了之后其实并不复杂,行场同步信号就是每一行或者每一列开始的标志,而消隐信号的作用就是表明后面的数据为有效数据,看懂时序之后实现其实不难,数据填充就是和点阵一模一样了。

你可能感兴趣的:(FPGA,fpga开发)