VGA驱动之-显示例程(最简单)

 

目录

Verilog设计

1.接口设计

2. 时序参数设置

3. 内部信号

4. PLL(VGA_CLK)

5.行计数器

6. 行同步信号

7.列计数器

8.显示方块

显示彩条

 VGA图像数据选择输出

按键控制程序


 

 


        学习的过程都是由浅入深,多数教程都是选择从某一点引入,逐步拓展,因此这里也是从VGA的显示出发开始学习(最简单了)。

      该VGA显示例程已经黑金AX309开发板(Spartan6)验证。采用的常用的RGB565格式进行VGA显示,5bit的R,6bit的G,5bitd B。共16bit,可标识65536色。

      该显示例程只是用来说明VGA的显示原理,因此并没有视频数据源(摄像头等),而是内部产生一些数据进行测试使用。FPGA上的VGA处理的是数字信号,而VGA显示器是模拟信号,因此之间需要一个DAC的过程,会有一个ADV/GM7123芯片来实现。AX309开发板上似乎没有用,而是用电阻匹配网络来实现RGB数字信号到模拟信号的转换。


Verilog设计


1.接口设计

      因为这里没有视频源,因此该模块并没有数据的输入,而数据就是RGB三种分量以及同步信号。为了实现多种显示模式的切换还需要一个按键开关。

