1nnocent 发表于 2022-8-3 15:29

08、安路SparkRoad国产FPGA测评【学习篇】VGA显示

本帖最后由 1nnocent 于 2022-7-30 09:19 编辑

<p>&nbsp;&nbsp;&nbsp;&nbsp;该例程主要分为 3 个模块, 用于实现 VGA 的驱动显示功能。 其中顶层模块例化了 vga 时钟模块,VGA 驱动模块以及 VGA 显示测试模块。</p>

<p>&nbsp;&nbsp;&nbsp;&nbsp;vga 时钟模块是通过调用 PLL IP 核产生 VGA 的驱动时钟。 需要注意的是选择不同的分辨率要生成不同的驱动时钟,这个可以在VESA官网上找到每种分辨率对应的驱动时钟,以及行场信号信息,这里直接贴出&ldquo;<span style="color:#1abc9c;">VESA和计算机显示监视器计时(DMT)行业标准和指南</span>&rdquo;:<br />
&nbsp;&nbsp;&nbsp;&nbsp;vga 驱动模块是用于驱动 VGA 显示。 用户可以通过改变 VGA 分辨率的时序参数(这里时序参数指的是行场信号,其中行信号主要包括:<span style="color:#1abc9c;">行显示同步信号、行显示前沿、行显示有效数据、行显示后沿</span>,其中行显示有效数据是分辨率的具体数值比如1920;场信号包括:<span style="color:#1abc9c;">场显示同步信号、场显示前沿、场显示有效数据、场显示后沿</span>,其中场显示有效数据是分辨率的具体数值比如1080;行同步信号以具体像素点为基本单位【比如行显示有效数据有1920个像素点】,场同步信号以场同步信号为基本单位【比如场显示有效数据有1080个信号,这里以行位基本单位,一个行信号不仅包含有效数据,还包含前沿、后沿等,是一个完整的行信号】)来自主选择实现不同的分辨率。 下图可以帮助理解(网上找的网图):</p>

<p> &nbsp;</p>

<p> &nbsp;</p>

<p>之前也用示波器抓过行场信号,这里也贴出来,便于理解(图一为场信号【红色】、行信号【黄色】以及使能信号【蓝色】的总和;图二为行信号和使能信号,图二可以看出显示前沿、有效数据即蓝色使能信号、显示后沿,行同步信号):</p>

<p><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; vga 显示模块是用于测试 VGA 显示。</p>

<p>&nbsp; &nbsp; 现在分析代码:输入接口为:系统时钟clk_24m、外部按键复位rst_n;输出接口为:像素时钟vga_clk(用于驱动显示频)、行同步信号vga_hs、场同步信号vga_hs、输出数据使能vga_de、红色数据通道vga_r、绿色数据通道vga_g、蓝色数据通道vga_b。</p>

<p>&nbsp; &nbsp; 首先是PLL模块,PLL输入时钟为系统时钟24MHz,产生一个108MHz时钟用于驱动显示器,通过查找上述的&ldquo;VESA和计算机显示监视器计时(DMT)行业标准和指南&rdquo;可以知道该频率用于驱动1280x1024@60Hz分辨率。</p>

<p>&nbsp; &nbsp; 接下来是显示器驱动模块(Driver.v),该模块用于产生行场信号。1、模块定义hcnt对行同步信号进行计数,该分辨率计数1688次后清零hcnt重新计数;2、hcnt计数H_SYNC(112)次时产生行同步信号,拉高lcd_hs;3、定义vcnt对场同步信号进行计数,计数1066次后清零vcnt重新计数,vcnt自加时还须满足行信号计数完1688次;4、vcnt计数V_SYNC(3)次时产生场同步信号,拉高lcd_vs;5、当hcnt满足行有效数据区间(hcnt &gt;= H_SYNC + H_BACK - H_AHEAD &amp;&amp; hcnt &lt; H_SYNC + H_BACK + H_DISP - H_AHEAD),并且vcnt满足场有效数据区间(vcnt &gt;= V_SYNC + V_BACK &amp;&amp; vcnt &lt; V_SYNC + V_BACK + V_DISP)拉高数据使能信号lcd_en,此时向显示器发送像素数据。</p>

