VGA信號詳解(VHDL版)

1. 原理簡述

VGA即視頻圖形陣列(Video Graphics Array),是一種使用模擬信號的視頻圖像顯示標準,最初是爲CRT顯示器而設計。

CRT顯示器,又稱陰極射線顯像管,主要由五部分組成:電子槍、偏轉線圈、蔭罩、高壓石墨電極、熒光粉塗層及玻璃外殼。

工作原理如下:
顯像管的熒光屏上塗滿了按一定方式緊密排列的紅、綠、藍三種顏色的熒光粉點或熒光粉條,稱爲熒光粉單元;相鄰的紅、綠、藍熒光粉單元爲一組,稱之爲像素。
工作時,燈絲加熱陰極,陰極發射電子,然後在加速極電場的作用下,經聚焦成爲很細的電子束,在陽極高壓作用下,獲得巨大的能量,以極高的速度去轟擊熒光粉層。電子槍發射的電子束不是一束,而是三束,受電腦顯卡RGB三個基色視頻信號電壓的控制。受到高速電子束的激發,熒光粉單元發出強弱不同的紅、綠、藍三種光。
蔭罩上面有很多小孔或細槽,和同一組的熒光粉單元相對應。掃描過程中,通過蔭罩來保證三支電子束準確擊中每一個像素。
偏轉線圈協助電子槍完成高速的掃描動作,使得圖像在熒光屏上連續不斷的出現。

VGA接口又稱D-sub接口,是VGA標準輸出數據的專用接口。接口共有15針,分成3排, 每排5個孔,是顯卡上輸出模擬信號的接口。

2. 接口示意


圖1 VGA接口示意圖
接口編號 功能
1 紅色視頻信號
2 綠色視頻信號
3 藍色視頻信號
4 地址碼bit2
5 自測試(各家定義不同)
6 紅色視頻信號地線
7 綠色視頻信號地線
8 藍色視頻信號地線
9 保留(各家定義不同)
10 同步信號地線
11 地址碼bit0
12 地址碼bit1/SDA
13 水平/複合同步信號
14 垂直同步信號
15 地址碼bit3/SCL

3. 時序標準

3.1. 基本時序說明

CRT掃描方式有多種,有直線掃描,圓形掃描等。直線掃描又分爲逐行掃描和隔行掃描。具體的掃描方式由顯示器而定,這裏僅介紹直線掃描中的逐行掃描。

直線逐行掃描是從屏幕左上角開始,從左至右逐點掃描。每掃描完一行後,電子束回到下一行的最左端,在這期間,需要對電子束進行行消隱,每行結束時,用行同步信號進行同步;當掃描完所有的行時,形成一幀圖像,用場同步信號進行場同步,並使掃描回到屏幕左上方,同時進行場消隱,開始下一幀。完成一行掃描的時間稱爲水平掃描時間,其倒數稱爲行頻率;完成一幀掃描的時間稱爲垂直掃描時間,其倒數稱爲場頻率。標準的VGA顯示的場頻率爲60Hz。
VGA工業標準顯示模式要求:行、場同步都爲負極性,即同步脈衝要求是負脈衝。綜合上述邏輯,其行、場時序如下:


圖2 行時序

圖3 場時序

vga信號中不存在點像素的頻率,因此具體的工作頻率需要通過分辨率和場頻率計算得來。以[email protected](60Hz)爲例,每場對應806個行週期,其中有768個顯示行;每顯示行包括1344個點時鐘,其中有1024個有效顯示點。故點時鐘頻率約爲1344x806x60=64995840Hz,即約爲65MHz。下面是一些常用的VGA分辨率時序參數:

顯示模式 a b c d e f g h i k
640x480@60Hz 96 48 640 16 800 2 33 480 10 525
800x600@60Hz 128 88 800 40 1056 4 23 600 1 623
1024x768@60Hz 136 160 1024 24 1344 6 29 768 3 806
1280x720@60Hz 40 220 1280 110 1650 5 20 720 5 750
1280x1024@60Hz 112 248 1280 48 1688 3 38 1024 1 1066
1920x1080@60Hz 44 148 1920 88 2200 5 36 1080 4 1125

注意:上表參數單位爲像素,字母含義對比時序圖理解(a/f爲同步脈衝,b/g爲顯示後沿,c/h爲顯示時序段,d/i爲顯示前沿,e/k爲總時序)。