`timescale 1ns / 1ps
//
// Module Name:    vga_test 
//
//
module vga_test(
                        input rst_n,
			input fpga_gclk,
			output vga_hs,
			output vga_vs,
			output [4:0] vga_r,
			output [5:0] vga_g,
			output [4:0] vga_b,
			input key1                        //按键key1
    );

2. 时序参数设置


为了实现分辨率可配置,可以 将分辨率参数化:

//-----------------------------------------------------------//
// 水平扫描参数的设定1024*768 60Hz VGA
//-----------------------------------------------------------//
parameter LinePeriod =1344;            //行周期数
parameter H_SyncPulse=136;             //行同步脉冲(Sync a)
parameter H_BackPorch=160;             //显示后沿(Back porch b)
parameter H_ActivePix=1024;            //显示时序段(Display interval c)
parameter H_FrontPorch=24;             //显示前沿(Front porch d)
parameter Hde_start=296;
parameter Hde_end=1320;

//-----------------------------------------------------------//
// 垂直扫描参数的设定1024*768 60Hz VGA
//-----------------------------------------------------------//
parameter FramePeriod =806;           //列周期数
parameter V_SyncPulse=6;              //列同步脉冲(Sync o)
parameter V_BackPorch=29;             //显示后沿(Back porch p)
parameter V_ActivePix=768;            //显示时序段(Display interval q)
parameter V_FrontPorch=3;             //显示前沿(Front porch r)
parameter Vde_start=35;
parameter Vde_end=803;


//-----------------------------------------------------------//
// 水平扫描参数的设定800*600 VGA
//-----------------------------------------------------------//
//parameter LinePeriod =1056;           //行周期数
//parameter H_SyncPulse=128;            //行同步脉冲(Sync a)
//parameter H_BackPorch=88;             //显示后沿(Back porch b)
//parameter H_ActivePix=800;            //显示时序段(Display interval c)
//parameter H_FrontPorch=40;            //显示前沿(Front porch d)

//-----------------------------------------------------------//
// 垂直扫描参数的设定800*600 VGA
//-----------------------------------------------------------//
//parameter FramePeriod =628;           //列周期数
//parameter V_SyncPulse=4;              //列同步脉冲(Sync o)
//parameter V_BackPorch=23;             //显示后沿(Back porch p)
//parameter V_ActivePix=600;            //显示时序段(Display interval q)
//parameter V_FrontPorch=1;             //显示前沿(Front porch r)


//-----------------------------------------------------------//
// 水平扫描参数的设定640*480 VGA
//-----------------------------------------------------------//
//parameter LinePeriod =800;           //行周期数
//parameter H_SyncPulse=96;            //行同步脉冲(Sync a)
//parameter H_BackPorch=48;             //显示后沿(Back porch b)
//parameter H_ActivePix=640;            //显示时序段(Display interval c)
//parameter H_FrontPorch=16;            //显示前沿(Front porch d)
//Parameter Hde_start=144;
//Parameter Hde_end  =784;


//-----------------------------------------------------------//
// 垂直扫描参数的设定640*480 VGA
//-----------------------------------------------------------//
//parameter FramePeriod =525;           //列周期数
//parameter V_SyncPulse=2;              //列同步脉冲(Sync o)
//parameter V_BackPorch=31;             //显示后沿(Back porch p)
//parameter V_ActivePix=480;            //显示时序段(Display interval q)
//parameter V_FrontPorch=11;             //显示前沿(Front porch r)
//parameter Vde_start=33;
//parameter Vde_end  =513;

3. 内部信号


 reg[10 : 0]  x_cnt;         //行计数器即水平计数
  reg[9 : 0]  y_cnt;         //列计数器即垂直计数
  reg[15 : 0] grid_data_1;   //小格子数据寄存器
  reg[15 : 0] grid_data_2;   //大方格数据寄存器
  reg[15 : 0] bar_data;      //彩条数据寄存器
  reg[3 : 0] vga_dis_mode;   //VGA图像显示模式寄存器
  reg[4 : 0]  vga_r_reg;
  reg[5 : 0]  vga_g_reg;
  reg[4 : 0]  vga_b_reg;  
  reg hsync_r;
  reg vsync_r; 
  reg hsync_de;
  reg vsync_de;
  reg [15:0] key1_counter;                 //按键检测寄存器
  
  wire vga_clk;       
  wire CLK_OUT1;   

4. PLL(VGA_CLK)

 在上一篇《VGA入门学习》中讲过,VGA驱动时钟与图像的分辨率有关,并提供了计算方式:800*525*60=约25.2近似25M。工业上帧率常用59.94fps。AX309开发板上提供50M外部时钟,需要经PLL分频拿到需要的时钟。

   //25.2Mhz for 640x480(60hz)/ 40.0Mhz for 800x600(60hz) / 65.0Mhz for 1024x768(60hz)/108.0Mhz for 1280x1024(60hz)
   pll1 pll1_inst
  (// Clock in ports
   .CLK_IN1(fpga_gclk),      // IN
   .CLK_OUT1(CLK_OUT1),     // 25.0Mhz for 640x480(60hz)
   .RESET(~rst_n),          // reset input 
   .LOCKED());        // OUT

 

为了实现横向或竖向或者不同尺寸的方块,首先要能够对一帧图像的行和列进行定位(好比在图像上建立一个坐标系),最直接的方法就是设置行/列计数器x_cnt和y_cnt; 同时,也方便生成同步信号。

5.行计数器

//----------------------------------------------------------------
// 水平扫描计数
//----------------------------------------------------------------
always @ (posedge vga_clk)
       if(~rst_n)    x_cnt <= 1;
       else if(x_cnt == LinePeriod) x_cnt <= 1;
       else x_cnt <= x_cnt+ 1;
		 
//----------------------------------------------------------------

有了行计数器,就方便生成行同步信号了 

6. 行同步信号

    Hsync 和Hsync_de 。 Hync_de信号就是图像行上的data_valid信号,这与Vsync_de信号共同确定有效像素显示区域,后续介绍。

// 水平扫描信号hsync,hsync_de产生
//----------------------------------------------------------------
always @ (posedge vga_clk)
   begin
       if(~rst_n) hsync_r <= 1'b1;
       else if(x_cnt == 1) hsync_r <= 1'b0;            //产生hsync信号
       else if(x_cnt == H_SyncPulse) hsync_r <= 1'b1;
		 
		 		 
	    if(~rst_n) hsync_de <= 1'b0;
       else if(x_cnt == Hde_start) hsync_de <= 1'b1;    //产生hsync_de信号
       else if(x_cnt == Hde_end) hsync_de <= 1'b0;	
	end

7.列计数器

      y_cnt;列计数器需要依赖行计数器来驱动,因为每当行计数器计满即传输一行时,下会到下一列。

//----------------------------------------------------------------
// 垂直扫描计数
//----------------------------------------------------------------
always @ (posedge vga_clk)
       if(~rst_n) y_cnt <= 1;
       else if(y_cnt == FramePeriod) y_cnt <= 1;
       else if(x_cnt == LinePeriod) y_cnt <= y_cnt+1;

同样,有了列计数器,列同步信号和列数据使能信号也就随之产生了:

//----------------------------------------------------------------
// 垂直扫描信号vsync, vsync_de产生
//----------------------------------------------------------------
always @ (posedge vga_clk)
  begin
       if(~rst_n) vsync_r <= 1'b1;
       else if(y_cnt == 1) vsync_r <= 1'b0;    //产生vsync信号
       else if(y_cnt == V_SyncPulse) vsync_r <= 1'b1;
		 
	    if(~rst_n) vsync_de <= 1'b0;
       else if(y_cnt == Vde_start) vsync_de <= 1'b1;    //产生vsync_de信号
       else if(y_cnt == Vde_end) vsync_de <= 1'b0;	 
  end

 ---------------------------------------------------------------------------分割线------------------------------------------------------------------------------------

     以上都是前期准备工作,图像定位问题,驱动时钟,以及对应分辨率下的同步信号什么的都准备好 ,就差输出图像数据了。因为没有视频源,下面就考虑想要显示什么以及怎么显示的问题了。比较简单的就是一些彩条,方块的显示了,虽然简单,但方便理解啊。

8.显示方块

行列计数器对方块间隔进行控制,这里有两种间隔即方块的大小,其数据分别放在grid_data_1和grid_data_2中。需要注意的是两种方块都在跟着计数器的变化生成,只不过在最后显示的时候根据需要,进行模式切换来选择其中一个罢了。

//----------------------------------------------------------------
// 格子测试图像产生
//----------------------------------------------------------------
 always @(negedge vga_clk)   
   begin
     if ((x_cnt[4]==1'b1) ^ (y_cnt[4]==1'b1))            //产生格子1图像
			    grid_data_1<= 16'h0000;
	  else
			    grid_data_1<= 16'hffff;
				 
	  if ((x_cnt[6]==1'b1) ^ (y_cnt[6]==1'b1))            //产生格子2图像 
			    grid_data_2<=16'h0000;
	  else
				 grid_data_2<=16'hffff; 
   
	end

 

 

显示彩条

      需要什么颜色的彩条就将对应的颜色填入数据中即可,x_cnt来控制彩条宽度。也可以选择将下面的各种颜色进行宏定义,这样方便使用。

//----------------------------------------------------------------
// 彩色条测试图像产生
//----------------------------------------------------------------
 always @(negedge vga_clk)   
   begin
     if (x_cnt==300)            
			    bar_data<= 16'hf800;
	  else if (x_cnt==420)
			    bar_data<= 16'h07e0;				 
	  else if (x_cnt==540)            
			    bar_data<=16'h001f;
	  else if (x_cnt==660)            
			    bar_data<=16'hf81f;
	  else if (x_cnt==780)            
			    bar_data<=16'hffe0;
	  else if (x_cnt==900)            
			    bar_data<=16'h07ff;
	  else if (x_cnt==1020)            
			    bar_data<=16'hffff;
	  else if (x_cnt==1140)            
			    bar_data<=16'hfc00;
	  else if (x_cnt==1260)            
			    bar_data<=16'h0000;
   
	end

 

 

 VGA图像数据选择输出

      可以设置多种的输出模式vga_dis_mode,这里就准备了多种模式的VGA图像数据,但是一次只能显示一种。那如何进行选择呢,本例程采用按键控制。

//----------------------------------------------------------------
// VGA图像选择输出
//----------------------------------------------------------------
 //LCD数据信号选择 
 always @(negedge vga_clk)  
    if(~rst_n) begin 
	    vga_r_reg<=0; 
	    vga_g_reg<=0;
	    vga_b_reg<=0;		 
	end
   else
     case(vga_dis_mode)
         4'b0000:begin
			     vga_r_reg<=0;                        //VGA显示全黑
                 vga_g_reg<=0;
                 vga_b_reg<=0;
			end
     	4'b0001:begin
			     vga_r_reg<=5'b11111;                 //VGA显示全白
                 vga_g_reg<=6'b111111;
                 vga_b_reg<=5'b11111;
			end
		4'b0010:begin
			     vga_r_reg<=5'b11111;                //VGA显示全红
                 vga_g_reg<=0;
                 vga_b_reg<=0;  
         end			  
	     4'b0011:begin
			     vga_r_reg<=0;                      //VGA显示全绿
                 vga_g_reg<=6'b111111;
                 vga_b_reg<=0; 
         end					  
         4'b0100:begin     
			     vga_r_reg<=0;                      //VGA显示全蓝
                 vga_g_reg<=0;
                 vga_b_reg<=5'b11111;
			end
         4'b0101:begin     
			     vga_r_reg<=grid_data_1[15:11];     // VGA显示方格1
                 vga_g_reg<=grid_data_1[10:5];
                 vga_b_reg<=grid_data_1[4:0];
         end					  
         4'b0110:begin     
			     vga_r_reg<=grid_data_2[15:11];    // VGA显示方格2
                 vga_g_reg<=grid_data_2[10:5];
                 vga_b_reg<=grid_data_2[4:0];
			end
		 4'b0111:begin     
			     vga_r_reg<=x_cnt[6:2];            //VGA显示水平渐变色
                 vga_g_reg<=x_cnt[6:1];
                 vga_b_reg<=x_cnt[6:2];
			end
		 4'b1000:begin     
			     vga_r_reg<=y_cnt[6:2];            //VGA显示垂直渐变色
                 vga_g_reg<=y_cnt[6:1];
                 vga_b_reg<=y_cnt[6:2];
			end
		 4'b1001:begin     
			     vga_r_reg<=x_cnt[6:2];            //VGA显示红水平渐变色
                 vga_g_reg<=0;
                 vga_b_reg<=0;
			end
		 4'b1010:begin     
			     vga_r_reg<=0;                     //VGA显示绿水平渐变色
                 vga_g_reg<=x_cnt[6:1];
                 vga_b_reg<=0;
			end
		 4'b1011:begin     
			     vga_r_reg<=0;                            //VGA显示蓝水平渐变色
                 vga_g_reg<=0;
                 vga_b_reg<=x_cnt[6:2];			
			end
		 4'b1100:begin     
			     vga_r_reg<=bar_data[15:11];              //VGA显示彩色条
                 vga_g_reg<=bar_data[10:5];
                 vga_b_reg<=bar_data[4:0];			
		     end
		 default:begin
			     vga_r_reg<=5'b11111;                 //VGA显示全白
                 vga_g_reg<=6'b111111;
                 vga_b_reg<=5'b11111;
			end					  
         endcase
	

  assign vga_hs = hsync_r;
  assign vga_vs = vsync_r;  
  assign vga_r = (hsync_de & vsync_de)?vga_r_reg:5'b00000;
  assign vga_g = (hsync_de & vsync_de)?vga_g_reg:6'b000000;
  assign vga_b = (hsync_de & vsync_de)?vga_b_reg:5'b00000;
  assign vga_clk = CLK_OUT1;
  

选择性的放图: 

 

 

按键控制程序

        按键消抖和模式的切换。按键本身只能有两种状态,但是通过每一次案件操作映射模式寄存器对应的vga_dis_mode寄存器值就能实现多种显示状态的切换。

 //按钮处理程序	
  always @(posedge vga_clk)
	  begin
	    if (key1==1'b0)                               //如果按钮没有按下,寄存器为0
	       key1_counter<=0;
	    else if ((key1==1'b1)& (key1_counter<=16'hc350))  //如果按钮按下并按下时间少于1ms,计数      
               key1_counter<=key1_counter+1'b1;
  	  
       if (key1_counter==16'hc349)                //一次按钮有效,改变显示模式
		    begin
		      if(vga_dis_mode==4'b1101)
			      vga_dis_mode<=4'b0000;
			   else
			      vga_dis_mode<=vga_dis_mode+1'b1; 
          end	
     end			 
	  

endmodule

 

 

你可能感兴趣的:(图像预处理以及实现,外设/接口/协议)