<pre>
<code>`timescale 1ns/1ns
/* VGA参数配置表
************        clk                       H_SYNC                 H_BACK                 H_DISP                 H_FRONT         H_TOTAL                 V_SYNC                 V_BACK                 V_DISP                 V_FRONT         V_TOTAL                *
640x480@60Hz        25.2MHz                96                        48                         640                 16                         800                         2                        33                        480                 10                        525                *
800x600@60Hz        40MHz                128                        88                         800                 40                         1056                        4                        23                        600                 1                        628                *
1024x768@60Hz        65MHz                136                        160                 1024                 24                         1344                        6                        29                        768                 3                        806                *
1280x720@60Hz        74.25MHz        40                        220                 1280                 110                        1650                        5                        20                        720                 5                        750                *
1280x1024@60Hz        108MHz                112                        248                 1280                 48                         1688                        3                        38                        1024                1                        1066        *
1920x1080@60Hz        148.5MHz        44                        148                 1920                 88                         2200                        5                        36                        1080                4                        1125        *
*/       
module Driver
#(
        parameter H_SYNC = 112        ,                 // 行同步信号时间
        parameter H_BACK = 248        ,                 // 行消隐后肩时间
        parameter H_DISP = 1280        ,                 // 行数据有效时间
        parameter H_FRONT = 48        ,                 // 行消隐前肩时间
        parameter H_TOTAL = 1688,                 // 行扫描总时间
                       
        parameter V_SYNC = 3        ,                 // 列同步信号时间
        parameter V_BACK = 38        ,                 // 列消隐后肩时间
        parameter V_DISP = 1024        ,                 // 列数据有效时间
        parameter V_FRONT = 1        ,                 // 列消隐前肩时间
        parameter V_TOTAL = 1066                // 列扫描总时间
)
(
        inputwire                        clk,                        //VGA clock
        inputwire                        rst_n,                   //sync reset
        inputwire                lcd_data,                //lcd data
       
        //lcd interface
        output wire                        lcd_dclk,           //lcd pixel clock
        output wire                        lcd_hs,                  //lcd horizontal sync
        output wire                        lcd_vs,                  //lcd vertical sync
        output wire                        lcd_en,                        //lcd display enable
        output wire                lcd_rgb,                //lcd display data

        //user interface
        output wire                lcd_xpos,                //lcd horizontal coordinate
        output wire                lcd_ypos                //lcd vertical coordinate
);       

localparam        H_AHEAD =         12'd1;

reg hcnt;
reg vcnt;
wire lcd_request;

/*******************************************
                SYNC--BACK--DISP--FRONT
*******************************************/
//h_sync counter &amp; generator
always @ (posedge clk or negedge rst_n)
begin
        if (!rst_n)
                hcnt &lt;= 12'd0;
        else
        begin
      if(hcnt &lt; H_TOTAL - 1'b1)                //line over                       
            hcnt &lt;= hcnt + 1'b1;
      else
            hcnt &lt;= 12'd0;
        end
end

assign        lcd_hs = (hcnt &lt;= H_SYNC - 1'b1) ? 1'b0 : 1'b1; // line over flag

//v_sync counter &amp; generator
always@(posedge clk or negedge rst_n)
begin
        if (!rst_n)
                vcnt &lt;= 12'b0;
        else if(hcnt == H_TOTAL - 1'b1)        //line over
                begin
                if(vcnt == V_TOTAL - 1'b1)                //frame over
                        vcnt &lt;= 12'd0;
                else
                        vcnt &lt;= vcnt + 1'b1;
                end
end

assign        lcd_vs = (vcnt &lt;= V_SYNC - 1'b1) ? 1'b0 : 1'b1; // frame over flag

// LED clock
assign        lcd_dclk = ~clk;

// Control Display
assign        lcd_en                =        (hcnt &gt;= H_SYNC + H_BACK&amp;&amp; hcnt &lt; H_SYNC + H_BACK + H_DISP) &amp;&amp;
                                                (vcnt &gt;= V_SYNC + V_BACK&amp;&amp; vcnt &lt; V_SYNC + V_BACK + V_DISP)
                                                ? 1'b1 : 1'b0;                   // Display Enable Signal
                                               
assign        lcd_rgb         =         lcd_en ? lcd_data : 24'h000000;       

//ahead x clock
assign        lcd_request        =        (hcnt &gt;= H_SYNC + H_BACK - H_AHEAD &amp;&amp; hcnt &lt; H_SYNC + H_BACK + H_DISP - H_AHEAD) &amp;&amp;
                                                (vcnt &gt;= V_SYNC + V_BACK &amp;&amp; vcnt &lt; V_SYNC + V_BACK + V_DISP)
                                                ? 1'b1 : 1'b0;
//lcd xpos &amp; ypos
assign        lcd_xpos        =         lcd_request ? (hcnt - (H_SYNC + H_BACK - H_AHEAD)) : 12'd0;
assign        lcd_ypos        =         lcd_request ? (vcnt - (V_SYNC + V_BACK)) : 12'd0;

endmodule</code></pre>

<p>&nbsp;</p>

<p>&nbsp; &nbsp; 最后是显示模块(Display.v):该模块在数据有效期间将像素数据按坐标规律输出给显示器,这里一共定义四种播放模式。</p>

<p>&nbsp;</p>

<pre>
<code>`timescale 1ns/1ns

// Define colors RGB--8|8|8
`define RED                24'hFF0000
`define GREEN        24'h00FF00
`define BLUE        24'h0000FF
`define WHITE         24'hFFFFFF
`define BLACK         24'h000000
`define YELLOW        24'hFFFF00
`define CYAN        24'hFF00FF
`define ROYAL         24'h00FFFF

// Define Display Mode
// `define        VGA_HORIZONTAL_COLOR        // 八种颜色横彩条
// `define        VGA_VERTICAL_COLOR                // 八种颜色竖彩条
// `define        VGA_GRAY_GRAPH                        // 红色彩条2x5
`define        VGA_GRAFTAL_GRAPH                // lcd_data &lt;= lcd_xpos * lcd_ypos;

module Display
#(
        parameter H_DISP = 1280,
        parameter V_DISP = 1024
)
(
        inputwire                       clk,       
        inputwire                        rst_n,       
        inputwire                lcd_xpos,        //lcd horizontal coordinate
        inputwire                lcd_ypos,        //lcd vertical coordinate
       
        output reg        lcd_data        //lcd data
);

`ifdef VGA_HORIZONTAL_COLOR
always@(posedge clk or negedge rst_n)
begin
        if(!rst_n)
                lcd_data &lt;= 24'h0;
        else
                begin
                if        (lcd_ypos &gt;= 0 &amp;&amp; lcd_ypos &lt; (V_DISP/8)*1)
                        lcd_data &lt;= `RED;
                else if(lcd_ypos &gt;= (V_DISP/8)*1 &amp;&amp; lcd_ypos &lt; (V_DISP/8)*2)
                        lcd_data &lt;= `GREEN;
                else if(lcd_ypos &gt;= (V_DISP/8)*2 &amp;&amp; lcd_ypos &lt; (V_DISP/8)*3)
                        lcd_data &lt;= `BLUE;
                else if(lcd_ypos &gt;= (V_DISP/8)*3 &amp;&amp; lcd_ypos &lt; (V_DISP/8)*4)
                        lcd_data &lt;= `WHITE;
                else if(lcd_ypos &gt;= (V_DISP/8)*4 &amp;&amp; lcd_ypos &lt; (V_DISP/8)*5)
                        lcd_data &lt;= `BLACK;
                else if(lcd_ypos &gt;= (V_DISP/8)*5 &amp;&amp; lcd_ypos &lt; (V_DISP/8)*6)
                        lcd_data &lt;= `YELLOW;
                else if(lcd_ypos &gt;= (V_DISP/8)*6 &amp;&amp; lcd_ypos &lt; (V_DISP/8)*7)
                        lcd_data &lt;= `CYAN;
                else
                        lcd_data &lt;= `ROYAL;
                end
end
`endif

`ifdef VGA_VERTICAL_COLOR
always@(posedge clk or negedge rst_n)
begin
        if(!rst_n)
                lcd_data &lt;= 24'h0;
        else
                begin
                if        (lcd_xpos &gt;= 0 &amp;&amp; lcd_xpos &lt; (H_DISP/8)*1)
                        lcd_data &lt;= `RED;
                else if(lcd_xpos &gt;= (H_DISP/8)*1 &amp;&amp; lcd_xpos &lt; (H_DISP/8)*2)
                        lcd_data &lt;= `GREEN;
                else if(lcd_xpos &gt;= (H_DISP/8)*2 &amp;&amp; lcd_xpos &lt; (H_DISP/8)*3)
                        lcd_data &lt;= `BLUE;
                else if(lcd_xpos &gt;= (H_DISP/8)*3 &amp;&amp; lcd_xpos &lt; (H_DISP/8)*4)
                        lcd_data &lt;= `WHITE;
                else if(lcd_xpos &gt;= (H_DISP/8)*4 &amp;&amp; lcd_xpos &lt; (H_DISP/8)*5)
                        lcd_data &lt;= `BLACK;
                else if(lcd_xpos &gt;= (H_DISP/8)*5 &amp;&amp; lcd_xpos &lt; (H_DISP/8)*6)
                        lcd_data &lt;= `YELLOW;
                else if(lcd_xpos &gt;= (H_DISP/8)*6 &amp;&amp; lcd_xpos &lt; (H_DISP/8)*7)
                        lcd_data &lt;= `CYAN;
                else
                        lcd_data &lt;= `ROYAL;
                end
end
`endif

`ifdef VGA_GRAFTAL_GRAPH
always@(posedge clk or negedge rst_n)
begin
        if(!rst_n)
                lcd_data &lt;= 24'h0;
        else
                lcd_data &lt;= lcd_xpos * lcd_ypos;
end
`endif


`ifdef VGA_GRAY_GRAPH
always@(posedge clk or negedge rst_n)
begin
        if(!rst_n)
                lcd_data &lt;= 24'h0;
        else
                begin
                if(lcd_ypos &lt; V_DISP/2)
                        lcd_data &lt;= {lcd_ypos, lcd_ypos, lcd_ypos};
                else
                        lcd_data &lt;= {lcd_xpos, lcd_xpos, lcd_xpos};
                end
end
`endif

endmodule</code></pre>

<p>&nbsp; &nbsp; 以下为四种显示效果,后续在发个VGA显示的【实战篇】,通过按键控制VGA输出显示的模式(下图四种),另一个按键控制VGA输出的分辨率:</p>

<p> &nbsp;</p>

Jacktang 发表于 2022-8-4 07:35

<p>四种显示效果可以,期待后续的VGA显示测试</p>

1nnocent 发表于 2022-8-4 08:55

Jacktang 发表于 2022-8-4 07:35
四种显示效果可以,期待后续的VGA显示测试

<p>安路这款FPGA的PLL IP核产生的频率不是很准,目前只能做两种分辨率的切换,本来想做个四五种分辨率按键控制切换的,产生的五个时钟,只有两个符合要求</p>

littleshrimp 发表于 2022-8-4 09:40

1nnocent 发表于 2022-8-4 08:55
安路这款FPGA的PLL IP核产生的频率不是很准,目前只能做两种分辨率的切换,本来想做个四五种分辨率按键控 ...

<p>PLL IP不能生成任意频率,有些频率会存在误差,具体可以从IP配置界面里看到。</p>

1nnocent 发表于 2022-8-4 11:09

littleshrimp 发表于 2022-8-4 09:40
PLL IP不能生成任意频率,有些频率会存在误差,具体可以从IP配置界面里看到。

<p>好的</p>

<p>&nbsp;</p>

<p>&nbsp;</p>

<p>了解</p>

<p>&nbsp;</p>
页: [1]
查看完整版本: 08、安路SparkRoad国产FPGA测评【学习篇】VGA显示