3.2. 地址碼線說明

該部分主要是爲即插即用技術服務的。

如果支持DDC功能,那麼將允許主機和顯示器之間進行通信,顯示器將把一系列數值信息傳給主機,當顯卡接收到這部分信息後,會根據顯示器的參數自動進行匹配,實現即插即用。同時,還能夠利用該功能對顯示器的故障等方面進行檢測。

關於此部分驅動的實現,我暫時還沒有用FPGA實現過,也沒有找到可供附上的例文,後續若有涉獵,會更新附上。若有小夥伴需要用到這方面,可以通過參考arm微處理器的顯示驅動來尋找靈感,亦可使用FPGA與arm的聯合開發來實現。

4. 硬件連接

紅色視頻信號、綠色視頻信號與藍色視頻信號輸入均爲模擬信號,採用RS343電平標準。

一般在使用FPGA驅動VGA顯示時,需要配有相應的驅動電路。現在許多FPGA開發板都配有VGA接口電阻網絡,能夠較好的滿足VGA模擬信號的輸出。具體阻抗匹配辦法,可以參考VGA 接口電阻網絡阻抗匹配

5. 示例代碼及上機驗證

下示VGA信號的行爲級建模(vhdl居然不高亮):

library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
use ieee.std_logic_arith.all;

------------------------------------------------------------------------------------------------------------------------
-- 1024*768 60Hz VGA
------------------------------------------------------------------------------------------------------------------------
entity VGA is

    -- 參數設定
    generic(
        -- 水平掃描參數設定
        LinePeriod   : integer := 1344;     -- 行週期數
        H_SyncPulse  : integer :=  136;     -- 行同步脈衝(Sync a)
        H_BackPorch  : integer :=  160;     -- 顯示後沿(Back porch b)
        H_ActivePix  : integer := 1024;     -- 顯示時序段(Display interval c)
        H_FrontPorch : integer :=   24;     -- 顯示前沿(Front porch d)
        Hde_start    : integer :=  296;
        Hde_end      : integer := 1320;
        H_unitlength : integer :=    1;

        -- 垂直掃描參數設定
        FramePeriod  : integer := 806;      -- 列週期數
        V_SyncPulse  : integer :=   6;      -- 列同步脈衝(Sync o)
        V_BackPorch  : integer :=  29;      -- 顯示後沿(Back porch p)
        V_ActivePix  : integer := 768;      -- 顯示時序段(Display interval q)
        V_FrontPorch : integer :=   3;      -- 顯示前沿(Front porch r)
        Vde_start    : integer :=  35;
        Vde_end      : integer := 803;
        V_unitlength : integer :=   1
        );

	 -- 外部信號定義
    port(
    	-- 輸入端口
    	rst      : in  std_logic;
    	fpga_clk : in  std_logic;

    	-- 輸出端口
    	vga_hs   : out std_logic;
    	vga_vs   : out std_logic;
    	vga_r    : out std_logic_vector (4 downto 0);
    	vga_g    : out std_logic_vector (5 downto 0);
    	vga_b    : out std_logic_vector (4 downto 0)
    	);

end VGA;

architecture behavioral of VGA is

	-- 內部信號定義
    signal x_cnt        : integer range 0 to  1344;
    signal y_cnt		: integer range 0 to   806;
    signal data		    : integer range 0 to 65535;

    signal vga_r_reg	: std_logic_vector ( 4 downto 0);	
    signal vga_g_reg	: std_logic_vector ( 5 downto 0);
    signal vga_b_reg	: std_logic_vector ( 4 downto 0);
	 signal data_reg  : std_logic_vector (15 downto 0);

    signal hsync_r		: std_logic;
    signal vsync_r		: std_logic;
    signal hsync_de		: std_logic;
    signal vsync_de		: std_logic;
    signal vga_clk		: std_logic;

    -- 器件聲明
    component PLL
        port(
            CLK_IN1  : in  std_logic;
            CLK_OUT1 : out std_logic;
            RESET    : in  std_logic;
            LOCKED   : out std_logic
            );
    end component;

begin

    -- 內部時鐘信號
    P0: PLL port map (CLK_IN1=>fpga_clk, CLK_OUT1=>vga_clk, RESET=>'0');

    -- 水平掃描計數
    process(vga_clk)
    begin
        if rising_edge(vga_clk) then
            if rst='0' then
                x_cnt <= 1;
            elsif x_cnt=LinePeriod then
                x_cnt <= 1;
            elsif rst='1' then
                x_cnt <= x_cnt+H_unitlength;
            end if;
        end if;
    end process;

    -- 垂直掃描計數
    process(vga_clk)
    begin
        if rising_edge(vga_clk) then
            if rst='0' then
                y_cnt <= 1;
            elsif y_cnt=FramePeriod then
                y_cnt <= 1;
            elsif x_cnt=LinePeriod then
                y_cnt <= y_cnt+V_unitlength;
            end if;
        end if;
    end process;

    -- 水平掃描信號
    process(vga_clk)
    begin
        if rising_edge(vga_clk) then
            if rst='0' then
                hsync_r <= '1';
            elsif x_cnt=H_unitlength then
                hsync_r <= '0';
            elsif x_cnt=H_SyncPulse then
                hsync_r <= '1';
            end if;
        end if ;
    end process;

    process(vga_clk)
    begin
        if rising_edge(vga_clk) then
            if rst='0' then
                hsync_de <= '0';
            elsif x_cnt=Hde_start then
                hsync_de <= '1';
            elsif x_cnt=Hde_end then
                hsync_de <= '0';
            end if;
        end if ;
    end process;

    -- 垂直掃描信號
    process(vga_clk)
    begin
        if rising_edge(vga_clk) then
            if rst='0' then
                vsync_r <= '1';
            elsif y_cnt=V_unitlength then
                vsync_r <= '0';
            elsif y_cnt=V_SyncPulse then
                vsync_r <= '1';
            end if;
        end if ;
    end process;

    process(vga_clk)
    begin
        if rising_edge(vga_clk) then
            if rst='0' then
                vsync_de <= '0';
            elsif y_cnt=Vde_start then
                vsync_de <= '1';
            elsif y_cnt=Vde_end then
                vsync_de <= '0';
            end if;
        end if ;
    end process;

    -- 外部連接
    with (hsync_de and vsync_de) select
    vga_r <= vga_r_reg when '1',
             "00000" when '0';

    with (hsync_de and vsync_de) select
    vga_g <= vga_g_reg when '1',
             "000000" when '0';

    with (hsync_de and vsync_de) select
    vga_b <= vga_b_reg when '1',
             "00000" when '0';

    vga_hs <= hsync_r;
    vga_vs <= vsync_r;

    -- 彩條圖像
    process(vga_clk)
    begin
        if falling_edge(vga_clk) then
            if x_cnt=300 then
                data <= 16#f800#;
            elsif x_cnt=420 then
                data <= 16#07e0#;
            elsif x_cnt=540 then
                data <= 16#001f#;
            elsif x_cnt=660 then
                data <= 16#f81f#;
            elsif x_cnt=780 then
                data <= 16#ffe0#;
            elsif x_cnt=900 then
                data <= 16#07ff#;
            elsif x_cnt=1020 then
                data <= 16#ffff#;
            elsif x_cnt=1140 then
                data <= 16#fc00#;
            elsif x_cnt=1260 then
                data <= 16#0000#;
            end if;
        end if;
    end process;

    -- 圖像顯示
	 data_reg <= conv_std_logic_vector(data, 16);
	 
    process(vga_clk, rst)
    begin
        if rst='0' then
            vga_r_reg <= "00000";
            vga_g_reg <= "000000";
            vga_b_reg <= "00000";
        elsif falling_edge(vga_clk) then
            vga_r_reg <= data_reg(15 downto 11);
            vga_g_reg <= data_reg(10 downto 5);
            vga_b_reg <= data_reg(4 downto 0);
        end if;
    end process;

end behavioral;

仿真結果如下:


圖4 仿真結果

上面那個好像也看不出來啥,下面是上機驗證的結果:

圖5 上機結果

6. 注意事項

上示代碼中,pll的rst端直接被我拉低了,我使用的實驗板型號是Xilinx Spartan6的XC6SLX9,不同型號器件可能存在差異,請詳細閱讀IP核的數據手冊。

另注意,原代碼中pll的rst端被我接了其他輸入信號,在ISE中調用pll時存在被鎖住的情況,查其原因是:在給pll輸入時鐘時,rst端的輸入信號存在"U"的時序,之後pll將會被一直鎖住。消除該段時序後,pll正常工作。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章