協議——VGA


  VGA(Video Graphics Array)是IBM在1987年隨PS/2機一起推出的一種視頻傳輸標准,具有分辨率高、顯示速率快、顏色豐富等優點,在彩色顯示器領域得到了廣泛的應用。不支持熱插拔,不支持音頻傳輸。對於一些嵌入式VGA顯示系統,可以在不使用VGA顯示卡和計算機的情況下,實現VGA圖像的顯示和控制。VGA顯示器具有成本低、結構簡單、應用靈活的優點。對於一名FPGA工程師,尤其是視頻圖像的方向的學習者,VGA協議是必須要掌握的。

 

一、外部接口

  由電路圖可以看到,VGA並沒有特殊的外部芯片,我們需要關注的其實只有5個信號:HS行同步信號,VS場同步信號,R紅基色,G綠基色,B藍基色。下面慢慢解釋這些信號。

 

二、色彩原理

  經過九年義務教育的我們都應該聽過三基色,還給老師了的那就在再復習一下。三基色是指通過其他顏色的混合無法得到的“基本色”由於人的肉眼有感知紅、綠、藍三種不同顏色的錐體細胞,因此色彩空間通常可以由三種基本色來表達。這是色度學的最基本原理,即三基色原理。三種基色是相互獨立的,任何一種基色都不能有其它兩種顏色合成。紅綠藍是三基色,這三種顏色合成的顏色范圍最為廣泛。我們的RGB信號真是三基色的運用,對這三個信號賦予不同的數值,混合起來便是不同的色彩。

  設計RGB信號時,既可以R信號、G信號和B信號獨立的賦值,最后連到端口上,也可以直接用RGB當做一個整體信號,RGB信號在使用時的位寬有三種常見格式,以你的VGA解碼芯片的配置有關。

  1. RGB_8,R:G:B = 3:3:2,即RGB332

  2. RGB_16,R:G:B = 5:6:5,即RGB565

  3. RGB_24,R:G:B = 8:8:8,即RGB888

 

三、掃描方式

  VGA顯示器掃描方式分為逐行掃描和隔行掃描:逐行掃描是掃描從屏幕左上角一點開始,從左像右逐點掃描,每掃描完一行,電子束回到屏幕的左邊下一行的起始位置,在這期間,CRT對電子束進行消隱,每行結束時,用行同步信號進行同步;當掃描完所有的行,形成一幀,用場同步信號進行場同步,並使掃描回到屏幕左上方,同時進行場消隱,開始下一幀。隔行掃描是指電子束掃描時每隔一行掃一線,完成一屏后在返回來掃描剩下的線,隔行掃描的顯示器閃爍的厲害,會讓使用者的眼睛疲勞。因此我們一般都采用逐行掃描的方式。

  掃描原理如下所示:

 

四、行場信號

  行場信號共有 4 種模式,即 hsync 和 vsync 的高低狀態不同,如下所示:

  一開始看這些時序圖可能看不懂,它是把行場信號繪制在同一張圖里,說明行場信號的控制是相似的,只是時間參數不一樣而已。如果展開的話,其實時序是這樣的:

  這樣就清楚了,大致是若干個HS信號才組合而成一個VS,如果在一副圖片中,那正確的時序表示方式應該如下圖這樣。

  現在稍稍解釋一下這些參數。SYNC是“信號同步”,Back proch和Left border常常加在一起稱為“顯示后沿”,Addressable video為“顯示區域”,Right porder和Front porch常常加在一起稱為“顯示前沿”,一個時序其實就是先拉高一段較短的“信號同步”時間,然后拉低一段很長的時間,這就是一個回合。同時需要注意,其實也可以完全相反。即先拉低一段時間“信號同步”時間,然后拉高一段很長的時間。

  具體這些時間參數是怎么來的呢?且看下文。

 

五、規格參數

  直接拿數據手冊說話!

  以上是 640x480 @60Hz 的參數表,對着這個表即可確定時間。如果為了嫌麻煩,也可以先計算好寫在代碼里。

//**************************************************************************
// *** 名稱 : VGA_driver.v
// *** 作者 : xianyu_FPGA
// *** 博客 : https://www.cnblogs.com/xianyufpga/
// *** 日期 : 2019-06-26
// *** 描述 : VGA驅動模塊,VGA_req和VGA_x、VGA_y信號一般不同時使用
//**************************************************************************

module VGA_driver
//========================< 端口 >==========================================
(
//system ----------------------------------------
input   wire                clk                 , //時鍾,25Mhz
input   wire                rst_n               , //復位,低電平有效
//VGA_display -----------------------------------
input   wire  [15:0]        VGA_din             , //得到圖像數據
output  wire                VGA_req             , //請求圖像數據
output  wire  [10:0]        VGA_x               , //請求顯示區域橫坐標
output  wire  [10:0]        VGA_y               , //請求顯示區域縱坐標
//VGA output ------------------------------------
output  wire                VGA_clk             , //VGA接口時鍾信號
output  wire                VGA_blank           , //VGA接口空白信號,低有效
output  wire                VGA_de                , //VGA接口使能信號,高有效
output  wire                VGA_hsync           , //VGA接口行信號
output  wire                VGA_vsync           , //VGA接口場信號
output  wire  [15:0]        VGA_data              //VGA接口數據信號
);
//========================< 參數 >==========================================
/*                      640x480 @60Hz 25Mhz
//---------------------------------------------------------------
parameter H_TOTAL           = 800                               ; //行掃描周期
parameter H_ADDR            = 640                               ; //行有效數據
parameter H_RIGHT_BORDER    = 8                                 ; 
parameter H_FRONT_PORCH     = 8                                 ;
parameter H_FRONT           = H_RIGHT_BORDER + H_FRONT_PORCH    ; //行顯示前沿
parameter H_SYNC            = 96                                ; //行同步
parameter H_BACK_PORCH      = 40                                ;
parameter H_LEFT_BORDER     = 8                                 ;
parameter H_BACK            = H_BACK_PORCH + H_LEFT_BORDER      ; //行顯示后沿
//-----------------------
parameter V_TOTAL           = 525                               ; //場掃描周期
parameter V_ADDR            = 480                               ; //場有效數據
parameter V_BOTTOM_BORDER   = 8                                 ;
parameter V_FRONT_PORCH     = 2                                 ;
parameter V_FRONT           = V_BOTTOM_BORDER + V_FRONT_PORCH   ; //場顯示前沿
parameter V_SYNC            = 2                                 ; //場同步
parameter V_BACK_PORCH      = 25                                ;
parameter V_TOP_BORDER      = 8                                 ;
parameter V_BACK            = V_BACK_PORCH + V_TOP_BORDER       ; //場顯示后沿
//--------------------------------------------------------------- */
//                      1024x768 @60Hz 65Mhz
//---------------------------------------------------------------
parameter H_TOTAL           = 1344                              ; //行掃描周期
parameter H_ADDR            = 1024                              ; //行有效數據
parameter H_RIGHT_BORDER    = 0                                 ; 
parameter H_FRONT_PORCH     = 24                                ;
parameter H_FRONT           = H_RIGHT_BORDER + H_FRONT_PORCH    ; //行顯示前沿
parameter H_SYNC            = 136                               ; //行同步
parameter H_BACK_PORCH      = 160                               ;
parameter H_LEFT_BORDER     = 0                                 ;
parameter H_BACK            = H_BACK_PORCH + H_LEFT_BORDER      ; //行顯示后沿
//-----------------------
parameter V_TOTAL           = 806                               ; //場掃描周期
parameter V_ADDR            = 768                               ; //場有效數據
parameter V_BOTTOM_BORDER   = 0                                 ;
parameter V_FRONT_PORCH     = 3                                 ;
parameter V_FRONT           = V_BOTTOM_BORDER + V_FRONT_PORCH   ; //場顯示前沿
parameter V_SYNC            = 6                                 ; //場同步
parameter V_BACK_PORCH      = 29                                ;
parameter V_TOP_BORDER      = 0                                 ;
parameter V_BACK            = V_BACK_PORCH + V_TOP_BORDER       ; //場顯示后沿
//---------------------------------------------------------------
/*                      1280x720 @60Hz 74.25Mhz
//---------------------------------------------------------------
parameter H_TOTAL           = 1650                              ; //行掃描周期
parameter H_ADDR            = 1280                              ; //行有效數據
parameter H_RIGHT_BORDER    = 0                                 ; 
parameter H_FRONT_PORCH     = 110                               ;
parameter H_FRONT           = H_RIGHT_BORDER + H_FRONT_PORCH    ; //行顯示前沿
parameter H_SYNC            = 40                                ; //行同步
parameter H_BACK_PORCH      = 220                               ;
parameter H_LEFT_BORDER     = 0                                 ;
parameter H_BACK            = H_BACK_PORCH + H_LEFT_BORDER      ; //行顯示后沿
//-----------------------
parameter V_TOTAL           = 750                               ; //場掃描周期
parameter V_ADDR            = 720                               ; //場有效數據
parameter V_BOTTOM_BORDER   = 0                                 ;
parameter V_FRONT_PORCH     = 5                                 ;
parameter V_FRONT           = V_BOTTOM_BORDER + V_FRONT_PORCH   ; //場顯示前沿
parameter V_SYNC            = 5                                 ; //場同步
parameter V_BACK_PORCH      = 20                                ;
parameter V_TOP_BORDER      = 0                                 ;
parameter V_BACK            = V_BACK_PORCH + V_TOP_BORDER       ; //場顯示后沿
//--------------------------------------------------------------- */
//========================< 信號 >==========================================
reg   [10:0]                cnt_h               ;
wire                        add_cnt_h           ;
wire                        end_cnt_h           ;
reg   [10:0]                cnt_v               ;
wire                        add_cnt_v           ;
wire                        end_cnt_v           ;
//==========================================================================
//==    行、場計數
//==========================================================================
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        cnt_h <= 0;
    else if(add_cnt_h) begin
        if(end_cnt_h)
            cnt_h <= 0;
        else
            cnt_h <= cnt_h + 1;
    end
