1. 程式人生 > >HDMI系列之一:基於Nios II的HDMI顯示圖片

HDMI系列之一:基於Nios II的HDMI顯示圖片

一休哥將在本文中介紹一個基於Nios II的HDMI顯示圖片的工程。我將主要分三個部分來介紹這一工程,從而實現工程效果。
1、 Nios II的常規使用套路
2、 自定義HDMI IP核的製作
3、 Nios II顯示圖片
本文涉及到的全部資料連結:
連結:http://pan.baidu.com/s/1eRNXagy 密碼:uv9w

1 Nios II的常規使用套路

雖然使用Nios II可以為我們簡化編寫Verilog程式碼的難度。但往往需要額外更多的GUI介面操作。本文為了照顧到Nios II的初學者,將帶領大家重新建立一個基於Nios II的FPGA工程。
這裡寫圖片描述
上圖是基於Nios II的FPGA工程的簡要結構圖,其中屬於Nios II的部分有兩個,一個是Qsys系統,它是Nios II的硬體;一個是軟核工程,它是Nios II的軟體。
首先我們新建一個空的FPGA工程,然後點選軟體介面的Qsys按鈕,進入Qsys的建立介面。
這裡寫圖片描述


然後,在介面中,我們雙擊Clock Source IP核,修改裡面的引數,設定時鐘頻率為100MHz,並給這個IP核重新命名為clk。
這裡寫圖片描述
接著,我們新增一個Nios II Processor IP核,在左上角的Library的搜尋視窗中搜nios即可。然後重新命名為nios_qsys。
這裡寫圖片描述
然後,新增SDRAM Controller IP核,同樣搜尋之後,我們需要根據自己板子上SDRAM的型號來配置IP核。我使用的是鋯石科技的A4開發板,使用的SDRAM晶片為MT48LC16M16A2,所以僅供大家參考。最後同樣的重新命名為sdram。
這裡寫圖片描述
然後接著新增JTAG UART和System ID Peripheral IP核,無需進行任何配置,僅需要重新命名為jtag_uart和sysid_qsys即可。
這裡寫圖片描述

接著,我們新增自定義的HDMI IP核。大家在使用該IP時,需要將我提供的IP核檔案放在下圖這個路徑上。(嘿嘿,由於這個IP核是我仿照鋯石科技的VGA IP核而製作的,為了防止被鋯石大大給和諧掉,因此也放在同樣的IP核路徑中。)
這裡寫圖片描述
最後,我們可以在zircon_ip的IP組裡找到zircon_hdmi的IP了,直接呼叫就行了。
這裡寫圖片描述
至此,Qsys系統所需的IP核就呼叫完了,接下來的操作就是連線,連線主要有clk埠,reset埠,Nios的資料與指令埠和Avalon—MM埠。首先我們把除Clock Source IP核以外的IP核的時鐘埠連上。由於我們這個Qsys系統只有一個Clock Source IP核,所以當然是用這個IP提供的時鐘埠來連線其他IP核。然後將Clock Source IP核的復位埠連線上其他IP核。接著將Nios II Processor IP核的jtag_debug_module_reset復位埠連線上其他IP核。
這裡寫圖片描述

這裡寫圖片描述
這裡寫圖片描述
接著,我們來連線Nios的資料與指令埠,連線這個的時候有一個規律,就是有儲存功能的IP核需要同時連線資料與指令埠,如SDRAM,RAM,ROM,EPCS等,而其他外設只需要連線資料埠。
這裡寫圖片描述
最後,由於我製作的HDMI IP核是基於Avalon-MM協議的,通過這個協議可以實現HDMI IP核與Nios II Processor IP核和SDRAM Controller IP核的資料通訊,所以需要將HDMI IP核的Avalon—MM的主埠與SDRAM Controller IP核來讀取儲存在SDRAM中的圖片資料,HDMI IP核的Avalon—MM的從埠與Nios II Processor IP核的資料埠相連來接收Nios II軟體傳送的圖片地址資料。關於HDMI IP核具體的介紹,將會在後兩個部分中詳細介紹。
這裡寫圖片描述
完成所有的連線之後,需要雙擊Nios II Processor IP核,設定其復位向量和異常向量為sdram.s1。接著,設定jtag_uart IP核的中斷號,引出sdram IP和HDMI IP的引腳。點選軟體介面右上角的System下拉選單中的Assign Base Address自動分配各個IP的地址。點選軟體介面右上角的File下拉選單中的Save as儲存Qsys檔案。最後點選軟體介面右上角的Generate下拉選單中的Generate,生成Qsys系統。這樣,一個Qsys系統就生成完了。
這裡寫圖片描述
這裡寫圖片描述
這裡寫圖片描述
這裡寫圖片描述
然後,回到Quartus主介面,右鍵點選Files,新增新生成的Qsys.qip檔案。
這裡寫圖片描述
最後,我們的頂層檔案是這樣的,這樣我們的硬體工程就算建立完畢了。

module Qsys_Hdmi_Ip
( 
    //時鐘復位埠
    CLK_50M,RST_N,
    //SDRAM埠
    SDRAM_ADDR,SDRAM_BA,SDRAM_CAS_N,SDRAM_CLK,SDRAM_CKE,
    SDRAM_CS_N,SDRAM_DQ,SDRAM_DQM,SDRAM_RAS_N,SDRAM_WE_N,
    //HDMI埠
    HDMI_CLK,HDMI_RST_N,HDMI_HS,HDMI_VS,HDMI_DE,
    HDMI_RGB_R,HDMI_RGB_G,HDMI_RGB_B,HDMI_SCLK,HDMI_SDAT
);
//時鐘復位
input               CLK_50M;
input               RST_N;

// SDRAM Interface
output [12:0]   SDRAM_ADDR;
output [ 1:0]   SDRAM_BA;
output          SDRAM_CAS_N;
output          SDRAM_CLK;
output          SDRAM_CKE;
output          SDRAM_CS_N;
inout  [15:0]   SDRAM_DQ;
output [ 1:0]   SDRAM_DQM;
output          SDRAM_RAS_N;
output          SDRAM_WE_N;   

//VGA
output          HDMI_CLK;
output          HDMI_RST_N;
output          HDMI_HS;                    
output          HDMI_VS;    
output          HDMI_DE;            
output [ 7:0]   HDMI_RGB_R; 
output [ 7:0]   HDMI_RGB_G; 
output [ 7:0]   HDMI_RGB_B; 
output          HDMI_SCLK;                          //iic的時鐘訊號
inout           HDMI_SDAT;                          //iic的資料訊號

assign   HDMI_RST_N = 1'b1; 

wire                clk_100m;
wire                clk_30m;
wire                HDMI_CLK;

PLL                                     PLL_Init 
(
    .inclk0                             (CLK_50M        ),
    .c0                                 (clk_100m       ),
    .c1                                 (SDRAM_CLK      ),
    .c2                                 (clk_30m        )
);

PLL_Hdmi                                PLL_Hdmi_Init 
(
    .inclk0                             (clk_30m        ),
    .c0                                 (HDMI_CLK       )
);

Qsys_system                             Qsys_system_Init
(
    .clk_clk                            (clk_100m           ), //clk.clk
    .reset_reset_n                      (RST_N              ), //reset.reset_n
    .sdram_addr                         (SDRAM_ADDR         ), //sdram_conduit.addr
    .sdram_ba                           (SDRAM_BA           ), //             .ba
    .sdram_cas_n                        (SDRAM_CAS_N        ), //             .cas_n
    .sdram_cke                          (SDRAM_CKE          ), //             .cke
    .sdram_cs_n                         (SDRAM_CS_N         ), //             .cs_n
    .sdram_dq                           (SDRAM_DQ           ), //             .dq
    .sdram_dqm                          (SDRAM_DQM          ), //             .dqm
    .sdram_ras_n                        (SDRAM_RAS_N        ), //             .ras_n
    .sdram_we_n                         (SDRAM_WE_N         ), //             .we_n
    .zircon_avalon_hdmi_clk             (HDMI_CLK           ), //  zircon_hdmi.clk
    .zircon_avalon_hdmi_hsync           (HDMI_HS            ), //             .hsync
    .zircon_avalon_hdmi_vsync           (HDMI_VS            ), //             .vsync
    .zircon_avalon_hdmi_de              (HDMI_DE            ), //             .de
    .zircon_avalon_hdmi_rgb             (HDMI_RGB           ), //             .rgb
    .zircon_avalon_hdmi_sclk            (HDMI_SCLK          ), //             .sclk
    .zircon_avalon_hdmi_sdat            (HDMI_SDAT          )  //             .sdat
);

wire    [31:0]  HDMI_RGB;

assign  HDMI_RGB_R = {HDMI_RGB[23:18],2'b11};
assign  HDMI_RGB_G = {HDMI_RGB[15:10],2'b11};   
assign  HDMI_RGB_B = {HDMI_RGB[ 7: 2],2'b11};   

endmodule

2 自定義HDMI IP核的製作

在第一個部分,一休哥詳細的介紹瞭如何新建Nios II的硬體工程。大家可以發現,這個工程十分簡潔,兩個PLL模組用於產生100M時鐘,SDRAM的時鐘和HDMI的時鐘,一個Qsys系統的頂層模組。這個Qsys系統的頂層模組是我們在Qsys設計介面中生成的。所以,在這個工程中,不需要我們手動編寫Verilog邏輯程式碼嗎?嘿嘿,好像真的是這樣哦,這就是Nios的簡便之處吧,不需要編寫複雜程式碼就能夠控制SDRAM。
接下來,一休哥將著重介紹HDMI IP核的製作。首先我們來看下HDMI的硬體原理圖。HDMI晶片與FPGA相連的介面可以分為兩類,一類是IIC介面,一類是類似於VGA的視訊時序介面。驅動HDMI晶片需要兩個步驟,先使用IIC介面對HDMI晶片進行配置,然後使用視訊時序介面輸出視訊時序訊號。這些操作都是通過HDMI IP核來實現的。
這裡寫圖片描述
接下來,開啟HDMI IP核的資料夾。可以看到有5個v檔案,這就是HDMI IP核的硬體。字尾為hw的tcl檔案是硬體配置檔案,這一檔案是通過Qsys介面生成的,通過這個檔案可以讓Qsys系統生成時自動呼叫這5個v檔案融於系統中。
這裡寫圖片描述
其中zircon_avalon_hdmi 是HDMI IP核的硬體的頂層檔案,i2c_timing_ctrl是用來完成IIC配置的,zircon_avalon_hdmi_logic是用來模擬視訊時序的,並控制讀取zircon_avalon_hdmi_fifo的FIFO模組中的資料。zircon_avalon_hdmi_register是HDMI IP核內部的暫存器操作,通過Avalon-MM協議可以實現HDMI IP核與Nios II Processor IP核和SDRAM Controller IP核的資料通訊。關於每個v檔案具體的作用,大家可以參考鋯石科技推出的《軟核演練篇》系列教程中VGA IP核的相關內容,在這裡就不一一介紹了。
接下來,我教大家來製作字尾為hw的硬體配置tcl檔案。首先我們在路徑下將資料夾中的hw檔案刪除。這時開啟Qsys介面,大家可以發現,我們已經找不到HDMI IP核了。
這裡寫圖片描述
這裡寫圖片描述
然後,雙擊New Component,然後配置IP核的資訊。嘿嘿,由於我這個IP核有抄襲鋯石科技的嫌疑,在這裡允許我把建立人寫為zircon吧。(鋯石大大,別怪我)
這裡寫圖片描述
然後進入下一個介面,點選新增按鈕,將路徑中的那5個v檔案選中新增,並且將zircon_avalon_hdmi.v檔案設定為頂層檔案,點選Analysis Synthesis Files檔案。
這裡寫圖片描述
這裡寫圖片描述
這裡寫圖片描述
接著,來到最後一個Interfaces介面,針對avalon_slave介面、avalon_master介面和conduit_end介面做如下修改:改名和新增復位訊號。最後點選Finish完成IP核的硬體建立。
這裡寫圖片描述
這裡寫圖片描述
這裡寫圖片描述
最後在FPGA工程的資料夾目錄中可以找到zircon_avalon_hdmi_hw.tcl,然後開啟這個檔案,對檔案中的部分程式碼進行修改。然後將這個檔案剪下到源HDMI IP核路徑中即可。然後重新開啟Qsys介面,就可以重新搜尋到HDMI IP核了。
這裡寫圖片描述
這裡寫圖片描述
這裡寫圖片描述
這裡寫圖片描述
這裡寫圖片描述

3 Nios II顯示圖片

完成了上述兩個部分後,接下來我們來介紹Nios 軟體工程。首先在FPGA工程的根目錄下新建一個software資料夾。
這裡寫圖片描述
然後點選Quartus軟體介面中的Nios II Software Build Tools for Eclipse,在彈出來的軟體工程路徑中選擇C:\Users\Administrator\Desktop\Qsys_Hdmi_Ip\software。開始建立軟體工程。
這裡寫圖片描述
這裡寫圖片描述
接下來我們進入Nios II-Eclipse軟體介面,點選Nios II Application and BSP from Template,開始建立工程。
這裡寫圖片描述
這裡寫圖片描述
建立完工程後,首先我們點選更新BSP
這裡寫圖片描述
這裡寫圖片描述
此時在BSP目錄下就可以找到HDMI IP核的c和h檔案。問個問題,這些檔案是如何自動綜合到BSP中的呢?答案就是之前提到的HDMI IP核中的zircon_avalon_hdmi_sw.tcl檔案,裡面包含了所有c和h檔案的詳細路徑。
這裡寫圖片描述
其中,kai、qiong、qiu1、qiu2、shui和ying的h檔案是我使用軟體Img2Lcd將圖片轉換成陣列的(該軟體的使用方法在上篇博文中具體介紹過。)該軟體的具體配置如下,然後點選儲存為h檔案即可。可以看到,一休哥將640*480的24bit圖片轉換成了相應的陣列。由於一休哥採取的是24位深度轉換,所以得到的是一個長度為921600的無符號字元型陣列,每相連的3個8bit資料組成一個24bit的畫素值。
這裡寫圖片描述
HDMI IP核中,除了包含圖片資料外,還包含了IP核所必備的暫存器標頭檔案,功能函式庫的c和h檔案。
首先,我們從main函式開始看起。首先需要呼叫HDMI IP核初始化函式zircon_avalon_hdmi_init。這個函式非常簡單。第一步,對HDMI IP核的控制暫存器寫0,即預設HDMI IP核停止工作。第二步,對HDMI IP核的資料暫存器進行配置,將陣列hdmi_buffer的首地址值寫進去。第三步,對HDMI IP核的控制暫存器寫1,即讓DMI IP核開始工作。其中,hdmi_buffer是一個大小為640*480的深度為32bit的陣列,這一陣列是一個全域性變數,即它預先就在SDRAM中佔據了一塊固定的記憶體。通過將陣列hdmi_buffer的首地址值寫入HDMI IP核的資料暫存器。則HDMI IP核就會連續對SDRAM中這塊記憶體進行迴圈讀取操作(起始地址為陣列hdmi_buffer的首地址值,範圍大小為640*480,深度為32bit)
這裡寫圖片描述
這裡寫圖片描述
這裡寫圖片描述
進行完初始化之後,直接進入一個while的死迴圈中,在這裡就執行著顯示圖片的操作zircon_avalon_hdmi_DisplayPic1,這裡有六個顯示圖片函式,並且中間會有一段時間的延時。所以本工程的效果就是六張圖片來回切換。我們最後來介紹下zircon_avalon_hdmi_DisplayPic1函式,這個函式就是一個連續畫點函式,先將原圖片陣列的3個8bit資料拼成一個24bit資料,然後依次寫入陣列hdmi_buffer。
這裡寫圖片描述
這裡寫圖片描述
最後我們執行程式,來看一下效果。
這裡寫圖片描述
這裡寫圖片描述
這裡寫圖片描述
總結一下,學習Nios II,我們需要關心Nios II與IP核、IP核與SDRAM之間的資料交換,也就是Avalon-MM協議,這才是我們學習的重點所在。一般我們會用一個FIFO來快取資料(而不會用ram),解決跨時鐘域的資料交換問題。大家可以發現,在本工程的效果圖中,圖片切換的過程中存在明顯的馬賽克現象,這是因為SDRAM不支援同時讀寫的緣故,馬賽克的持續時間是由於Nios的讀寫速度造成的。因此,可以選擇將SDRAM換成可支援同時讀寫的大容量SRAM晶片,不過這個成本很高。最值得嘗試的解決辦法就是,可以嘗試使用DDR晶片,加快讀寫速度,縮短馬賽克現象的存在時間。