目錄
- 1、前言
- 2、設計思路和架構
-
- TMDS 編碼算法
- OSERDESE串并轉換
- 3、頂層源碼和IP封裝
- 4、源碼和IP擷取
1、前言
本設計使用Xilinx原語和自己手寫的代碼實作了HDMI發送功能,純verilog手寫,有源碼,也提供封裝好的IP,你喜歡用例化的方式就用源碼,你喜歡搭建BD工程就用IP,目前IP的适應器件為zynq,如果是用7系列FPGA的兄弟可以改一下晶片型号即可使用。
這個HDMI發送子產品我已在項目中多次使用,穩定性和實用性可以保證,最大支援[email protected]幀率。
本文較長的描述了設計方案,工程代碼編譯通過後上闆調試驗證,可直接項目移植,适用于在校學生、研究所學生項目開發,也适用于在職工程師做項目開發,可應用于醫療、軍工等行業的數字成像和圖像傳輸領域;
提供完整的、跑通的工程源碼和技術支援;
工程源碼和技術支援的擷取方式放在了文章末尾,請耐心看到最後;
關于HDMI的理論介紹這裡不多說,請自行搜尋,很簡單,本文隻講如何代碼層級實作的幹貨。
2、設計思路和架構
HDMI編碼框圖如下:
Encoder 子產品負責對資料進行編碼,Serializer 子產品對編碼後的資料進行并串轉換,最後
通過 OBUFDS 轉化成 TMDS 差分信号傳輸。
整個系統需要兩個輸入時鐘,一個是視訊的像素時鐘 Pixel Clk,另外一個時鐘 Pixel Clk x5 的頻率是像
素時鐘的五倍。由前面的簡介部分我們知道,并串轉換過程的實作的是 10:1 的轉換率,理論上轉換器需要一個 10 倍像素時鐘頻率的串行時鐘。這裡我們隻用了一個 5 倍的時鐘頻率,這是因為 OSERDESE2 子產品可以實作 DDR 的功能,即它在五倍時鐘頻率的基礎上又實作了雙倍資料速率。
TMDS 連接配接的時鐘通道我們采用與資料通道相同的并轉串邏輯來實作。通過對 10 位二進制序列10’b11111_00000 在 10 倍像素時鐘頻率下進行并串轉換,就可以得到像素時鐘頻率下的 TMDS 參考時鐘。
另外需要注意的是,圖中左下腳 HDMI 的音頻/附加資料輸入在本次實驗中并未用到,是以以虛線表示。
TMDS 編碼算法
Encoder 子產品完成TMDS 編碼算法,dvi_encoder 子產品按照 DVI 接口規範中 TMDS 編碼算法對輸入的 8 位像素資料以及 2 位行場同步信号進行編碼。該子產品是 Xilinx 應用筆記 XAPP460 中所提供的編碼子產品,其具體實作的編碼算法如下圖所示:
TMDS 通過邏輯算法将 8 位字元資料通過最小轉換編碼為 10 位字元資料,前 8 位資料由原始信号經運
算後獲得,第 9 位表示運算的方式,1 表示異或 0 表示異或非。經過 DC 平衡後(第 10 位),采用差分信号傳輸資料。第 10 位實際是一個反轉标志位,1 表示進行了反轉而 0 表示沒有反轉,進而達到 DC 平衡。
接收端在收到信号後,再進行相反的運算。TMDS 和 LVDS、TTL 相比有較好的電磁相容性能。這種算
法可以減小傳輸信号過程的上沖和下沖,而 DC 平衡使信号對傳輸線的電磁幹擾減少,可以用低成本的專用電纜實作長距離、高品質的數字信号傳輸。
算法中各個參數的含義如下圖所示:
Encoder 子產品頂層接口如下:
module dvi_encoder (
input clkin, // pixel clock input
input rstin, // async. reset input (active high)
input [7:0] din , // data inputs: expect registered
input c0 , // c0 input
input c1 , // c1 input
input de , // de input
output reg [9:0] dout // data outputs
);
代碼部分并不複雜,就是将TMDS 編碼算法的流程圖翻譯成了 verilog語言。
OSERDESE串并轉換
TMDS 編碼之後的資料由 Serializer 子產品進行并串轉換,通過調用 OSERDESE2 原語來實作 10:1 的并串轉換。原語是 Xilinx 器件底層硬體中的功能子產品,它使用專用的資源來實作一系列的功能。相比于 IP 核,原語的調用方法更簡單,但是一般隻用于實作一些簡單的功能。
需要注意的是,一個 OSERDESE2 隻能實作最多 8:1 的轉換率,在這裡我們通過位寬擴充實作了 10:1
的并串轉換,如下圖所示:
OSERDESE2 位寬擴充通過兩個 OSERDESE2 子產品來實作,其中一個作為 Master,
另一個作為 Slave,通過這種方式最多可實作 14:1 的并串轉換。需要注意的是,在位寬擴充時,Slave 子產品的資料輸入端隻能使用 D3 至 D8。
OSERDESE串并轉換子產品頂層接口如下:
module serializer_10_to_1(
input reset,
input paralell_clk,
input serial_clk_5x,
input [9:0] paralell_data,
output serial_data_out
);
3、頂層源碼和IP封裝
将各個功能子產品進行例化,即完成了本設計,為了适應不同的使用需求,提供源碼的同時也将源碼進行了IP封裝,兩者一并提供。
頂層源碼如下:
module helai_hdmi_out(
input clk_hdmi ,
input clk_hdmix5 ,
input reset_n ,
input i_vga_hs ,
input i_vga_vs ,
input i_vga_de ,
input [23:0] i_vga_rgb ,
output o_hdmi_clk_p ,
output o_hdmi_clk_n ,
output [2: 0] o_hdmi_data_p,
output [2: 0] o_hdmi_data_n
);
wire reset;
//²¢ÐÐÊý¾Ý
wire [9:0] red_10bit;
wire [9:0] green_10bit;
wire [9:0] blue_10bit;
wire [9:0] clk_10bit;
//´®ÐÐÊý¾Ý
wire [2:0] tmds_data_serial;
wire tmds_clk_serial;
//*****************************************************
//** main code
//*****************************************************
assign clk_10bit = 10'b1111100000;
//Òì²½¸´Î»£¬Í¬²½ÊÍ·Å
asyn_rst_syn reset_syn(
.reset_n (reset_n),
.clk (clk_hdmi),
.syn_reset (reset) //¸ßÓÐЧ
);
//¶ÔÈý¸öÑÕɫͨµÀ½øÐбàÂë
dvi_encoder encoder_b (
.clkin (clk_hdmi),
.rstin (reset),
.din (i_vga_rgb[7:0]),
.c0 (i_vga_hs),
.c1 (i_vga_vs),
.de (i_vga_de),
.dout (blue_10bit)
) ;
dvi_encoder encoder_g (
.clkin (clk_hdmi),
.rstin (reset),
.din (i_vga_rgb[15:8]),
.c0 (i_vga_hs),
.c1 (i_vga_vs),
.de (i_vga_de),
.dout (green_10bit)
) ;
dvi_encoder encoder_r (
.clkin (clk_hdmi),
.rstin (reset),
.din (i_vga_rgb[23:16]),
.c0 (i_vga_hs),
.c1 (i_vga_vs),
.de (i_vga_de),
.dout (red_10bit)
) ;
//¶Ô±àÂëºóµÄÊý¾Ý½øÐв¢´®×ª»»
serializer_10_to_1 serializer_b(
.reset (reset), // ¸´Î»,¸ßÓÐЧ
.paralell_clk (clk_hdmi), // ÊäÈë²¢ÐÐÊý¾ÝʱÖÓ
.serial_clk_5x (clk_hdmix5), // ÊäÈë´®ÐÐÊý¾ÝʱÖÓ
.paralell_data (blue_10bit), // ÊäÈë²¢ÐÐÊý¾Ý
// .paralell_data (10'h146), // ÊäÈë²¢ÐÐÊý¾Ý
.serial_data_out (tmds_data_serial[0]) // Êä³ö´®ÐÐÊý¾Ý
);
serializer_10_to_1 serializer_g(
.reset (reset),
.paralell_clk (clk_hdmi),
.serial_clk_5x (clk_hdmix5),
.paralell_data (green_10bit),
// .paralell_data (10'h146),
.serial_data_out (tmds_data_serial[1])
);
serializer_10_to_1 serializer_r(
.reset (reset),
.paralell_clk (clk_hdmi),
.serial_clk_5x (clk_hdmix5),
.paralell_data (red_10bit),
// .paralell_data (10'h146),
.serial_data_out (tmds_data_serial[2])
);
serializer_10_to_1 serializer_clk(
.reset (reset),
.paralell_clk (clk_hdmi),
.serial_clk_5x (clk_hdmix5),
.paralell_data (clk_10bit),
.serial_data_out (tmds_clk_serial)
);
//ת»»²î·ÖÐźÅ
OBUFDS #(
.IOSTANDARD ("TMDS_33") // I/Oµçƽ±ê׼ΪTMDS
) TMDS0 (
.I (tmds_data_serial[0]),
.O (o_hdmi_data_p[0]),
.OB (o_hdmi_data_n[0])
);
OBUFDS #(
.IOSTANDARD ("TMDS_33") // I/Oµçƽ±ê׼ΪTMDS
) TMDS1 (
.I (tmds_data_serial[1]),
.O (o_hdmi_data_p[1]),
.OB (o_hdmi_data_n[1])
);
OBUFDS #(
.IOSTANDARD ("TMDS_33") // I/Oµçƽ±ê׼ΪTMDS
) TMDS2 (
.I (tmds_data_serial[2]),
.O (o_hdmi_data_p[2]),
.OB (o_hdmi_data_n[2])
);
OBUFDS #(
.IOSTANDARD ("TMDS_33") // I/Oµçƽ±ê׼ΪTMDS
) TMDS3 (
.I (tmds_clk_serial),
.O (o_hdmi_clk_p),
.OB (o_hdmi_clk_n)
);
endmodule
封裝後的IP如下:
4、源碼和IP擷取
福利:工程代碼的擷取
代碼太大,無法郵箱發送,以某度網盤連結方式發送,
資料擷取方式:點選擷取
網盤資料如下: