基于FPGA的VGA显示实验

VGA驱动原理

基于FPGA的VGA显示实验_第1张图片

信号线 定义
HS 行同步信号(3.3V 电平)
VS 场同步信号(3.3V 电平)
R 红基色 (0~0.714V 模拟信号)
G 绿基色 (0~0.714V 模拟信号)
B 蓝基色 (0~0.714V 模拟信号)

显示扫描轨迹

基于FPGA的VGA显示实验_第2张图片每扫描完一行,重新开始下一行;每扫描完一场,重新开始下一场;直到像素点扫描完成。

行场扫描

基于FPGA的VGA显示实验_第3张图片场扫描时序分析
基于FPGA的VGA显示实验_第4张图片

  • a~b:场消隐期 即同步,相当于还原扫描坐标
  • b~c:场消隐后肩 相当于准备开始扫描
  • c~d:场显示期 扫描中,数据有效区域
  • d~e:场消隐前肩 完成扫描,相当于准备同步

行扫描时序
基于FPGA的VGA显示实验_第5张图片

  • a~b:行消隐期 即同步,相当于还原扫描坐标
  • b~c:行消隐后肩 相当于准备开始扫描
  • c~d:行显示期 扫描中,数据有效区域
  • d~e:行消隐前肩 完成扫描,相当于准备同步

三原色

VGA接口:R,G,B三通道,直接赋给数字信号,RGB,最多产生8种色彩。
基于FPGA的VGA显示实验_第6张图片

真彩显示

通过构建电阻网络进行连接,即通过电阻网络分流模拟DAC控制电压大小来控制R,G,B的输入电压。
基于FPGA的VGA显示实验_第7张图片

驱动代码

//----------------------------------------------------------------------------------------
// Modified by:		    正点原子
// Modified date:	    2018/1/30 11:12:36
// Version:			    V1.1
// Descriptions:	    vga驱动
//
//----------------------------------------------------------------------------------------
//****************************************************************************************//

module vga_driver(
    input           vga_clk,      //VGA驱动时钟
    input           sys_rst_n,    //复位信号
    //VGA接口                          
    output          vga_hs,       //行同步信号
    output          vga_vs,       //场同步信号
    output  [15:0]  vga_rgb,      //红绿蓝三原色输出
    
    input   [15:0]  pixel_data,   //像素点数据
    output  [ 9:0]  pixel_xpos,   //像素点横坐标
    output  [ 9:0]  pixel_ypos    //像素点纵坐标    
    );                             
                                                        
//parameter define  
parameter  H_SYNC   =  10'd96;    //行同步
parameter  H_BACK   =  10'd48;    //行显示后沿
parameter  H_DISP   =  10'd640;   //行有效数据
parameter  H_FRONT  =  10'd16;    //行显示前沿
parameter  H_TOTAL  =  10'd800;   //行扫描周期

parameter  V_SYNC   =  10'd2;     //场同步
parameter  V_BACK   =  10'd33;    //场显示后沿
parameter  V_DISP   =  10'd480;   //场有效数据
parameter  V_FRONT  =  10'd10;    //场显示前沿
parameter  V_TOTAL  =  10'd525;   //场扫描周期
          
//reg define                                     
reg  [9:0] cnt_h;
reg  [9:0] cnt_v;

//wire define
wire       vga_en;
wire       data_req; 

//*****************************************************
//**                    main code
//*****************************************************
//VGA行场同步信号
assign vga_hs  = (cnt_h <= H_SYNC - 1'b1) ? 1'b0 : 1'b1;
assign vga_vs  = (cnt_v <= V_SYNC - 1'b1) ? 1'b0 : 1'b1;

//使能RGB565数据输出
assign vga_en  = (((cnt_h >= H_SYNC+H_BACK) && (cnt_h < H_SYNC+H_BACK+H_DISP))
                 &&((cnt_v >= V_SYNC+V_BACK) && (cnt_v < V_SYNC+V_BACK+V_DISP)))
                 ?  1'b1 : 1'b0;
                 
//RGB565数据输出                 
assign vga_rgb = vga_en ? pixel_data : 16'd0;

//请求像素点颜色数据输入                
assign data_req = (((cnt_h >= H_SYNC+H_BACK-1'b1) && (cnt_h < H_SYNC+H_BACK+H_DISP-1'b1))
                  && ((cnt_v >= V_SYNC+V_BACK) && (cnt_v < V_SYNC+V_BACK+V_DISP)))
                  ?  1'b1 : 1'b0;

//像素点坐标                
assign pixel_xpos = data_req ? (cnt_h - (H_SYNC + H_BACK - 1'b1)) : 10'd0;
assign pixel_ypos = data_req ? (cnt_v - (V_SYNC + V_BACK - 1'b1)) : 10'd0;

//行计数器对像素时钟计数
always @(posedge vga_clk or negedge sys_rst_n) begin         
    if (!sys_rst_n)
        cnt_h <= 10'd0;                                  
    else begin
        if(cnt_h < H_TOTAL - 1'b1)                                               
            cnt_h <= cnt_h + 1'b1;                               
        else 
            cnt_h <= 10'd0;  
    end
end

//场计数器对行计数
always @(posedge vga_clk or negedge sys_rst_n) begin         
    if (!sys_rst_n)
        cnt_v <= 10'd0;                                  
    else if(cnt_h == H_TOTAL - 1'b1) begin
        if(cnt_v < V_TOTAL - 1'b1)                                               
            cnt_v <= cnt_v + 1'b1;                               
        else 
            cnt_v <= 10'd0;  
    end
end

endmodule 

常见VGA显示刷新率时序

基于FPGA的VGA显示实验_第8张图片

VGA显示程序

//****************************************************************************************//

module vga_display(
    input             vga_clk,                  //VGA驱动时钟
    input             sys_rst_n,                //复位信号
    
    input      [ 9:0] pixel_xpos,               //像素点横坐标
    input      [ 9:0] pixel_ypos,               //像素点纵坐标    
    output reg [15:0] pixel_data                //像素点数据,
    );    

//parameter define    
parameter  H_DISP = 10'd640;                    //分辨率——行
parameter  V_DISP = 10'd480;                    //分辨率——列


localparam WIDTH  = 10'd160;                    //字符区域宽度
localparam HEIGHT = 10'd22;                     //字符区域高度
localparam SIDE_W = 10'd40;                     //边框宽度
localparam BLACK  = 16'b00000_000000_00000;     //RGB565 黑色
//localparam PINK   = 16'b11010_010111_10101;     //RGB565 粉色
//localparam RED    = 16'b11111_000000_00000;     //RGB565 红色
//localparam GREEN  = 16'b00000_111111_00000;     //RGB565 绿色

//reg define
reg  [159:0] char[22:0];                         //字符数组
reg [ 9:0] block_x;                             //字符左上角横坐标
reg [ 9:0] block_y;                             //字符左上角纵坐标
reg [21:0] div_cnt;                             //时钟分频计数器
reg        h_direct;                            //字符水平移动方向,1:右移,0:左移
reg        v_direct;                            //字符竖直移动方向,1:向下,0:向上

reg [16:0] color[10:0];                         //颜色数组
reg [ 9:0] flag;

//wire define   
wire [ 9:0] x_cnt;
wire [ 9:0] y_cnt;

wire move_en;                                   //方块移动使能信号,频率为100hz

//*****************************************************
//**                    main code
//*****************************************************

assign move_en = (div_cnt == 22'd300000 - 1'b1) ? 1'b1 : 1'b0;

assign x_cnt = pixel_xpos - block_x;              //像素点相对于字符区域起始点水平坐标
assign y_cnt = pixel_ypos - block_y;              //像素点相对于字符区域起始点竖直坐标

//给字符数组赋值,显示汉字“I LOVE YOU”,汉字大小为32*32
always @(posedge vga_clk) begin
    char[0]  <= 160'h3FFC00007E0003C07C1E7FFC00007E3E03C0FC3E;
    char[1]  <= 160'h03C0000018000C30180C180C000038080C303008;
    char[2]  <= 160'h03C0000018001818180818040000180818183008;
    char[3]  <= 160'h03C0000018001008180818020000181010083008;
    char[4]  <= 160'h03C000001800300C1808180200000C10300C3008;
    char[5]  <= 160'h03C000001800300C0C10180000000C10300C3008;
    char[6]  <= 160'h03C00000180060040C10180000000C2060043008;
    char[7]  <= 160'h03C00000180060060C1018100000062060063008;
    char[8]  <= 160'h03C00000180060060C1018100000062060063008;
    char[9]  <= 160'h03C00000180060060C2018300000034060063008;
    char[10] <= 160'h03C000001800600606201FF00000034060063008;  
	char[11] <= 160'h03C0000018006006062018300000038060063008;
    char[12] <= 160'h03C0000018006006062018100000018060063008;
    char[13] <= 160'h03C0000018006006064018100000018060063008;
    char[14] <= 160'h03C0000018006006034018000000018060063008;
    char[15] <= 160'h03C0000018002006034018000000018020063008;
    char[16] <= 160'h03C000001800300C0340180000000180300C3008;
    char[17] <= 160'h03C000001802300C0380180200000180300C3008;
    char[18] <= 160'h03C0000018021008018018020000018010083008;
    char[19] <= 160'h03C0000018041818018018040000018018181810;
    char[20] <= 160'h03C00000180C0C300100180C000001800C301C20;
    char[20] <= 160'h3FFC00007FFC03C001007FFC000007E003C007C0; 
end
always @(posedge vga_clk) begin
	 color[0] <= 16'b11010_010111_10101;     //RGB565 粉色
	 color[1] <= 16'b11111_000000_00000;     //RGB565 红色
	 color[2] <= 16'b00000_111111_00000;     //RGB565 绿色
	 color[3] <= 16'b00000_000000_11111;     //RGB565 蓝色
	 color[4] <= 16'b11111_111111_11111;     //RGB565 白色
	 color[5] <= 16'b00000_011111_01111;     //RGB565 深青色
	 color[6] <= 16'b01111_000000_01111;     //RGB565 紫色 
	 color[7] <= 16'b00000_111111_11111;     //RGB565 青色
	 color[8] <= 16'b11111_111111_00000;     //RGB565 黄色
	 color[9] <= 16'b00000_000000_01111;     //RGB565 深蓝色
end
//通过对vga驱动时钟计数,实现时钟分频
always @(posedge vga_clk or negedge sys_rst_n) begin         
    if (!sys_rst_n)
        div_cnt <= 22'd0;
    else begin
        if(div_cnt < 22'd300000 - 1'b1) 
            div_cnt <= div_cnt + 1'b1;
        else
            div_cnt <= 22'd0;                   //计数达10ms后清零
    end
end

//当方块移动到边界时,改变移动方向
always @(posedge vga_clk or negedge sys_rst_n) begin         
    if (!sys_rst_n) begin
        h_direct <= 1'b1;                       //方块初始水平向右移动
        v_direct <= 1'b1;                       //方块初始竖直向下移动
		  flag	  <= 10'd0;
    end
    else begin
        if(block_x == SIDE_W - 1'b1)            //到达左边界时,水平向右
            h_direct <= 1'b1;
        else                                    //到达右边界时,水平向左
        if(block_x == H_DISP - SIDE_W - WIDTH)
            h_direct <= 1'b0;
        else
            h_direct <= h_direct;
            
        if(block_y == SIDE_W - 1'b1)            //到达上边界时,竖直向下
            v_direct <= 1'b1;   
        else                                    //到达下边界时,竖直向上
        if(block_y == V_DISP - SIDE_W - HEIGHT)
            v_direct <= 1'b0;
        else
            v_direct <= v_direct;
		  if((block_x == SIDE_W - 1'b1)||(block_x == H_DISP - SIDE_W - WIDTH)
		  	 ||(block_y == SIDE_W - 1'b1)||(block_y == V_DISP - SIDE_W - HEIGHT))
				flag <= flag + 1'b1;
		  else
				flag <= flag;	
				
		  if (flag > 10'd9)
				flag <= 1'b0;
		  else if(flag < 1'b0)
				flag <= 10'd9;
    end
end

//根据方块移动方向,改变其纵横坐标
always @(posedge vga_clk or negedge sys_rst_n) begin         
    if (!sys_rst_n) begin
        block_x <= 22'd100;                     //字符初始位置横坐标
        block_y <= 22'd100;                     //字符初始位置纵坐标
    end
    else if(move_en) begin
        if(h_direct) 
            block_x <= block_x + 1'b1;          //字符向右移动
        else
            block_x <= block_x - 1'b1;          //字符向左移动
            
        if(v_direct) 
            block_y <= block_y + 1'b1;          //字符向下移动
        else
            block_y <= block_y - 1'b1;          //字符向上移动
    end
    else begin
        block_x <= block_x;
        block_y <= block_y;
    end
end

//给不同的区域绘制不同的颜色
always @(posedge vga_clk or negedge sys_rst_n) begin         
    if (!sys_rst_n) 
        pixel_data <= BLACK;
    else begin
		  if((pixel_xpos < SIDE_W) || (pixel_xpos >= H_DISP - SIDE_W)
          || (pixel_ypos < SIDE_W) || (pixel_ypos >= V_DISP - SIDE_W))
            pixel_data <= color[ flag - 1'b1];                 //绘制边框颜色
        else
        if((pixel_xpos >= block_x) && (pixel_xpos < block_x + WIDTH)
          && (pixel_ypos >= block_y) && (pixel_ypos < block_y + HEIGHT)) begin
            if(char[y_cnt][10'd159 - x_cnt])
                pixel_data <= color[  flag ];              //绘制字符颜色
            else
                pixel_data <= BLACK;             //绘制字符区域背景为黑色      
        end
        else
            pixel_data <= BLACK;                //绘制屏幕背景为黑色
    end
end

endmodule 

效果图

基于FPGA的VGA显示实验_第9张图片
基于FPGA的VGA显示实验_第10张图片
基于FPGA的VGA显示实验_第11张图片

你可能感兴趣的:(FPGA)