国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

【正點原子FPGA連載】第二十三章PS通過VDMA驅(qū)動LCD顯示實驗 摘自【正點原子】DFZU2EG_4EV MPSoC之嵌入式Vitis開發(fā)指南

這篇具有很好參考價值的文章主要介紹了【正點原子FPGA連載】第二十三章PS通過VDMA驅(qū)動LCD顯示實驗 摘自【正點原子】DFZU2EG_4EV MPSoC之嵌入式Vitis開發(fā)指南。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

1)實驗平臺:正點原子MPSoC開發(fā)板
2)平臺購買地址:https://detail.tmall.com/item.htm?id=692450874670
3)全套實驗源碼+手冊+視頻下載地址: http://www.openedv.com/thread-340252-1-1.html

第二十三章PS通過VDMA驅(qū)動LCD顯示實驗

AXI VDMA是Xilinx專門針對視頻應(yīng)用提供的一種高帶寬的解決方案,旨在實現(xiàn)AXI4-Stream視頻接口和AXI4接口之間的高帶寬接入,可以方便地實現(xiàn)雙緩沖和多緩沖機制。
本章我們將在PL端搭建VDMA的使用框架,并通過VDMA將PS端需要顯示的數(shù)據(jù)顯示在LCD上。本章包括以下幾個部分:
2323.1簡介
23.2實驗任務(wù)
23.3硬件設(shè)計
23.4軟件設(shè)計
23.5下載驗證

23.1簡介

AXI VDMA(AXI Video Direct Memory Access,以下簡稱VDMA),是Xilinx提供的軟核IP。其功能和AXI DMA(以下簡稱DMA)有些類似,都可以為存儲器或者AXI4-Stream類目標(biāo)外設(shè)之間提供高帶寬直接存儲器存取。和DMA相比,VDMA增加了幀緩存的緩沖機制和同步鎖相(GenLock)等功能,是為了針對視頻圖像應(yīng)用而做的升級版的DMA。VDMA集成了視頻專用功能,如幀同步和2D DMA傳輸?shù)龋浅_m合基于MPSOC架構(gòu)上的圖像和視頻處理,縮短了開發(fā)周期。
DMA和VDMA的對比如表 23.1.1所示。
表 23.1.1 DMA和VDMA的對比
黑金7z100 ps驅(qū)動lcd,正點原子,fpga開發(fā)

這里我們引入了幀緩存的緩沖機制和動態(tài)同步鎖相兩個概念,接下來向大家詳細介紹這兩個概念。
幀緩存
首先我們來看下什么是幀緩存。幀緩存存儲器(Frame Buffer),簡稱幀緩存,也常被稱作顯存,是為顯示設(shè)備(如HDMI顯示器、RGB LCD液晶屏等)提供數(shù)據(jù)緩存的一片存儲區(qū)域。一般圖像輸入源和圖像顯示的傳輸速率不匹配(如圖像輸入源傳輸速度較快或者圖像顯示端傳輸速度較快),這個時候需要一片存儲區(qū)域來緩存輸入的數(shù)據(jù),以便顯示設(shè)備讀取數(shù)據(jù),同時也方便后續(xù)對視頻數(shù)據(jù)做圖像處理。幀緩存的每一個存儲單元對應(yīng)屏幕上的一個像素,整個幀緩存對應(yīng)一幀圖像。
在實際應(yīng)用中,一般選擇外置的DDR存儲器作為幀緩存,而不是選擇片內(nèi)的BRAM,這是由于MPSOC片內(nèi)的BRAM存儲容量比較小。我們知道,XCZU2EG的BRAM存儲容量為5.3Mbit,XCZU4EV的存儲容量為4.5Mbit,假設(shè)RGB LCD屏的分辨率為800480,那么存儲一幀圖像需要的存儲容量為(如:RGB888數(shù)據(jù)格式=24位)800480*24= 9216000bit≈8.79Mbit,整個BRAM的存儲容量不足以存儲單幀圖像,更何況有時需要多個顯存的情況,因此使用外置的DDR存儲器作為幀緩存。
對于使用外置存儲器來緩存圖像數(shù)據(jù)來說,可以采用單幀緩存或者多幀緩存的方案。單幀緩存是指圖像的輸入和圖像的顯示都是通過讀寫同一片存儲區(qū)域來實現(xiàn)的。雖然也可以實現(xiàn)顯示設(shè)備顯示圖像的功能,但這會帶來一個問題,即讀出的單幀圖像是輸入的兩幀圖像或者更多幀圖像數(shù)據(jù)疊加在一起的結(jié)果,可能會導(dǎo)致顯示設(shè)備顯示的圖像出現(xiàn)割裂的現(xiàn)象。單幀緩存方案的讀寫示意圖如下圖所示:
黑金7z100 ps驅(qū)動lcd,正點原子,fpga開發(fā)

圖 23.1.1 單幀緩存方案讀寫示意圖
由上圖可知,隨著輸入數(shù)據(jù)源的不斷寫入,單幀存儲區(qū)域會出現(xiàn)單幀圖像數(shù)據(jù)或者兩幀圖像數(shù)據(jù)疊加在一起的情況。當(dāng)然,如果讀出速度比寫入速度慢很多的話,也可能出現(xiàn)更多幀圖像疊加在一起,從而導(dǎo)致顯示設(shè)備顯示的圖像出現(xiàn)割裂的現(xiàn)象。
對于本次RGB LCD屏彩條顯示實驗來說,由于彩條只需要寫入一次,寫入完成后,后續(xù)只需要進行讀操作,因此本次實驗使用單幀緩存的方案即可,那么采用DMA或者VDMA的方案區(qū)別不大。而對于需要頻繁寫入和讀出的應(yīng)用來說,比如攝像頭的圖像顯示,如果僅僅使用單個幀緩存的話,勢必會造成圖像疊加的情況,這個時候就要采用VDMA的多幀緩存的方案了。本次實驗旨在通過VDMA的RGB LCD屏顯示彩條的實驗,讓大家熟悉并掌握VDMA IP核的使用方法,為后續(xù)學(xué)習(xí)攝像頭的圖像顯示實驗打下基礎(chǔ)。
在VDMA IP核中,通過配置VDMA的同步鎖相模式來配置幀緩存的緩沖進制,接下來我們向大家介紹VDMA的同步鎖相模式。
同步鎖相(Genlock)
在很多的視頻應(yīng)用中,圖像輸入端和輸出端的數(shù)據(jù)速率不匹配,通常使用幀緩存來避免因速率不匹配而導(dǎo)致的潛在錯誤。為了解決單幀緩存區(qū)域帶來的圖像疊加問題,通過分配多個幀緩存區(qū)域保存數(shù)據(jù),圖像輸入端在寫入其中一個幀緩存時,輸出端讀取其它的幀緩存。
VDMA支持四種同步鎖相模式,分別是Genlock Master(同步鎖相主模式)、Genlock Slave(同步鎖從模式)、Dynamic Genlock Master(動態(tài)同步鎖相主模式)和Dynamic Genlock Slave(動態(tài)同步鎖相從模式)。
VDMA有一個寫通道(S2MM)和一個讀通道(MM2S),用戶通過寫通道將輸入端數(shù)據(jù)寫入幀緩存,通過讀通道將從幀緩存中讀出數(shù)據(jù)。VDMA的每一個通道都可以選擇以上四種模式中的一種,接下來我們分別向大家介紹這四種同步模式。
Genlock Master
當(dāng)寫通道(S2MM)或者讀通道(MM2S)配置為Genlock Master時,該通道不會跳過或者重復(fù)任一幀緩存區(qū)域,按照幀緩存順序讀出數(shù)據(jù)。配置為Genlock Slave的通道應(yīng)當(dāng)緊跟Genlock Master通道變化,但有一定的延遲,延遲的大小在寄存器(*frmdly_stride[28:24])中配置。
Genlock Slave
當(dāng)寫通道(S2MM)或者讀通道(MM2S)配置為Genlock Slave時,該通道會通過跳過或者重復(fù)一些幀緩存區(qū)域的方式,嘗試與Genlock Master通道同步。
Dynamic Genlock Master
當(dāng)寫通道(S2MM)或者讀通道(MM2S)配置為Dynamic Genlock Master時,該通道會跳過Dynamic Genlock Slave通道正在操作的幀緩存,通過跳過或者重復(fù)一些幀緩存區(qū)域的方式來完成。
這里以分配三個幀緩存為例。當(dāng)配置為Dynamic Genlock Master的通道訪問幀緩存時,沒有檢測到Slave通道訪問的幀緩存,那么它會循環(huán)訪問幀緩存0 1 2 0 1 2;而如果檢測到Slave訪問的幀緩存區(qū)域,它們它會跳過該區(qū)域并開始訪問下一幀緩存。因此,如果Slave通道長時間訪問幀緩存1,則Master會循環(huán)訪問幀緩存2和3。
Dynamic Genlock Slave
當(dāng)寫通道(S2MM)或者讀通道(MM2S)配置為Dynamic Genlock Slave時,該通道會操作 Dynamic Genlock Master通道上一周期操作的幀。
下面以配置為Genlock模式為例,寫通道(S2MM)配置為Genlock Master模式,讀通道(MM2S)配置為Genlock Slave模式,操作示意圖如圖 23.1.2所示:
黑金7z100 ps驅(qū)動lcd,正點原子,fpga開發(fā)

圖 23.1.2 Genlock Master/Slave示意圖
s2mm_frame_ref:寫通道訪問的幀緩存;
s2mm_fsync:寫通道幀同步信號,下降沿表示VDMA開始進行寫操作;
mm2s_frame_ref:讀通道訪問的幀緩存;
mm2s_fsync:讀通道幀同步信號,下降沿表示VDMA開始進行讀操作;
由上圖可知,寫通道在循環(huán)訪問幀緩存0,1,2;而由于讀通道的幀速率比寫通道慢,讀通道會緊跟寫通道,在操作0之后,跳過1,而去處理2。
下面以配置為Dynamic Genlock模式為例,寫通道(S2MM)配置為Dynamic Genlock Master模式,讀通道(MM2S)配置為Dynamic Genlock Slave模式,操作示意圖 23.1.3所示:
黑金7z100 ps驅(qū)動lcd,正點原子,fpga開發(fā)

圖 23.1.3 Dynamic Genlock Master/Slave示意圖
在動態(tài)同步鎖相模式下,Master會跳過Slave當(dāng)前操作的幀緩存,Slave工作在Master上一次操作過的幀緩存。在上圖中,寫通道循環(huán)訪問0,1,2,當(dāng)再次返回至0時,由于Slave在操作0,所以Master跳過0,而去訪問1。而讀通道操作Master上一次訪問過的幀緩存。
由此我們可以得出結(jié)論,如果想要避開讀通道和寫通道同時訪問同一幀緩存,那么VMDA必須配置成動態(tài)同步鎖相的模式,且?guī)彺鏀?shù)量要大于等于3。由于分配過多的幀緩存區(qū)域?qū)π实奶嵘呀?jīng)微乎其微,且會占用更多的存儲空間和消耗CPU的時間,因此在攝像頭圖像顯示的應(yīng)用中,幀緩存空間一般設(shè)置為3,并采用動態(tài)同步鎖相的模式。
需要注意的是,VDMA只是Xilinx提供的IP核,本身不提供存儲的功能,幀緩存一般設(shè)置在外置的存儲器(如DDR4)中,VDMA只是提供了用于訪問DDR4的接口。
VDMA概述
VDMA用于將AXI Stream格式的數(shù)據(jù)流轉(zhuǎn)換為Memory Map格式或?qū)emory Map格式的數(shù)據(jù)轉(zhuǎn)換為AXI Stream數(shù)據(jù)流,也就是說VDMA內(nèi)核旨在提供從AXI4域到AXI4-Stream域的視頻讀/寫傳輸功能,反之亦然,從而實現(xiàn)系統(tǒng)內(nèi)存(主要指DDR4)和基于AXI4-Stream的目標(biāo)視頻IP之間的高速數(shù)據(jù)移動。VDMA的框圖如下圖所示:
黑金7z100 ps驅(qū)動lcd,正點原子,fpga開發(fā)

圖 23.1.4 VDMA的框圖
AXI4-Lite可以對寄存器進行編程(配置),從而實現(xiàn)軟件動態(tài)配置VDMA的功能。通過AXI4-Lite接口對寄存器進行編程后,控制/狀態(tài)邏輯塊會為DataMover生成適當(dāng)?shù)拿?,以在AXI4主接口上啟動寫入和讀取命令??膳渲玫漠惒絣ine buffer用于在將像素數(shù)據(jù)寫入AXI4-Memory Map接口或AXI4-Stream接口之前臨時保存像素數(shù)據(jù)。
VDMA數(shù)據(jù)接口可以分為讀、寫兩個通道,且寫入和讀取獨立運行。用戶可以通過寫通道將AXI-Stream類型的數(shù)據(jù)流寫入系統(tǒng)存儲器(主要指DDR4)。在讀通道中,VDMA使用AXI4主接口從系統(tǒng)存儲器讀取數(shù)據(jù)并在AXI4-Stream主接口上輸出。可以看到,VDMA本質(zhì)上是一個數(shù)據(jù)搬運的IP,可以看作是為視頻圖像處理做特殊優(yōu)化的帶有幀緩沖功能的高性能DMA,為數(shù)據(jù)進出系統(tǒng)存儲器提供了一種便捷的方案。
VDMA內(nèi)核不僅具有幀緩沖功能,而且集成了視頻專用功能,如Gen-Lock和幀同步,用于完全同步的幀DMA操作和2D DMA傳輸。除了同步之外,還可以使用幀存儲編號和scatter gather或寄存器直接模式操作,以便中央處理器控制。在本設(shè)計中,不使用VDMA scatter gather功能,因為可以使用VDMA的更簡單的寄存器直接模式充分實現(xiàn)系統(tǒng),從而避免實現(xiàn)scatter gather功能帶來的面積成本。只有在系統(tǒng)需要對VDMA進行相對復(fù)雜的軟件控制時,才應(yīng)啟用scatter gather。
23.2實驗任務(wù)
本章的實驗任務(wù)是PS寫彩條數(shù)據(jù)至DDR內(nèi)存中,然后通過VDMA IP核將彩條數(shù)據(jù)顯示在RGB LCD液晶屏上。
23.3硬件設(shè)計
VDMA IP核為存儲器和AXI4-Stream類目標(biāo)外設(shè)之間提供高帶寬直接存儲器存取,本身不產(chǎn)生RGB LCD的接口時序,因此本次實驗除添加VDMA IP核外,還需要添加用于產(chǎn)生RGB LCD接口時序的IP核。MPSOC中提供了AXI4-Stream to Video Out IP核,可以將VDMA輸出的AXI4-Stream數(shù)據(jù)流轉(zhuǎn)換成視頻協(xié)議的數(shù)據(jù)流(包括并行數(shù)據(jù)、視頻同步信號等),轉(zhuǎn)換后的數(shù)據(jù)流符合RGB LCD接口的時序。另外,在添加AXI4-Stream to Video Out IP核之后,還需要添加Video Timing Controller(VTC)IP核,這個IP核為AXI4-Stream接口和視頻輸出接口提供了一個橋,用于控制視頻輸出的時序參數(shù)。
本次實驗需要兼容正點原子推出的所有RGB LCD液晶屏,因此本次實驗需要先獲取LCD屏ID,然后對RGB LCD屏接口時序相關(guān)的IP核進行動態(tài)配置,以實現(xiàn)兼容所有RGB LCD屏的功能。
圖 23.3.1為本次實驗的系統(tǒng)框圖,粉紅色的走線是控制流,綠色的走線是數(shù)據(jù)流。我們先來看數(shù)據(jù)流,彩條數(shù)據(jù)由PS端產(chǎn)生,產(chǎn)生的數(shù)據(jù)寫入系統(tǒng)存儲器DDR4內(nèi)存中;VDMA通過AXI Smartconnect IP核與AXI_HP端口進行連接,從而高效訪問DDR4。VDMA將從DDR4中讀取的視頻或圖像數(shù)據(jù)傳輸給AXI4-Stream to Video Out IP核。AXI4-Stream to Video Out IP核在VTC IP核的控制下,把AXI4-Stream格式的數(shù)據(jù)轉(zhuǎn)換成視頻輸出的數(shù)據(jù)格式(如RGB888),并將輸出的視頻數(shù)據(jù)流連接至RGB2LCD IP核(rgb2lcd)的輸入端。RGB2LCD IP核是本次實驗自定義的IP核,實現(xiàn)了獲取LCD屏的ID,以及將LCD屏的引腳封裝到總線接口上,以方便將LCD引腳引出至頂層模塊端口上。
最后我們再來看控制流,AXI Interconnect實現(xiàn)M_AXI_HPM接口與外設(shè)配置接口的互聯(lián),從而實現(xiàn)PS控制PL端的外設(shè)。PS通過AXI GPIO IP核獲取LCD屏ID,并根據(jù)獲取到的ID,配置VDMA IP核的幀緩存空間大小、讀通道等以及配置VTC IP核的輸出的時序參數(shù)。由于不同分辨率的LCD屏其驅(qū)動時鐘不一樣,因此本次實驗添加了Clocking Wizard(時鐘)IP核,并使能了其時鐘重配置的功能,PS根據(jù)LCD屏的ID配置時鐘IP核輸出不同的時鐘。
黑金7z100 ps驅(qū)動lcd,正點原子,fpga開發(fā)

圖 23.3.1 VDMA系統(tǒng)框圖
首先創(chuàng)建Vivado工程,工程名為“vdma_lcd”。
1 配置Zynq UltraScale+ MPSOC
添加Zynq UltraScale+ MPSOC,完成UART和DDR4的基本配置外,本實驗我們還需要使用S_AXI_HP接口。
首先在PS-PL Configuration欄開啟AXI HP0 FPD,如下圖所示:
黑金7z100 ps驅(qū)動lcd,正點原子,fpga開發(fā)

圖 23.3.2 開啟AXI HP0 FPD接口
上圖中AXI HP FPD和AXI HPC FPD都是高性能的接口,都可以滿足本次實驗的要求。其中AXI HPC FPD接口連接到了CCI(Cache-coherent interconnect),可以訪問L1和L2 Cache,也正是連接到了CCI,所以在訪問DDR控制器時,相比于AXI HP FPD接口來說,延時會較大,所以一般選擇AXI HP FPD接口。
接下來配置時鐘,PL0 配置為100Mhz。需要說明的是,VDMA端口的各種總線都有自己的時鐘,這些時鐘是異步的,可以使用同一個時鐘,可以使用不同的時鐘。為了降低設(shè)計的復(fù)雜度,本次統(tǒng)一使用100Mhz的時鐘,對于一些有特殊需求的應(yīng)用,可以使用不同的時鐘。時鐘配置界面如下圖所示:
黑金7z100 ps驅(qū)動lcd,正點原子,fpga開發(fā)

圖 23.3.3 配置時鐘
由上圖可知,將PL0配置成100Mhz,但實際輸出的時鐘只有96.968727Mhz,和預(yù)期的時鐘偏差較大。我們的開發(fā)板的PS輸入時鐘頻率為33.333Mhz,MPSOC芯片內(nèi)部會通過PLL對該時鐘進行倍頻和分頻,從而得到其它頻率的時鐘,而上圖中的PLL Options,就是PLL的倍頻和分頻系數(shù),這些系數(shù)是由工具自動計算得到的,共輸出5路時鐘,名稱分別為APLL、DPLL、VPLL、IOPLL和RPLL。
這些不同的時鐘應(yīng)用于不同的功能模塊,在UG1085手冊“PS Clock Subsystem”章節(jié)中有著詳細的介紹,關(guān)于這些時鐘的描述如下圖所示:
黑金7z100 ps驅(qū)動lcd,正點原子,fpga開發(fā)

圖 23.3.4 時鐘功能描述
需要注意的是,由于時鐘倍頻和分頻的系數(shù)是由Vivado軟件自動生成的,所以有時這些系數(shù)會有差別,筆者第一次在做本章實驗的時候,PLL Options的系數(shù)就和圖中的系數(shù)有差別,不過這個影響不大,只要我們最終需要得到的時鐘和實際產(chǎn)生的時鐘偏差不大就行。當(dāng)然也可以勾中上圖中的“Enable Manual Mode”,來手動調(diào)整這些系數(shù),但是通常不建議這么做,一方面在使用上會比較麻煩,另一方面調(diào)整有誤的話,會發(fā)揮不出MPSOC芯片的性能。
再次回到PL0配置成100Mhz和實際輸出的時鐘偏差較大的問題,如果大家在配置時發(fā)現(xiàn)偏差很小,則可以直接使用,不用做任何修改;如果偏差較大的話,可以嘗試修改Source一欄的時鐘源,如下圖所示:
黑金7z100 ps驅(qū)動lcd,正點原子,fpga開發(fā)

圖 23.3.5 PL0時鐘源修改
由上圖可知,將時鐘源改成IOPLL之后,實際輸出的時鐘頻率和配置的時鐘偏差非常小。Source選擇不同的時鐘源,表示PL0的時鐘源來自于哪里,此處選擇IOPLL。
配置完成后,點擊“OK”按鈕,Zynq UltraScale+ MPSOC如下圖所示:
黑金7z100 ps驅(qū)動lcd,正點原子,fpga開發(fā)

圖 23.3.6 MPSOC7 框圖
2 添加并配置VDMA IP核。
2-1 添加VDMA IP。
點擊“+”圖標(biāo),在搜索框中輸入“vdma”,添加該IP,如下圖所示:
黑金7z100 ps驅(qū)動lcd,正點原子,fpga開發(fā)

圖 23.3.7 添加VDMA IP
2-2 配置VDMA。
雙擊打開VDMA配置界面,在Basic界面,Address Width保持默認32即可。Frame Buffers選項可以選擇AXI VDMA要處理的幀緩沖存儲位置的數(shù)量。對于大于32位的地址空間,最多允許8個幀緩存。由于對于本次彩條顯示實驗來說,數(shù)據(jù)只需要寫入一次,因此不需要設(shè)置多個幀緩存區(qū)域,這里我們設(shè)置為1。因為本實驗是從DDR4中讀取數(shù)據(jù)輸出給LCD,所以只需要勾選Enable Read Channel就可以了,無需勾選Enable Write Channel。
Memory Map Data Width選項可以為MM2S通道選擇所需的AXI4數(shù)據(jù)寬度。有效值為32、64、128、256、512和1024。此處保持默認64即可。
Read Burst Size用于指定突發(fā)讀的大小,此處選擇64。
Stream Data Width選項可以選擇MM2S通道的AXI4-Stream數(shù)據(jù)寬度。有效值是8的倍數(shù),最大到1024。必須注意的是該值必須小于或等于Memory Map Data Width。此處因輸出數(shù)據(jù)格式為RGB888,設(shè)置為24。
Line Buffer Depth選項可以選擇MM2S通道的行緩沖深度(行緩沖區(qū)寬度為stream data 的大?。?,此處設(shè)置為2048即可。
Advanced界面用于配置讀寫通道的同步鎖相模式,由于本次實驗只有一個幀緩存,且只開啟了讀通道,多以同步鎖相模式無需配置,保持默認就可以了。VDMA的配置如下圖所示:
黑金7z100 ps驅(qū)動lcd,正點原子,fpga開發(fā)

圖 23.3.8 配置VDMA
配置完成后,VDMA的框圖如下圖所示:
黑金7z100 ps驅(qū)動lcd,正點原子,fpga開發(fā)

圖 23.3.9 AXI VDMA框圖
AXI VDMA部分接口說明:
S_AXI_LITE:AXI-Lite接口,PS通過這個接口配置VDMA的寄存器;
s_axi_lite_aclk:AXI VDMA AXI4-Lite接口時鐘;
M_AXI_MM2S:讀通道存儲器端映射的AXI4接口,提供對存儲器(DDR4)的訪問;
m_axi_mm2s_aclk:AXI VDMA MM2S時鐘;
M_AXIS_MM2S:讀通道AXI-Stream端映射的AXI4接口,用于輸出到外設(shè);
m_axis_mm2s_aclk:AXI VDMA MM2S AXIS時鐘;
mm2s_introut:讀通道中斷輸出信號。
3 添加視頻時序控制器Video Timing Controller
3-1 添加Video Timing Controller,如下圖所示:
黑金7z100 ps驅(qū)動lcd,正點原子,fpga開發(fā)

圖 23.3.10 添加視頻時序控制器
3-2 配置Video Timing Controller
在配置界面,確保勾選Include AXI4-Lite Interface,該選項將使用AXI4-Lite接口,該接口可以動態(tài)訪問程序并更改處理參數(shù),也就是說可以動態(tài)配置Video Timing Controller。勾選Enable Generation,使能Video Timing Controller生成并輸出視頻時序。因為本實驗通過PS動態(tài)配置生成時序,所以無需勾選Enable Detection使能檢測,其它選項保持默認就可以了。
黑金7z100 ps驅(qū)動lcd,正點原子,fpga開發(fā)

圖 23.3.11 配置視頻時序控制器
配置完成后,Video Timing Controller的框圖如下圖所示:
黑金7z100 ps驅(qū)動lcd,正點原子,fpga開發(fā)

圖 23.3.12 Video Timing Controller框圖
Video Timing Controller部分接口說明:
ctrl:Video Timing Controller配置接口,PS通過這個接口配置VTC的寄存器;
clk:Video核時鐘;
gen_clken:Video Timing Generator時鐘使能信號,高電平有效;
vtiming_out:Video時序輸出接口。
4 添加視頻輸出控制器Video Out
4-1 添加Video Out,如下圖所示:
黑金7z100 ps驅(qū)動lcd,正點原子,fpga開發(fā)

圖 23.3.13 添加視頻輸出控制器
4-2 配置Video Out
Clock Mode時鐘模式用于指定AXI4-Stream輸入和視頻輸出信號是使用公共時鐘還是獨立時鐘進行時鐘控制,此處我們使用獨立時鐘進行控制,即勾選Independent,取消勾選Common,其它選項保持默認即可。
黑金7z100 ps驅(qū)動lcd,正點原子,fpga開發(fā)

圖 23.3.14 配置視頻輸出控制器
配置完成后,Video Out的框圖如下圖所示:
黑金7z100 ps驅(qū)動lcd,正點原子,fpga開發(fā)

圖 23.3.15 AXI4-Stream to Video Out框圖
AXI4-Stream to Video Out部分接口說明:
video_in:AXI-Stream接口輸入數(shù)據(jù)流;
vtiming_in:Video時序輸入接口;
vid_io_out:Video數(shù)據(jù)流輸出接口;
vtg_ce:VTC時鐘使能信號。
5 添加自定義IP
由于RGB LCD液晶屏有多種分辨率,所以我們需要一個獲取LCD屏ID的模塊,我們自定義的rgb2lcd IP核可以實現(xiàn)該功能,且該IP核把LCD引腳封裝到一起,方便引出至端口。
該IP我們放在例程(vdma_lcd)的ip_repo目錄,大家可以將ip_repo文件夾拷貝到自己的工程目錄下,如下圖所示:
黑金7z100 ps驅(qū)動lcd,正點原子,fpga開發(fā)

圖 23.3.16 自定義IP存放目錄
拷貝完成后將IP核添加至工程中。打開設(shè)置界面,如下圖所示:
黑金7z100 ps驅(qū)動lcd,正點原子,fpga開發(fā)

圖 23.3.17 點擊設(shè)置按鈕
在彈出的界面中,點擊左側(cè)的IP->Repository,單擊右側(cè)的“+”按鈕,添加自定義IP庫,如下圖所示:
黑金7z100 ps驅(qū)動lcd,正點原子,fpga開發(fā)

圖 23.3.18 修改宏定義
在彈出的界面中選擇自定義IP所存放的目錄,本實驗為F:\MPSOC\Embedded_System\vdma_lcd\ip_repo,添加完成后,如下圖所示,在彈出的“Add Repository”界面中直接單擊“OK”按鈕即可。
黑金7z100 ps驅(qū)動lcd,正點原子,fpga開發(fā)

圖 23.3.19 確認添加
6 添加rgb2lcd IP,如下圖所示:
黑金7z100 ps驅(qū)動lcd,正點原子,fpga開發(fā)

圖 23.3.20 添加RGB轉(zhuǎn)LCD輸出的IP
雙擊打開配置界面,Vid Data Width用于指定輸入的RGB總線寬度,為24位。
黑金7z100 ps驅(qū)動lcd,正點原子,fpga開發(fā)

圖 23.3.21 rgb2lcd IP核配置
rgb2lcd IP核將rgb2lcd.v文件封裝成自定義的IP核,其模塊代碼如下:

1  module rgb2lcd #(
2      //parameter define
3      parameter    VID_DATA_WIDTH = 24
4  )(
5      //VID_OUT
6      input  [VID_DATA_WIDTH-1:0]   rgb_data ,
7      input                         rgb_vde  ,
8      input                         rgb_hsync,
9      input                         rgb_vsync,
10 
11     input                         pixel_clk,
12    
13     //RGB LCD
14     output                        lcd_pclk ,
15     output                        lcd_rst  ,
16     output                        lcd_hs   ,
17     output                        lcd_vs   ,
18     output                        lcd_de   ,
19     output                        lcd_bl   ,
20    
21     //AXI GPIO(LCD ID)
22     input  [2:0]                  lcd_id_i, 
23     input  [2:0]                  lcd_id_t,
24     output [2:0]                  lcd_id_o,
25     
26     //LCD數(shù)據(jù)引腳為雙向引腳,改成三態(tài)引腳的形式  
27     input  [VID_DATA_WIDTH-1:0]   lcd_rgb_i,
28     output [VID_DATA_WIDTH-1:0]   lcd_rgb_o,
29     output [VID_DATA_WIDTH-1:0]   lcd_rgb_t
30 );
31 
32 //reg define
33 reg  [2:0]  lcd_id;
34 
35 //*****************************************************
36 //**                  main code
37 //*****************************************************
38 
39 //LCD信號賦值
40 assign lcd_pclk = (lcd_id == 5) ? ~pixel_clk : pixel_clk;  //如果是10寸屏,輸出的時鐘取反
41 assign lcd_de = rgb_vde  ;
42 assign lcd_hs = rgb_hsync;
43 assign lcd_vs = rgb_vsync;
44 assign lcd_bl = 1'b1;
45 assign lcd_rst = 1'b1;
46 
47 //讀取LCD ID
48 assign lcd_id_o[0] = (lcd_id_t[0]==1'b1) ? lcd_rgb_i[23] : 1'bz;  //R7:M0
49 assign lcd_id_o[1] = (lcd_id_t[1]==1'b1) ? lcd_rgb_i[15] : 1'bz;  //G7:M1
50 assign lcd_id_o[2] = (lcd_id_t[2]==1'b1) ? lcd_rgb_i[7] : 1'bz;   //B7:M2
51 
52 assign lcd_rgb_o  = (lcd_id_t[0]==1'b1) ? {VID_DATA_WIDTH{1'bz}} :
53                                            rgb_data[VID_DATA_WIDTH-1:0];
54 assign lcd_rgb_t  = {VID_DATA_WIDTH{lcd_id_t[0]}};
55 
56 always @(posedge pixel_clk) begin
57     if(lcd_id_t == 3'b111)
58         lcd_id = lcd_id_o;
59 end
60 
61 endmodule

這個模塊的代碼比較簡單,這里有幾點需要注意。首先LCD屏的數(shù)據(jù)引腳是雙向的引腳,由于在封裝IP核的時候不能直接定義成inout信號(軟件最終會綜合成輸出的引腳),因此這里需要將LCD的數(shù)據(jù)引腳定義成三個引腳。lcd_rgb_i為輸入的24位LCD數(shù)據(jù)信號,用于獲取LCD屏的ID;lcd_rgb_o為輸出到LCD屏的像素數(shù)據(jù);lcd_rgb_t用于控制LCD數(shù)據(jù)引腳的方向。這三個信號在自定義IP核的時候增加一個GPIO的接口,即可在端口上以一個雙向引腳的形式存在。
lcd_id連接至AXI GPIO,PS通過AXI GPIO來讀取LCD ID。由于lcd_id的引腳方向控制著lcd_rgb數(shù)據(jù)引腳的方向(輸入或者輸出),因此lcd_id也要寫成三個引腳的形式。
程序中第40行至45行代碼為LCD的信號進行賦值,其實就是將輸入的信號直接賦值給輸出,這里將LCD的端口信號統(tǒng)一在rgb2lcd IP核中輸出,方便后續(xù)對接口進行封裝。
而獲取LCD的ID實際上只用到了三個數(shù)據(jù)引腳,分別是lcd_rgb_i各顏色分量的高位,M0~M2分別對應(yīng)lcd_rgb_i[23]、lcd_rgb_i[15]和lcd_rgb_i[7]。
需要注意的是,由于10寸屏對時序的要求更為嚴格,需要調(diào)整輸出的lcd時鐘和數(shù)據(jù)的相位,此處如果檢測到開發(fā)板連接的是10寸屏,直接對輸出的時鐘進行區(qū)分,相當(dāng)于時鐘和數(shù)據(jù)相位偏移180度。
7 添加Clocking Wizard時鐘IP核
不同分辨率的LCD屏,其驅(qū)動時鐘頻率不同,所以需要使用一個動態(tài)時鐘IP核來控制時鐘的輸出,本章實驗我們選擇使用Vivado軟件自帶的時鐘IP核,并使能其動態(tài)可配置功能,IP核的添加方式如下圖所示。
黑金7z100 ps驅(qū)動lcd,正點原子,fpga開發(fā)

圖 23.3.22 添加時鐘IP核
雙擊打開Clocking Wizard IP核,開始進行配置,如下圖所示:
黑金7z100 ps驅(qū)動lcd,正點原子,fpga開發(fā)

圖 23.3.23 Clocking Options配置頁面
在Clocking Options選項頁中,勾中“Dynamic Reconfig”選項,即使能時鐘的動態(tài)重配置功能,選中之后,左側(cè)IP核的端口中會多出一個s_axi_lite接口,PS通過這個接口來對時鐘IP核進行配置,從而開發(fā)板連接不同的LCD屏,時鐘IP核可以輸出不同的時鐘,該選項頁的其它配置保持默認即可。
在Output Clocks配置頁面中,使能clk_out1的時鐘輸出,時鐘選擇33.3333Mhz,這里是設(shè)置時鐘IP核默認輸出的時鐘頻率,當(dāng)連接了LCD屏之后,會根據(jù)LCD的ID,對時鐘IP核輸出的時鐘頻率進行重配置,配置完成后點擊“OK”按鈕,如下圖所示。
黑金7z100 ps驅(qū)動lcd,正點原子,fpga開發(fā)

圖 23.3.24 Output Clocks配置頁面
8 添加GPIO IP核
PS通過AXI GPIO獲取LCD屏的ID,接下來添加AXI GPIO IP核。點擊Add IP按鈕,在搜索框中輸入“AXI GPIO”,雙擊AXI GPIO IP核,將IP核添加進來,該IP核配置界面如下:
黑金7z100 ps驅(qū)動lcd,正點原子,fpga開發(fā)

圖 23.3.25 配置AXI GIPIO
將GPIO設(shè)置為輸入,位寬設(shè)置為3位即可。
9 連線。
因為Vivado的自動連線功能不可能完全按照我們的需求進行連接,我們在進行自動連線之前,先手動連接部分接口,避免Vivado錯誤連接,手動連線如下圖所示:
黑金7z100 ps驅(qū)動lcd,正點原子,fpga開發(fā)

圖 23.3.26 手動連線
這里是將時鐘IP核輸出的時鐘連接至LCD屏?xí)r序控制相關(guān)的IP核,另外將rgb2lcd IP核的lcd_id端口連接至AXI GPIO IP核,以供PS讀取LCD屏的ID。
接下來點擊界面上方“Run Connection Automation”,在彈出的對話框左側(cè)確認勾選All Automation,然后點擊“OK”按鈕。
此時系統(tǒng)會自動生成AXI Interconnect和AXI Smartconnect。AXI Interconnect(ps8_0_axi_periph)用于橋接MPSOC處理器M_AXI_HPM0_LPD總線和外部低速外設(shè)的AXI_LITE總線;AXI Smartconnect(axi_smc)用于連接MPSOC處理器的S_AXI_HP0_FPD接口和VDMA的M_AXI_MM2S 總線。另外系統(tǒng)也自動生成了1個reset模塊(rst_ps8_0_99M),用于復(fù)位AXI_Lite總線上的外設(shè)。
Vivado自動連線完成后,有部分信號Vivado無法自動連接,需我們手動連接,如下圖所示:
黑金7z100 ps驅(qū)動lcd,正點原子,fpga開發(fā)

圖 23.3.27 需手動連接的信號
10 引出rgb2lcd的輸出引腳。由于在硬件上,LCD復(fù)位信號和PL復(fù)位信號直接連接在一起,因此LCD的復(fù)位端口信號不用引出,其余的LCD端口信號需要引出,如圖 23.3.28所示。
黑金7z100 ps驅(qū)動lcd,正點原子,fpga開發(fā)

圖 23.3.28 引出引腳
整體系統(tǒng)架構(gòu)圖如下:
黑金7z100 ps驅(qū)動lcd,正點原子,fpga開發(fā)

圖 23.3.29 整體系統(tǒng)架構(gòu)連接圖
到這里我們的Block Design就設(shè)計完成了,在Diagram窗口空白處右擊,然后選擇“Validate Design”驗證設(shè)計。驗證完成后彈出對話框提示“Validation Successful”表明設(shè)計無誤,點擊“OK”確認。最后按快捷鍵“Ctrl + S”保存設(shè)計。
接下來在Source窗口中右鍵點擊Block Design設(shè)計文件“system.bd”,然后依次執(zhí)行“Generate Output Products”和“Create HDL Wrapper”。
11 添加引腳約束。
引腳約束如下:

#RGB LCD
set_property -dict {PACKAGE_PIN W14 IOSTANDARD LVCMOS33} [get_ports {lcd_rgb_tri_io[0]}]
set_property -dict {PACKAGE_PIN Y14 IOSTANDARD LVCMOS33} [get_ports {lcd_rgb_tri_io[1]}]
set_property -dict {PACKAGE_PIN W13 IOSTANDARD LVCMOS33} [get_ports {lcd_rgb_tri_io[2]}]
set_property -dict {PACKAGE_PIN Y13 IOSTANDARD LVCMOS33} [get_ports {lcd_rgb_tri_io[3]}]
set_property -dict {PACKAGE_PIN W12 IOSTANDARD LVCMOS33} [get_ports {lcd_rgb_tri_io[4]}]
set_property -dict {PACKAGE_PIN Y12 IOSTANDARD LVCMOS33} [get_ports {lcd_rgb_tri_io[5]}]
set_property -dict {PACKAGE_PIN W11 IOSTANDARD LVCMOS33} [get_ports {lcd_rgb_tri_io[6]}]
set_property -dict {PACKAGE_PIN AA12 IOSTANDARD LVCMOS33} [get_ports {lcd_rgb_tri_io[7]}]
set_property -dict {PACKAGE_PIN AC14 IOSTANDARD LVCMOS33} [get_ports {lcd_rgb_tri_io[8]}]
set_property -dict {PACKAGE_PIN AD15 IOSTANDARD LVCMOS33} [get_ports {lcd_rgb_tri_io[9]}]
set_property -dict {PACKAGE_PIN AC13 IOSTANDARD LVCMOS33} [get_ports {lcd_rgb_tri_io[10]}]
set_property -dict {PACKAGE_PIN AD14 IOSTANDARD LVCMOS33} [get_ports {lcd_rgb_tri_io[11]}]
set_property -dict {PACKAGE_PIN AE15 IOSTANDARD LVCMOS33} [get_ports {lcd_rgb_tri_io[12]}]
set_property -dict {PACKAGE_PIN AA13 IOSTANDARD LVCMOS33} [get_ports {lcd_rgb_tri_io[13]}]
set_property -dict {PACKAGE_PIN AE14 IOSTANDARD LVCMOS33} [get_ports {lcd_rgb_tri_io[14]}]
set_property -dict {PACKAGE_PIN AB13 IOSTANDARD LVCMOS33} [get_ports {lcd_rgb_tri_io[15]}]
set_property -dict {PACKAGE_PIN AG14 IOSTANDARD LVCMOS33} [get_ports {lcd_rgb_tri_io[16]}]
set_property -dict {PACKAGE_PIN AB15 IOSTANDARD LVCMOS33} [get_ports {lcd_rgb_tri_io[17]}]
set_property -dict {PACKAGE_PIN AH14 IOSTANDARD LVCMOS33} [get_ports {lcd_rgb_tri_io[18]}]
set_property -dict {PACKAGE_PIN AB14 IOSTANDARD LVCMOS33} [get_ports {lcd_rgb_tri_io[19]}]
set_property -dict {PACKAGE_PIN AG13 IOSTANDARD LVCMOS33} [get_ports {lcd_rgb_tri_io[20]}]
set_property -dict {PACKAGE_PIN AE13 IOSTANDARD LVCMOS33} [get_ports {lcd_rgb_tri_io[21]}]
set_property -dict {PACKAGE_PIN AH13 IOSTANDARD LVCMOS33} [get_ports {lcd_rgb_tri_io[22]}]
set_property -dict {PACKAGE_PIN AF13 IOSTANDARD LVCMOS33} [get_ports {lcd_rgb_tri_io[23]}]

set_property -dict {PACKAGE_PIN J11 IOSTANDARD LVCMOS33} [get_ports lcd_hs]
set_property -dict {PACKAGE_PIN K12 IOSTANDARD LVCMOS33} [get_ports lcd_vs]
set_property -dict {PACKAGE_PIN J10 IOSTANDARD LVCMOS33} [get_ports lcd_de]
set_property -dict {PACKAGE_PIN J12 IOSTANDARD LVCMOS33} [get_ports lcd_bl]
set_property -dict {PACKAGE_PIN K13 IOSTANDARD LVCMOS33} [get_ports lcd_clk]
set_property -dict {PACKAGE_PIN F10 IOSTANDARD LVCMOS33} [get_ports lcd_rst]

最后在左側(cè)Flow Navigator導(dǎo)航欄中找到PROGRAM AND DEBUG,點擊該選項中的“Generate Bitstream”,對設(shè)計進行綜合、實現(xiàn)、并生成Bitstream文件。
在生成Bitstream之后,在菜單欄中選擇 File > Export > Export hardware導(dǎo)出硬件,并在彈出的對話框中,勾選“Include bitstream”。然后在菜單欄選擇Tools> Launch Vitis,啟動Vitis軟件。
23.4軟件設(shè)黑金7z100 ps驅(qū)動lcd,正點原子,fpga開發(fā)

在將硬件導(dǎo)出至 Vitis,并打開 Vitis 開發(fā)環(huán)境后,創(chuàng)建應(yīng)用工程的步驟都是一樣的,這里不再贅述,新創(chuàng)建的空白應(yīng)用工程命名為vdma_lcd。
在Vitis軟件中新建一個空的應(yīng)用工程,應(yīng)用工程名為“vdma_lcd”。Vitis中需要添加的源代碼較多,大家可以從提供的例程中拷貝Vitis的源文件,需拷貝的文件如下:
黑金7z100 ps驅(qū)動lcd,正點原子,fpga開發(fā)

圖 23.4.1 Vitis源文件
在硬件設(shè)計部分我們添加了時鐘Clocking Wizard IP核,這里我們通過C代碼對該IP核輸出的時鐘頻率進行重配置,從而控制該IP核生成不同頻率的時鐘信號以驅(qū)動不同分辨率的LCD。display_ctrl文件夾來自于Digilent的開源文件,由于其默認的VDMA配置只支持VDMA的讀通道,不支持寫通道,為了兼容后面攝像頭的圖像顯示實驗,本次實驗對開源文件做了簡單的修改,把文件中VDMA相關(guān)的代碼刪除,將VDMA相關(guān)API函數(shù)放在單獨的vdma_api文件夾內(nèi)。除此之外,display_ctrl文件夾內(nèi)的函數(shù)也實現(xiàn)了獲取LCD屏ID的功能。
vdma_api文件夾存放了對VDMA相關(guān)操作的API函數(shù),該文件來源于Xilinx官方提供的VDMA模板,如下圖所示:
黑金7z100 ps驅(qū)動lcd,正點原子,fpga開發(fā)

圖 23.4.2 VDMA模塊例程
VDMA模板提供的函數(shù)默認同時打開了寫通道和讀通道,由于本次試驗不需要打開寫通道,所以對文件內(nèi)的函數(shù)作了修改,通過函數(shù)輸入的形參來判斷是否打開讀通道或者寫通道。
main.c文件中的宏定義、全局變量等相關(guān)內(nèi)容,代碼如下:

1   #include <stdio.h>
2   #include <stdlib.h>
3   #include <string.h>
4   #include "xil_types.h"
5   #include "xil_cache.h"
6   #include "xparameters.h"
7   #include "xgpio.h"
8   #include "xaxivdma.h"
9   #include "xaxivdma_i.h"
10  #include "display_ctrl/display_ctrl.h"
11  #include "vdma_api/vdma_api.h"
12  #include "clk_wiz/clk_wiz.h"
13  
14  //宏定義
15  #define BYTES_PIXEL        3                          //像素字節(jié)數(shù),RGB888占3個字節(jié)
16  #define CLK_WIZ_ID         XPAR_CLK_WIZ_0_DEVICE_ID   //時鐘IP核器件ID
17  #define VDMA_ID            XPAR_AXIVDMA_0_DEVICE_ID   //VDMA器件ID
18  #define DISP_VTC_ID        XPAR_VTC_0_DEVICE_ID       //VTC器件ID
19  #define AXI_GPIO_0_ID      XPAR_AXI_GPIO_0_DEVICE_ID  //PL端  AXI GPIO 0(lcd_id)器件ID
20  #define AXI_GPIO_0_CHANEL  1                          //AXI GPIO(lcd_id)通道1
21  
22  //函數(shù)聲明
23  void colorbar(u8 *frame, u32 width, u32 height, u32 stride);
24  
25  //全局變量
26  XAxiVdma     vdma;
27  DisplayCtrl  dispCtrl;
28  XGpio        axi_gpio_inst;   //PL端 AXI GPIO 驅(qū)動實例
29  VideoMode    vd_mode;
30  //frame buffer的起始地址
31  unsigned int const frame_buffer_addr = (XPAR_PSU_DDR_0_S_AXI_BASEADDR + 0x1000000);
32  unsigned int lcd_id=0;        //LCD ID

在代碼第27行, 我們定義了一個DisplayCtrl 結(jié)構(gòu)體變量 dispctrl,DisplayCtrl是在displya_ctrl.h文件下定義的結(jié)構(gòu)體,包含 vtc結(jié)構(gòu)體,視頻分辨率video mode等信息,用于顯示控制,具體定義如下圖所示:
黑金7z100 ps驅(qū)動lcd,正點原子,fpga開發(fā)

圖 23.4.3 DisplayCtrl 結(jié)構(gòu)體
在代碼的第29行,定義了一個VideoMode結(jié)構(gòu)體變量vd_mode,VideoMode是在lcd_modes.h文件下定義的結(jié)構(gòu)體,如下圖所示:
黑金7z100 ps驅(qū)動lcd,正點原子,fpga開發(fā)

圖 23.4.4 VideoMode結(jié)構(gòu)體
不同分辨率的LCD屏,需要為這個結(jié)構(gòu)體所包含的變量賦不同的值,具體變量的賦值方法在注釋中已給出,如hps的值為LCD水平方向有效像素+行顯示前沿。為了方便賦值,我們在lcd_modes.h文件下針對不同分辨率的LCD屏,分別賦值了不同的參數(shù),在獲取到LCD屏的ID直接選擇對應(yīng)的參數(shù)即可,如7寸LCD屏(分辨率800*480)的參數(shù)如下圖所示:
黑金7z100 ps驅(qū)動lcd,正點原子,fpga開發(fā)

圖 23.4.5 7寸LCD屏(分辨率800*480)的參數(shù)
在代碼的第30行定義了VDMA的幀緩存區(qū)域位于DDR4中的起始地址(frame_buffer_addr),寫彩條和VDMA的讀通道都是從這個起始地址開始操作的。
main.c文件中的函數(shù)代碼如下:

34  int main(void)
35  {
36      XGpio_Initialize(&axi_gpio_inst,AXI_GPIO_0_ID);                //GPIO初始化
37      XGpio_SetDataDirection(&axi_gpio_inst,AXI_GPIO_0_CHANEL,0x07); //設(shè)置AXI GPIO為輸入
38      lcd_id = LTDC_PanelID_Read(&axi_gpio_inst,AXI_GPIO_0_CHANEL);  //獲取LCD的ID
39      XGpio_SetDataDirection(&axi_gpio_inst,AXI_GPIO_0_CHANEL,0x00); //設(shè)置AXI GPIO為輸出
40      xil_printf("LCD ID: %x\r\n",lcd_id);
41  
42      //根據(jù)獲取的LCD的ID號來進行video參數(shù)的選擇
43      switch(lcd_id){
44          case 0x4342 : vd_mode = VMODE_480x272; break;  //4.3寸屏,480*272分辨率
45          case 0x4384 : vd_mode = VMODE_800x480; break;  //4.3寸屏,800*480分辨率
46          case 0x7084 : vd_mode = VMODE_800x480; break;  //7寸屏,800*480分辨率
47          case 0x7016 : vd_mode = VMODE_1024x600; break; //7寸屏,1024*600分辨率
48          case 0x1018 : vd_mode = VMODE_1280x800; break; //10.1寸屏,1280*800分辨率
49          default : vd_mode = VMODE_800x480; break;
50      }
51  
52      //配置VDMA
53      run_vdma_frame_buffer(&vdma, VDMA_ID, vd_mode.width, vd_mode.height,
54                              frame_buffer_addr,0, 0,ONLY_READ);
55  
56      //設(shè)置時鐘IP核輸出的時鐘頻率
57      clk_wiz_cfg(CLK_WIZ_ID,vd_mode.freq);
58      //初始化Display controller
59      DisplayInitialize(&dispCtrl, DISP_VTC_ID);
60      //設(shè)置VideoMode
61      DisplaySetMode(&dispCtrl, &vd_mode);
62      DisplayStart(&dispCtrl);
63  
64      //寫彩條
65      colorbar((u8*)frame_buffer_addr, vd_mode.width,
66              vd_mode.height, vd_mode.width*BYTES_PIXEL);
67      return 0;
68  }

在main函數(shù)中,我們首先需要獲取LCD屏的ID,才能對VDMA、VTC和時鐘做配置,如代碼中第36行至第40行代碼所示。PS通過AXI GPIO獲取LCD屏的ID,因此先對AXI GPIO做初始化,然后將AXI GPIO設(shè)置成輸入,LTDC_PanelID_Read()函數(shù)返回的值即為LCD屏的ID。LTDC_PanelID_Read()函數(shù)根據(jù)讀取到的AXI GPIO的引腳數(shù)據(jù)映射成16位的值,以方便指示當(dāng)前的LCD屏尺寸和分辨率。當(dāng)AXI GPIO引腳為000時,函數(shù)返回0x4342,表示LCD屏為4.3寸屏,分辨率為480*272;001對應(yīng)0x7084;010對應(yīng)0x7016;100對應(yīng)0x4384;101對應(yīng)0x1018。在獲取到LCD ID后,將AXI GPIO設(shè)置成輸出,此時可以開始驅(qū)動LCD屏。
在main函數(shù)的第42行至第50行代碼,根據(jù)不同的LCD屏ID,為vd_mode結(jié)構(gòu)體變量賦不同的值。接下來通過run_vdma_frame_buffer()函數(shù)配置VDMA,包括配置幀緩存地址、讀通道配置、開啟讀通道等,如程序中第53行和第54行代碼所示。需要說明的是,該函數(shù)輸入的最后一個參數(shù)用來配置VDMA的讀寫通道,當(dāng)該參數(shù)為ONLY_READ時表示只開啟讀通道;ONLY_WRITE表示只開啟寫通道;BOTH表示既開啟讀通道,也開啟寫通道。
在main函數(shù)的第56行至第62行代碼對時鐘和VTC進行配置,并啟動VTC開始運行。在main函數(shù)的第65和第66行,通過colorbar()函數(shù)向幀緩存區(qū)域中寫入彩條數(shù)據(jù),寫彩條的函數(shù)同樣位于main.c文件中,代碼如下:

70  //寫彩條函數(shù)(彩虹色)
71  void colorbar(u8 *frame, u32 width, u32 height, u32 stride)
72  {
73      u32 color_edge;
74      u32 x_pos, y_pos;
75      u32 y_stride = 0;
76      u8 rgb_r, rgb_b, rgb_g;
77  
78      color_edge = width * BYTES_PIXEL / 7;
79      for (y_pos = 0; y_pos < height; y_pos++) {
80          for (x_pos = 0; x_pos < (width * BYTES_PIXEL); x_pos += BYTES_PIXEL) {
81              if (x_pos < color_edge) {                                           //紅色
82                  rgb_r = 0xFF;
83                  rgb_g = 0;
84                  rgb_b = 0;
85              } else if ((x_pos >= color_edge) && (x_pos < color_edge * 2)) {     //橙色
86                  rgb_r = 0xFF;
87                  rgb_g = 0x7F;
88                  rgb_b = 0;
89              } else if ((x_pos >= color_edge * 2) && (x_pos < color_edge * 3)) { //黃色
90                  rgb_r = 0xFF;
91                  rgb_g = 0xFF;
92                  rgb_b = 0;
93              } else if ((x_pos >= color_edge * 3) && (x_pos < color_edge * 4)) { //綠色
94                  rgb_r = 0;
95                  rgb_g = 0xFF;
96                  rgb_b = 0;
97              } else if ((x_pos >= color_edge * 4) && (x_pos < color_edge * 5)) { //青色
98                  rgb_r = 0;
99                  rgb_g = 0xFF;
100                 rgb_b = 0xFF;
101             } else if ((x_pos >= color_edge * 5) && (x_pos < color_edge * 6)) { //藍色
102                 rgb_r = 0;
103                 rgb_g = 0;
104                 rgb_b = 0xFF;
105             } else if ((x_pos >= color_edge * 6) && (x_pos < color_edge * 7)) { //紫色
106                 rgb_r = 0x8B;
107                 rgb_g = 0;
108                 rgb_b = 0xFF;
109             }
110             frame[x_pos + y_stride + 0] = rgb_b;
111             frame[x_pos + y_stride + 1] = rgb_g;
112             frame[x_pos + y_stride + 2] = rgb_r;
113         }
114         y_stride += stride;
115     }
116     Xil_DCacheFlush();     //刷新Cache,數(shù)據(jù)更新至內(nèi)存
117     xil_printf("show color bar\r\n");
118 }

colorbar()函數(shù)實現(xiàn)了一個彩虹色(紅橙黃綠青藍紫)的七色彩條。在函數(shù)的最后通過調(diào)用Xil_DCacheFlush()函數(shù)將緩存在DataCache中的數(shù)據(jù)刷新到DDR4中,以便VDMA進行讀取。
最后我們來著重介紹下,如果對時鐘IP核進行動態(tài)重配置。在Block Design設(shè)計界面中,我們勾選了時鐘IP核的動態(tài)重配置功能,此時該IP核的接口中多了一個AXI4-Lite的從機接口,通過AXI互聯(lián)連接到了PS的M_AXI_HPM0_LPD接口,我們在PS編寫的C代碼,就是通過對時鐘IP核的寄存器進行修改,最終通過AXI接口實現(xiàn)對時鐘IP核的重配置功能。由于Vitis并沒有提供對時鐘進行重配置的庫函數(shù),因此需要對時鐘IP核的寄存器比較熟悉才行,關(guān)于時鐘IP核寄存器的詳細介紹,請參考Xilinx官方手冊pg065(pg065-Clocking Wizard LogiCORE IP Product Guide)。
clk_wiz.h源代碼中定義了對時鐘進行重配置的寄存器地址偏移量,以及函數(shù)的聲明,代碼如下:

1  #ifndef CLK_WIZ_H_
2  #define CLK_WIZ_H_
3  
4  #include "xil_types.h"
5  
6  #define CLK_SR_OFFSET    0x04    //Status Register
7  #define CLK_CFG0_OFFSET  0x200   //Clock Configuration Register 0
8  #define CLK_CFG2_OFFSET  0x208   //Clock Configuration Register 2
9  #define CLK_CFG23_OFFSET 0x25C   //Clock Configuration Register 23
10 
11 void clk_wiz_cfg(u32 clk_base_addr,double freq);
12 
13 #endif /* CLK_WIZ_H_ */

由代碼中第6至9行代碼可知,程序中只配置了4個寄存器,即可實現(xiàn)對時鐘進行重配置,寄存器描述如下:
黑金7z100 ps驅(qū)動lcd,正點原子,fpga開發(fā)

圖 23.4.6 寄存器描述1
Software Reset Register (SRR)的基地址偏移量為0x00,用于實現(xiàn)對時鐘IP核軟復(fù)位的功能。
Status Register (SR) 的基地址偏移量為0x04,用于判斷時鐘IP核輸出的時鐘是否穩(wěn)定,即Locked信號是否為1。當(dāng)Locked信號等于1時,表示時鐘輸出穩(wěn)定;而當(dāng)Locked等于0時,表示時鐘IP核正在進行重配置。
黑金7z100 ps驅(qū)動lcd,正點原子,fpga開發(fā)

圖 23.4.7 寄存器描述2
Clock Configuration Register 0的基地址偏移量為0x200,用于設(shè)置輸出時鐘的分頻/倍頻系數(shù)。其中Bit[7:0]表示分頻系數(shù)(DIVCLK_DIVIDE);Bit[15:8]表示倍頻系數(shù)的整數(shù)部分(CLKFBOUT_MULT);Bit[25:16]表示倍頻系數(shù)的小數(shù)部分(CLKFBOUT_FRAC)。需要注意的是,該寄存器設(shè)置的分頻/倍頻系數(shù),對時鐘IP核輸出的所有時鐘都有效。
黑金7z100 ps驅(qū)動lcd,正點原子,fpga開發(fā)

圖 23.4.8 寄存器描述3
Clock Configuration Register 2的基地址偏移量為0x208,用于設(shè)置CLKOUT0時鐘的分頻系數(shù)。Bit[7:0]表示分頻系數(shù)的整數(shù)部分(CLKOUT0_DIVIDE);Bit[17:8]表示倍頻系數(shù)的小數(shù)部分(CLKOUT0_FRAC);該寄存器僅用于配置clkout0的分頻系數(shù),即clkout0最終輸出的時鐘頻率是先在Clock Configuration Register 0的基礎(chǔ)上進行分頻/倍頻,再通過Clock Configuration Register 2進行分頻得到的。
黑金7z100 ps驅(qū)動lcd,正點原子,fpga開發(fā)

圖 23.4.9 寄存器描述4
Clock Configuration Register 23的基地址偏移量為0x25C,用于加載動態(tài)重配置的參數(shù),在配置完分配/倍頻系數(shù)后,必須通過此寄存器加載重配置的參數(shù)才行,否則配置的參數(shù)無法生效。當(dāng)Bit[0]等于1時,表示加載重配置的參數(shù)。而Bit[1]用于選擇重配置的參數(shù),當(dāng)Bit[1]等于1時,重配置的參數(shù)來源于前面的配置寄存器;當(dāng)Bit[1]等于0時,重配置的參數(shù)來源于Clocking Wizard GUI界面。
對于本次實驗來說,只需要了解以上寄存器,即可實現(xiàn)對時鐘的重配置功能。配置步驟為:
1、先配置整體時鐘的分頻/倍頻系數(shù),通過Clock Configuration Register 0寄存器進行配置;
2、再配置所需時鐘的分頻系數(shù)(本次實驗只用到了一個時鐘,即CLKOUT0),通過Clock Configuration Register 2寄存器進行配置;
3、接下來加載重配置的參數(shù),由Clock Configuration Register 23寄存器進行加載;
4、最后判斷時鐘是否重配置完成,由Status Register (SR)寄存器的Bit[0](Locked)進行判斷。
在熟悉了時鐘IP核的重配置寄存器之后,再來看時鐘配置的代碼就比較容易了,clk_wiz.c代碼如下:

1  #include "xclk_wiz.h"
2  #include "clk_wiz.h"
3  #include "xparameters.h"
4  
5  #define CLK_WIZ_IN_FREQ 100  //時鐘IP核輸入100Mhz
6  
7  XClk_Wiz clk_wiz_inst;       //時鐘IP核驅(qū)動實例
"xclk_wiz.h"是Vitis提供的關(guān)于時鐘IP核相關(guān)的庫函數(shù)頭文件,"clk_wiz.h"是我們自己編寫的用于定義時鐘相關(guān)寄存器的頭文件。程序中第5行代碼定義了Clocking Wizard輸入源的時鐘頻率,為100Mhz;第7行代碼聲明了結(jié)構(gòu)體的變量,表示時鐘IP核的驅(qū)動實例。
9  //時鐘IP核動態(tài)重配置
10 //參數(shù)1:時鐘IP核的器件ID
11 //參數(shù)2:時鐘IP核輸出的時鐘 單位:MHz
12 void clk_wiz_cfg(u32 clk_device_id,double freq){
13  double div_factor = 0;
14  u32 div_factor_int = 0,dviv_factor_frac=0;
15  u32 clk_divide = 0;
16  u32 status = 0;
17 
18  //初始化XCLK_Wiz
19  XClk_Wiz_Config *clk_cfg_ptr;
20  clk_cfg_ptr = XClk_Wiz_LookupConfig(clk_device_id);
21  XClk_Wiz_CfgInitialize(&clk_wiz_inst,clk_cfg_ptr,clk_cfg_ptr->BaseAddr);
22 
23  if(freq <= 0)
24      return;
25  //配置時鐘倍頻/分頻系數(shù)
26  XClk_Wiz_WriteReg(clk_cfg_ptr->BaseAddr,CLK_CFG0_OFFSET,0x00000a01);  //10倍頻,1分頻
27  //計算分頻系數(shù)
28  div_factor = CLK_WIZ_IN_FREQ * 10 / freq;
29  div_factor_int = (u32)div_factor;
30  dviv_factor_frac = (u32)((div_factor - div_factor_int) * 1000);
31  clk_divide = div_factor_int | (dviv_factor_frac<<8);
32  //配置分頻系數(shù)
33  XClk_Wiz_WriteReg(clk_cfg_ptr->BaseAddr,CLK_CFG2_OFFSET,clk_divide);
34  //加載重配置的參數(shù)
35  XClk_Wiz_WriteReg(clk_cfg_ptr->BaseAddr,CLK_CFG23_OFFSET,0x00000003);
36  //獲取時鐘IP核的狀態(tài),判斷是否重配置完成
37  while(1){
38      status = XClk_Wiz_ReadReg(clk_cfg_ptr->BaseAddr,CLK_SR_OFFSET);
39      if(status&0x00000001)    //Bit0 Locked信號
40          return ;
41  }
42 }

時鐘IP核動態(tài)重配置函數(shù)共有兩個輸入?yún)?shù),分別為時鐘IP核的器件ID和時鐘IP核輸出的時鐘。程序中第19行至21行代碼根據(jù)時鐘IP核的器件ID,對時鐘IP核進行初始化,這個操作和初始化其它外設(shè)是一樣的。
程序中第26行代碼通過Clock Configuration Register 0寄存器配置時鐘的分頻/倍頻系數(shù),此處配置成10倍頻,1分頻。由于輸入的時鐘源為100Mhz,此處相當(dāng)于先對時鐘做10倍頻,即1000Mhz。
程序中第28至31行代碼根據(jù)1000Mhz和需要輸出的時鐘頻率,計算需要配置的分頻系數(shù),并得到分頻系數(shù)的整數(shù)部分和小數(shù)部分。程序中第33行代碼通過Clock Configuration Register 2寄存器配置CLKOUT0的分頻系數(shù)。程序中第35行代碼通過Clock Configuration Register 23寄存器加載重配置的參數(shù)。
程序的最后,通過Status Register (SR)寄存器來獲取status的值,判斷時鐘的重配置是否完成,當(dāng)status的最低位等于1時,表示重配置完成。
程序設(shè)計完成后,對代碼進行編譯。編譯完成后控制臺(Console)中會出現(xiàn)提示信息“Build Finished”,同時在應(yīng)用工程的Binaries目錄下可以看到生成的elf文件。
23.5下載驗證
首先我們將下載器與開發(fā)板上的JTAG接口連接,下載器另外一端與電腦連接。然后使用USB連接線將USB UART接口(PS_PORT)與電腦連接,用于串口通信。接下來使用FPC排線一端與RGB LCD液晶屏上的接口連接,另一端連接開發(fā)板上的RGB LCD接口,如圖 23.5.1和圖 23.5.2所示。連接時,將黑色翻蓋的兩側(cè)同時往上拉,將 FPC 排線藍色面朝黑色翻蓋方向插入,最后將黑色蓋兩側(cè)壓下以固定 FPC 排線。