end

assign add_cnt_h = 1;
assign end_cnt_h = add_cnt_h && cnt_h==H_TOTAL-1;

always @(posedge clk or negedge rst_n) begin 
    if(!rst_n)
        cnt_v <= 0;
    else if(add_cnt_v) begin
        if(end_cnt_v)
            cnt_v <= 0;
        else
            cnt_v <= cnt_v + 1;
    end
end

assign add_cnt_v = end_cnt_h;
assign end_cnt_v = add_cnt_v && cnt_v==V_TOTAL-1;
//==========================================================================
//==    數據請求和數據坐標
//==========================================================================
//VGA請求
assign VGA_req = (cnt_h >= H_SYNC + H_BACK - 1) && (cnt_h < H_SYNC + H_BACK + H_ADDR - 1) &&
                 (cnt_v >= V_SYNC + V_BACK    ) && (cnt_v < V_SYNC + V_BACK + V_ADDR    )
                 ? 1 : 0;
//VGA坐標
assign VGA_x = VGA_req ? (cnt_h - (H_SYNC + H_BACK - 1'b1)) : 10'd0;
assign VGA_y = VGA_req ? (cnt_v - (V_SYNC + V_BACK - 1'b1)) : 10'd0;
//==========================================================================
//==    VGA output
//==========================================================================
//時鍾
assign VGA_clk = ~clk;

//行場
assign VGA_hsync = (cnt_h < H_SYNC) ? 0 : 1;
assign VGA_vsync = (cnt_v < V_SYNC) ? 0 : 1;

//空白,低有效,可以看成是de
assign VGA_blank = VGA_hsync & VGA_vsync; //或=VGA_de

//使能,高有效,非VGA必須,但大多數LCD屏都需要
assign VGA_de = (cnt_h >= H_SYNC + H_BACK) && (cnt_h < H_SYNC + H_BACK + H_ADDR) &&
                (cnt_v >= V_SYNC + V_BACK) && (cnt_v < V_SYNC + V_BACK + V_ADDR)
                ? 1 : 0;

//數據
assign VGA_data = VGA_de ? VGA_din : 16'b0;




endmodule

  注意一下,VGA 的驅動電路常用的有 2 種:

(1) R-2R 電阻模擬電路設計方案

  該方案更便宜,在 1024x768@60hz 及以下的分辨率條件下穩定運行,多見於 16 位的 VGA 接口中,通常不需要 VGA_clk 和 VGA_blank 信號。

(2)專用視頻轉換 DAC 芯片實現 VGA電路方案

  各方面都更牛逼,常見於 24 位的 VGA 接口中,通常需要 VGA_clk 和 VGA_blank 信號,有些還有 VGA_sync 信號,但該信號一般在電路上就接地了。

  本代碼中對這些信號都進行了設計,要用就用,不用就不連到 FPGA 引腳就行。如果需要 24 位,只需要修改輸入輸出的位寬即可。

 

六、實例講解

  最近使用的開發板帶了一個TFT屏,分辨率為480x272,其顯示原理和VGA接口完全相同,因此拿這個屏幕編寫一段程序看看。

  1 //==========================================================================
  2 // --- 名稱 : TFT_driver.v
  3 // --- 作者 : xianyu_FPGA
  4 // --- 日期 : 2019-01-03
  5 // --- 描述 : TFT顯示屏控制器,分辨率480x272,顯示三個豎着的彩條
  6 //==========================================================================
  7 
  8 module TFT_driver
  9 //=====================<端口聲明>===========================================
 10 (
 11 //input -------------------------------------
 12 input  wire             clk                 , //時鍾,9Mhz
 13 input  wire             rst_n               , //復位,低電平有效
 14 //user interfaces ---------------------------
 15 output wire             TFT_req             , //輸出請求信號
 16 input  wire [15:0]      data                , //得到圖像數據
 17 //output ------------------------------------
 18 output wire             TFT_clk             , //TFT像素時鍾
 19 output wire             TFT_de              , //TFT使能
 20 output wire             TFT_pwm             , //TFT背光控制
 21 output wire             TFT_hsync           , //TFT行同步信號
 22 output wire             TFT_vsync           , //TFT場同步信號
 23 output reg  [15:0]      TFT_rgb               //TFT像素輸出
 24 );
 25 //=====================<參數定義>===========================================
 26 //480x272 @60 9Mhz --------------------------
 27 parameter H_TOTAL       = 525               ; //行掃描周期
 28 parameter H_ADDR        = 480               ; //行有效數據
 29 parameter H_FRONT       = 2                 ; //行顯示前沿
 30 parameter H_SYNC        = 41                ; //行同步
 31 parameter H_BACK        = 2                 ; //行顯示后沿
 32 parameter V_TOTAL       = 286               ; //場掃描周期
 33 parameter V_ADDR        = 272               ; //場有效數據
 34 parameter V_FRONT       = 2                 ; //場顯示前沿
 35 parameter V_SYNC        = 10                ; //場同步
 36 parameter V_BACK        = 2                 ; //場顯示后沿
 37 
 38 //=====================<信號定義>===========================================
 39 //行場信號
 40 reg  [9:0]              cnt_h               ;
 41 wire                    add_cnt_h           ;
 42 wire                    end_cnt_h           ;
 43 reg  [9:0]              cnt_v               ;
 44 wire                    add_cnt_v           ;
 45 wire                    end_cnt_v           ;
 46 reg                     TFT_en              ;
 47 wire                    red_area            ;
 48 wire                    green_area          ;
 49 wire                    blue_area           ;
 50 
 51 //--------------------------------------------------------------------------
 52 //--   行、場計數
 53 //--------------------------------------------------------------------------
 54 always @(posedge clk or negedge rst_n) begin
 55     if(!rst_n)
 56         cnt_h <= 0;
 57     else if(add_cnt_h) begin
 58         if(end_cnt_h)
 59             cnt_h <= 0;
 60         else
 61             cnt_h <= cnt_h + 1;
 62     end
 63 end
 64 
 65 assign add_cnt_h = 1;
 66 assign end_cnt_h = add_cnt_h && cnt_h==H_TOTAL-1;
 67 
 68 always @(posedge clk or negedge rst_n) begin 
 69     if(!rst_n)
 70         cnt_v <= 0;
 71     else if(add_cnt_v) begin
 72         if(end_cnt_v)
 73             cnt_v <= 0;
 74         else
 75             cnt_v <= cnt_v + 1;
 76     end
 77 end
 78 
 79 assign add_cnt_v = end_cnt_h;
 80 assign end_cnt_v = add_cnt_v && cnt_v==V_TOTAL-1;
 81 
 82 //--------------------------------------------------------------------------
 83 //--    TFT請求信號和使能信號,注意時序的對齊
 84 //--------------------------------------------------------------------------
 85 assign TFT_req = (cnt_h >= H_SYNC + H_BACK - 1) && (cnt_h < H_SYNC + H_BACK + H_ADDR - 1) &&
 86                  (cnt_v >= V_SYNC + V_BACK    ) && (cnt_v < V_SYNC + V_BACK + V_ADDR    )
 87                  ? 1 : 0;
 88 
 89 always @(posedge clk) begin
 90     TFT_en <= TFT_req;
 91 end
 92 
 93 //--------------------------------------------------------------------------
 94 //--   行場信號
 95 //--------------------------------------------------------------------------
 96 assign TFT_hsync = (cnt_h < H_SYNC) ? 0 : 1;
 97 assign TFT_vsync = (cnt_v < V_SYNC) ? 0 : 1;
 98 
 99 //--------------------------------------------------------------------------
100 //--    其他信號
101 //--------------------------------------------------------------------------
102 assign TFT_clk = clk;
103 assign TFT_de  = TFT_en;
104 assign TFT_pwm = rst_n;
105 
106 //--------------------------------------------------------------------------
107 //--   rgb信號
108 //--------------------------------------------------------------------------
109 //assign TFT_rgb = TFT_en ? data : 0;
110 
111 always @(*) begin
112     if(TFT_en) begin
113 
114         if(red_area) begin                      //紅色區域
115             TFT_rgb <= 16'b11111_000000_00000;
116         end
117         else if(green_area) begin               //綠色區域
118             TFT_rgb <= 16'b00000_111111_00000;
119         end
120         else if(blue_area) begin                //藍色區域
121             TFT_rgb <= 16'b00000_000000_11111;
122         end
123         
124     end
125     else begin                                  //非顯示區域
126         TFT_rgb <= 0;
127     end
128 end
129 
130 
131 assign red_area   = cnt_h >= (H_SYNC + H_BACK) && cnt_h < (H_SYNC + H_BACK + H_ADDR*1/3) && 
132                     cnt_v >= (V_SYNC + V_BACK) && cnt_v < (V_SYNC + V_BACK + V_ADDR);
133 assign green_area = cnt_h >= (H_SYNC + H_BACK) && cnt_h < (H_SYNC + H_BACK + H_ADDR*2/3) && 
134                     cnt_v >= (V_SYNC + V_BACK) && cnt_v < (V_SYNC + V_BACK + V_ADDR);
135 assign blue_area  = cnt_h >= (H_SYNC + H_BACK) && cnt_h < (H_SYNC + H_BACK + H_ADDR*3/3) && 
136                     cnt_v >= (V_SYNC + V_BACK) && cnt_v < (V_SYNC + V_BACK + V_ADDR);
137 
138 
139                                  
140 
141 endmodule

 

  這個工程還包括頂層top模塊,pll分頻模塊,這些就不展示了。還一點是接口處的user interfaces的信號沒有使用到,而是自己通過代碼賦的值。工程最終正常運行,顯示出從左到右的三個豎彩條,其效果如下所示:

 

七、后記

  這樣只是簡單的使用了VGA,最終還是要以顯示視頻或圖像為目標,這就涉及到模塊之間的交互問題,下次再總結吧!

 

參考資料:

[1]開源騷客.VGA系列之一:VGA顯示驅動篇

[2]NingHeChuan.基於FPGA的VGA顯示靜態圖片

[3]威三學院FPGA教程

[4]袁玉卓, 曾凱鋒, 梅雪松. FPGA自學筆記:設計與驗證[M]. 北京航空航天出版社, 2017.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM