(三)AX301 VGA接口的实现

为了督促自己更深入地学习FPGA,发博客似乎成了自己的爱好。突然发觉用英文写好别扭,算了还是用中文写吧。

那么今天来剖析一下AX301黑金开发板VGA接口实例吧。首先,上实验结果图:

哈哈哈哈哈桌面有点乱请勿嫌弃。

在上代码之前,先给大家讲解一下VGA相关的知识。

首先,列填充(HSync)和行扫描(VSync)都由四个区组成,分别叫同步段(sync time)、后廊段(Back Porch)、激活区(Active)、前廊区(Front Porch),这些英文在途中都有标注。其中,真正有效的只有激活区,其他的区域只是用来作同步操作的。那么,了解到这个之后,就可以去看一个表:

特别要注意的是字体加深的数字,这在接下来的编程中起到至关重要的作用。那么接下来看一下代码,关键来看懂color_bar.v,代码如下:

`include "video_define.v"
module color_bar(
    input                 clk,           //pixel clock
    input                 rst,           //reset signal high active
    output                hs,            //horizontal synchronization
    output                vs,            //vertical synchronization
    output                de,            //video valid
    output[7:0]           rgb_r,         //video red data
    output[7:0]           rgb_g,         //video green data
    output[7:0]           rgb_b          //video blue data
);
//video timing parameter definition
`ifdef  VIDEO_1280_720
parameter H_ACTIVE = 16'd1280;           //horizontal active time (pixels)
parameter H_FP = 16'd110;                //horizontal front porch (pixels)
parameter H_SYNC = 16'd40;               //horizontal sync time(pixels)
parameter H_BP = 16'd220;                //horizontal back porch (pixels)
parameter V_ACTIVE = 16'd720;            //vertical active Time (lines)
parameter V_FP  = 16'd5;                 //vertical front porch (lines)
parameter V_SYNC  = 16'd5;               //vertical sync time (lines)
parameter V_BP  = 16'd20;                //vertical back porch (lines)
parameter HS_POL = 1'b1;                 //horizontal sync polarity, 1 : POSITIVE,0 : NEGATIVE;
parameter VS_POL = 1'b1;                 //vertical sync polarity, 1 : POSITIVE,0 : NEGATIVE;
`endif

//480x272 9Mhz
`ifdef  VIDEO_480_272
parameter H_ACTIVE = 16'd480; 
parameter H_FP = 16'd2;       
parameter H_SYNC = 16'd41;    
parameter H_BP = 16'd2;       
parameter V_ACTIVE = 16'd272; 
parameter V_FP  = 16'd2;
parameter V_SYNC  = 16'd10;   
parameter V_BP  = 16'd2;     
parameter HS_POL = 1'b0;
parameter VS_POL = 1'b0;
`endif

//640x480 25.175Mhz
`ifdef  VIDEO_640_480
parameter H_ACTIVE = 16'd640; 
parameter H_FP = 16'd16;      
parameter H_SYNC = 16'd96;    
parameter H_BP = 16'd48;      
parameter V_ACTIVE = 16'd480; 
parameter V_FP  = 16'd10;
parameter V_SYNC  = 16'd2;    
parameter V_BP  = 16'd33;    
parameter HS_POL = 1'b0;
parameter VS_POL = 1'b0;
`endif

//800x480 33Mhz
`ifdef  VIDEO_800_480
parameter H_ACTIVE = 16'd800; 
parameter H_FP = 16'd40;      
parameter H_SYNC = 16'd128;   
parameter H_BP = 16'd88;      
parameter V_ACTIVE = 16'd480; 
parameter V_FP  = 16'd1;
parameter V_SYNC  = 16'd3;    
parameter V_BP  = 16'd21;    
parameter HS_POL = 1'b0;
parameter VS_POL = 1'b0;
`endif

//800x600 40Mhz
`ifdef  VIDEO_800_600
parameter H_ACTIVE = 16'd800; 
parameter H_FP = 16'd40;      
parameter H_SYNC = 16'd128;   
parameter H_BP = 16'd88;      
parameter V_ACTIVE = 16'd600; 
parameter V_FP  = 16'd1;
parameter V_SYNC  = 16'd4;    
parameter V_BP  = 16'd23;    
parameter HS_POL = 1'b1;
parameter VS_POL = 1'b1;
`endif

//1024x768 65Mhz
`ifdef  VIDEO_1024_768
parameter H_ACTIVE = 16'd1024;
parameter H_FP = 16'd24;      
parameter H_SYNC = 16'd136;   
parameter H_BP = 16'd160;     
parameter V_ACTIVE = 16'd768; 
parameter V_FP  = 16'd3;
parameter V_SYNC  = 16'd6;    
parameter V_BP  = 16'd29;     
parameter HS_POL = 1'b0;
parameter VS_POL = 1'b0;
`endif

//1920x1080 148.5Mhz
`ifdef  VIDEO_1920_1080
parameter H_ACTIVE = 16'd1920;
parameter H_FP = 16'd88;
parameter H_SYNC = 16'd44;
parameter H_BP = 16'd148; 
parameter V_ACTIVE = 16'd1080;
parameter V_FP  = 16'd4;
parameter V_SYNC  = 16'd5;
parameter V_BP  = 16'd36;
parameter HS_POL = 1'b1;
parameter VS_POL = 1'b1;
`endif
parameter H_TOTAL = H_ACTIVE + H_FP + H_SYNC + H_BP;//horizontal total time (pixels)
parameter V_TOTAL = V_ACTIVE + V_FP + V_SYNC + V_BP;//vertical total time (lines)
//define the RGB values for 8 colors
parameter WHITE_R       = 8'hff;
parameter WHITE_G       = 8'hff;
parameter WHITE_B       = 8'hff;
parameter YELLOW_R      = 8'hff;
parameter YELLOW_G      = 8'hff;
parameter YELLOW_B      = 8'h00;
parameter CYAN_R        = 8'h00;
parameter CYAN_G        = 8'hff;
parameter CYAN_B        = 8'hff;                                
parameter GREEN_R       = 8'h00;
parameter GREEN_G       = 8'hff;
parameter GREEN_B       = 8'h00;
parameter MAGENTA_R     = 8'hff;
parameter MAGENTA_G     = 8'h00;
parameter MAGENTA_B     = 8'hff;
parameter RED_R         = 8'hff;
parameter RED_G         = 8'h00;
parameter RED_B         = 8'h00;
parameter BLUE_R        = 8'h00;
parameter BLUE_G        = 8'h00;
parameter BLUE_B        = 8'hff;
parameter BLACK_R       = 8'h00;
parameter BLACK_G       = 8'h00;
parameter BLACK_B       = 8'h00;
reg hs_reg;                      //horizontal sync register
reg vs_reg;                      //vertical sync register
reg hs_reg_d0;                   //delay 1 clock of 'hs_reg'
reg vs_reg_d0;                   //delay 1 clock of 'vs_reg'
reg[11:0] h_cnt;                 //horizontal counter
reg[11:0] v_cnt;                 //vertical counter
reg[11:0] active_x;              //video x position 
reg[11:0] active_y;              //video y position 
reg[7:0] rgb_r_reg;              //video red data register
reg[7:0] rgb_g_reg;              //video green data register
reg[7:0] rgb_b_reg;              //video blue data register
reg h_active;                    //horizontal video active
reg v_active;                    //vertical video active
wire video_active;               //video active(horizontal active and vertical active)
reg video_active_d0;             //delay 1 clock of video_active
assign hs = hs_reg_d0;
assign vs = vs_reg_d0;
assign video_active = h_active & v_active;
assign de = video_active_d0;
assign rgb_r = rgb_r_reg;
assign rgb_g = rgb_g_reg;
assign rgb_b = rgb_b_reg;
always@(posedge clk or posedge rst)
begin
    if(rst == 1'b1)
        begin
            hs_reg_d0 <= 1'b0;
            vs_reg_d0 <= 1'b0;
            video_active_d0 <= 1'b0;
        end
    else
        begin
            hs_reg_d0 <= hs_reg;
            vs_reg_d0 <= vs_reg;
            video_active_d0 <= video_active;
        end
end

always@(posedge clk or posedge rst)
begin
    if(rst == 1'b1)
        h_cnt <= 12'd0;
    else if(h_cnt == H_TOTAL - 1)//horizontal counter maximum value
        h_cnt <= 12'd0;
    else
        h_cnt <= h_cnt + 12'd1;
end

always@(posedge clk or posedge rst)
begin
    if(rst == 1'b1)
        active_x <= 12'd0;
    else if(h_cnt >= H_FP + H_SYNC + H_BP - 1)//horizontal video active
        active_x <= h_cnt - (H_FP[11:0] + H_SYNC[11:0] + H_BP[11:0] - 12'd1);
    else
        active_x <= active_x;
end

always@(posedge clk or posedge rst)
begin
    if(rst == 1'b1)
        v_cnt <= 12'd0;
    else if(h_cnt == H_FP  - 1)//horizontal sync time
        if(v_cnt == V_TOTAL - 1)//vertical counter maximum value
            v_cnt <= 12'd0;
        else
            v_cnt <= v_cnt + 12'd1;
    else
        v_cnt <= v_cnt;
end

always@(posedge clk or posedge rst)
begin
    if(rst == 1'b1)
        hs_reg <= 1'b0;
    else if(h_cnt == H_FP - 1)//horizontal sync begin
        hs_reg <= HS_POL;
    else if(h_cnt == H_FP + H_SYNC - 1)//horizontal sync end
        hs_reg <= ~hs_reg;
    else
        hs_reg <= hs_reg;
end

always@(posedge clk or posedge rst)
begin
    if(rst == 1'b1)
        h_active <= 1'b0;
    else if(h_cnt == H_FP + H_SYNC + H_BP - 1)//horizontal active begin
        h_active <= 1'b1;
    else if(h_cnt == H_TOTAL - 1)//horizontal active end
        h_active <= 1'b0;
    else
        h_active <= h_active;
end

always@(posedge clk or posedge rst)
begin
    if(rst == 1'b1)
        vs_reg <= 1'd0;
    else if((v_cnt == V_FP - 1) && (h_cnt == H_FP - 1))//vertical sync begin
        vs_reg <= HS_POL;
    else if((v_cnt == V_FP + V_SYNC - 1) && (h_cnt == H_FP - 1))//vertical sync end
        vs_reg <= ~vs_reg;  
    else
        vs_reg <= vs_reg;
end

always@(posedge clk or posedge rst)
begin
    if(rst == 1'b1)
        v_active <= 1'd0;
    else if((v_cnt == V_FP + V_SYNC + V_BP - 1) && (h_cnt == H_FP - 1))//vertical active begin
        v_active <= 1'b1;
    else if((v_cnt == V_TOTAL - 1) && (h_cnt == H_FP - 1)) //vertical active end
        v_active <= 1'b0;
    else
        v_active <= v_active;
end

always@(posedge clk or posedge rst)
begin
    if(rst == 1'b1)
        begin
            rgb_r_reg <= 8'h00;
            rgb_g_reg <= 8'h00;
            rgb_b_reg <= 8'h00;
        end
    else if(video_active)
        if(active_x == 12'd0)
            begin
                rgb_r_reg <= WHITE_R;
                rgb_g_reg <= WHITE_G;
                rgb_b_reg <= WHITE_B;
            end
        else if(active_x == (H_ACTIVE/8) * 1)
            begin
                rgb_r_reg <= YELLOW_R;
                rgb_g_reg <= YELLOW_G;
                rgb_b_reg <= YELLOW_B;
            end
        else if(active_x == (H_ACTIVE/8) * 2)
            begin
                rgb_r_reg <= CYAN_R;
                rgb_g_reg <= CYAN_G;
                rgb_b_reg <= CYAN_B;
            end
        else if(active_x == (H_ACTIVE/8) * 3)
            begin
                rgb_r_reg <= GREEN_R;
                rgb_g_reg <= GREEN_G;
                rgb_b_reg <= GREEN_B;
            end
        else if(active_x == (H_ACTIVE/8) * 4)
            begin
                rgb_r_reg <= MAGENTA_R;
                rgb_g_reg <= MAGENTA_G;
                rgb_b_reg <= MAGENTA_B;
            end
        else if(active_x == (H_ACTIVE/8) * 5)
            begin
                rgb_r_reg <= RED_R;
                rgb_g_reg <= RED_G;
                rgb_b_reg <= RED_B;
            end
        else if(active_x == (H_ACTIVE/8) * 6)
            begin
                rgb_r_reg <= BLUE_R;
                rgb_g_reg <= BLUE_G;
                rgb_b_reg <= BLUE_B;
            end
        else if(active_x == (H_ACTIVE/8) * 7)
            begin
                rgb_r_reg <= BLACK_R;
                rgb_g_reg <= BLACK_G;
                rgb_b_reg <= BLACK_B;
            end
        else
            begin
                rgb_r_reg <= rgb_r_reg;
                rgb_g_reg <= rgb_g_reg;
                rgb_b_reg <= rgb_b_reg;
            end
    else
        begin
            rgb_r_reg <= 8'h00;
            rgb_g_reg <= 8'h00;
            rgb_b_reg <= 8'h00;
        end
end

endmodule

代码非常长,我们来一步一步地剖析吧(大家可以一边对着代码一边看)。

端口:clk,rst,hs列填充,vs行扫描,de使能,RGB三个颜色地寄存器

扫描二维码关注公众号,回复: 3921801 查看本文章

参数:列填充和行扫描地激活区、前廊区、后廊区、同步区各自对应的像素(这里就是为了选定激活区使能,参数都可以在上一张图加深的数字中找到),这里有非常多的ifdef是为了方便以后选择,所以代码虽长,但都是很多无关痛痒的东西,直接可以跳过了。

接下来就是列填充和行扫描总的像素点:H_TOTAL和V_TOTAL

接着就是定义各种颜色:因为红绿蓝是颜色的三基色,任何颜色都可以通过这三种颜色表示出来。因此只要控制颜色的深浅和有无就可以表达其他颜色,代码的意思就是这样,因此每一种颜色都需要R\G\B的值。

接下来就是一些暂时的寄存器,这里就不讲了。

接下来开始讲解always,其中关于rst复位的忽略不讲

第一个always:就是将暂时的寄存器的值直接给最终执行操作的寄存器,包括列填充和行扫描以及激活段的使能

第二个always:对列填充的像素开始计数,一个很简单的计数器h_cnt,当到达H_TOTAL时重新归零,否则加1;

第三个always:x坐标的确定(和列填充有关),在一个周期内,当扫描完前廊、后廊和同步区,到达激活区的时候,x做报表被激活,同时计算出坐标的值

第四个always:当列填充完一次后,行扫描进行下一行,因此这里是行扫描的计数器。

第五个always:到达同步时钟的时候,可以看到刚刚上面的时序图,同步区会有一个翻转,这里就是翻转的意思

第六个always:当列填充到达激活区的时候,使能列填充信号,否则失能

第七个always:和第五个always一样,是行扫描的同步区的时钟翻转,第五个是列填充

第八个always:当列填充和行扫描同时到达激活区的时候,激活区就被使能,注意一定是同时。而离开激活区之后就失能激活信号。

第九个always:当active_x到达第一区,显示白色,第二区黄色。。。。。就这个意思理解。

好了,代码理解完毕。接下来,要知道,AX301的晶振是50MHz的,而VGA的扫描需要60MHz,否则是不会显示出来的,因此,需要设置pll。设置pll专门有一节详细讲了,自己去看视频吧,完全不用写代码只需要跟着步骤走超级简单我就不说了。

接下来就是顶层文件:

module top(
    input                       clk,
    input                       rst_n,
    //vga output
    output                      vga_out_hs, //vga horizontal synchronization
    output                      vga_out_vs, //vga vertical synchronization
    output[4:0]                 vga_out_r,  //vga red
    output[5:0]                 vga_out_g,  //vga green
    output[4:0]                 vga_out_b   //vga blue
    
);

wire                            video_clk;
wire                            video_hs;
wire                            video_vs;
wire                            video_de;
wire[7:0]                       video_r;
wire[7:0]                       video_g;
wire[7:0]                       video_b;

assign vga_out_hs = video_hs;
assign vga_out_vs = video_vs;
assign vga_out_r  = video_r[7:3]; //discard low bit data
assign vga_out_g  = video_g[7:2]; //discard low bit data
assign vga_out_b  = video_b[7:3]; //discard low bit data

//generate video pixel clock
video_pll video_pll_m0(
    .inclk0(clk),
    .c0(video_clk));

color_bar color_bar_m0(
    .clk(video_clk),
    .rst(~rst_n),
    .hs(video_hs),
    .vs(video_vs),
    .de(video_de),
    .rgb_r(video_r),
    .rgb_g(video_g),
    .rgb_b(video_b)
);
endmodule

然后将管脚和你原理图上的管脚连起来就OK了,VGA接口无非五个pin。列填充,行扫描,R、G、B,对着原理图连根本不会有什么大问题。

这么长的代码分析起来原来这么简单。

PS一点心得,感觉学硬件无论是ARM还是FPGA,编程的思想还是要根据硬件的时序图来走,其实无非就是时序,命令符,只要理解透好像完全不是大问题。当然这些都是入门级的东西了。所以赶紧学,入门级的水平还是低了些。加油。

猜你喜欢

转载自blog.csdn.net/weixin_41892263/article/details/82961438
VGA
今日推荐