圖 23.5.1 RGB LCD液晶屏
黑金7z100 ps驅(qū)動lcd,正點原子,fpga開發(fā)

圖 23.5.2 開發(fā)板連接圖
打開Vitis Terminal終端,設(shè)置并連接串口。然后下載本次實驗的程序,下載完成后,在下方的Terminal中可以看到應(yīng)用程序打印的信息,如下圖所示:
黑金7z100 ps驅(qū)動lcd,正點原子,fpga開發(fā)

圖 23.5.3 Terimnal打印的信息
由于本次實驗連接的是7寸液晶屏(分辨率:800*480),因此串口打印的LCD ID為7084。接下來觀察RGB LCD液晶屏,可以看到,LCD上顯示出彩條,彩條顏色從左到右依次為紅橙黃綠青藍紫,與寫入的彩虹色數(shù)據(jù)相同,如圖 23.5.4所示。
黑金7z100 ps驅(qū)動lcd,正點原子,fpga開發(fā)

圖 23.5.4 RGB LCD液晶屏顯示彩條文章來源地址http://www.zghlxwxcb.cn/news/detail-766028.html

到了這里,關(guān)于【正點原子FPGA連載】第二十三章PS通過VDMA驅(qū)動LCD顯示實驗 摘自【正點原子】DFZU2EG_4EV MPSoC之嵌入式Vitis開發(fā)指南的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經(jīng)查實,立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費用

相關(guān)文章

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包