亚洲综合图片区自拍_思思91精品国产综合在线观看_一区二区三区欧美_欧美黑人又粗又大_亚洲人成精品久久久久桥本

每日熱議!VGA接口原理與Verilog實(shí)現(xiàn)編程案例解析

2023-07-01 09:19:58 來(lái)源:FPGA之家

一、 軟件平臺(tái)與硬件平臺(tái)


(資料圖片僅供參考)

軟件平臺(tái):

1、操作系統(tǒng):Windows-8.1

2、開發(fā)套件:ISE14.7

3、仿真工具:ModelSim-10.4-SE

硬件平臺(tái):

1、 FPGA型號(hào):Xilinx公司的XC6SLX45-2CSG324

2、 VGA接口

3、 液晶顯示器

二、 原理介紹

VGA(Video Graphics Array)即視頻圖形陣列,是IBM在1987年推出的使用模擬信號(hào)的一種視頻傳輸標(biāo)準(zhǔn),在當(dāng)時(shí)具有分辨率高、顯示速率快、顏色豐富等優(yōu)點(diǎn),在彩色顯示器領(lǐng)域得到了廣泛的應(yīng)用。這個(gè)標(biāo)準(zhǔn)對(duì)于現(xiàn)今的個(gè)人電腦市場(chǎng)已經(jīng)十分過(guò)時(shí)。即使如此,VGA仍然是最多制造商所共同支持的一個(gè)標(biāo)準(zhǔn),個(gè)人電腦在加載自己的特殊驅(qū)動(dòng)程序之前,都必須支持VGA的標(biāo)準(zhǔn)。

VGA接口實(shí)物圖如下圖所示

左邊帶針的叫VGA公頭,右邊帶槽的叫VGA母頭。

VGA接口的特點(diǎn):

1、 VGA接口不支持熱插拔

VGA接口跟HDMI接口一樣是不支持熱插拔的。熱插拔是指一般帶電狀態(tài)下對(duì)于接插件的插入或是拔除,并不只是針對(duì)有電源接口或者帶供電的接口的接插件,而是所有。在運(yùn)行狀態(tài)時(shí),插拔會(huì)產(chǎn)生耦合電流,電流不穩(wěn)造成硬件燒壞,導(dǎo)致筆記本的接口端的保護(hù)受到?jīng)_擊。就像U盤不能再一個(gè)時(shí)間段多次在一個(gè)端口插拔使用一樣。各種電器的外露端子都會(huì)有金屬的部分,它們都是要求接地的,但是不同的電器之間的地并不一定相同,比如一臺(tái)DVD的地和一臺(tái)電視機(jī)的接地都是相對(duì)于本身系統(tǒng)而言。

當(dāng)端子插入時(shí),首先要建立共同的地來(lái)對(duì)傳輸?shù)男盘?hào)作參考,這就要依靠端子和傳輸線上的金屬部分了,金屬部分接地同時(shí)也是對(duì)信號(hào)的屏蔽和保護(hù)。兩個(gè)地相接觸一瞬間,會(huì)有很高的尖峰脈沖產(chǎn)生,這種脈沖如果不加以濾除可能會(huì)直達(dá)芯片并將其損壞。另外還有一種是ESD,即靜電損壞,這種更難以避免,因?yàn)樵?u>電子產(chǎn)品上,只能去防護(hù),ESD的持續(xù)時(shí)間會(huì)更短US級(jí)別。所以正規(guī)的電子產(chǎn)品對(duì)于金屬端子的接地有比較高的要求,同時(shí)在信號(hào)線上增加ESD防護(hù)器件來(lái)避免熱插拔的損壞。但實(shí)際上很多廠家為了節(jié)省成本而偷工減料,或者是對(duì)熱插拔的防護(hù)意識(shí)不夠?qū)е略O(shè)計(jì)不合理,使得用戶會(huì)出現(xiàn)熱插拔損壞電器的現(xiàn)象產(chǎn)生。

2、 VGA不能傳輸音頻

因?yàn)橐曨l是VGA信號(hào),而音頻信號(hào)不是,所以VGA不能傳輸音頻,只能傳輸視頻。相信這就是為什么這幾年極度的需求創(chuàng)新轉(zhuǎn)換器的原因。VGA不支持音頻傳輸也是給很多消費(fèi)者帶來(lái)煩惱,這最好的辦法其實(shí)就是購(gòu)買一款轉(zhuǎn)換器,VGA轉(zhuǎn)HDMI或者HDMI轉(zhuǎn)VGA,達(dá)到視頻傳輸?shù)耐瑫r(shí)還支持音頻信號(hào)的輸出,一舉兩得。但是不要只想著轉(zhuǎn)換器的輸入與輸出成問(wèn)題,同時(shí)想想音頻輸出口,3.5mm是音頻輸出信號(hào)的重要連接線。購(gòu)買時(shí)可以考慮想轉(zhuǎn)換器有沒(méi)有帶3.5mm的音頻輸出口,然后另外購(gòu)買一條音頻線。

3、 VGA接口是一種D型接口,上面共有15針孔,分成三排,每排五個(gè)。其中比較重要的是3根RGB彩色分量信號(hào)和2根掃描同步信號(hào)HSYNC和VSYNC針。其引腳編號(hào)圖如下圖所示:

其中每個(gè)管腳的詳細(xì)定義如下表所示

管腳名稱定義
1RED紅基色(75Ω,0.7Vp-p)
2GREEN綠基色(75Ω,0.7Vp-p)
3BLUE藍(lán)基色(75Ω,0.7Vp-p)
4ID2地址碼(顯示器標(biāo)識(shí)位2)
5GND
6RGND紅色地
7GGND綠色地
8BGND藍(lán)色地
9KEY保留
10SGND同步信號(hào)地
11ID0地址碼(顯示器標(biāo)識(shí)位0)
12ID1地址碼(顯示器標(biāo)識(shí)位1)
13HSYNC行同步信號(hào)
14VSYNC場(chǎng)同步信號(hào)
15ID3地址碼(顯示器標(biāo)識(shí)位3)

VGA接口時(shí)序詳解

VGA 顯示器掃描方式從屏幕左上角一點(diǎn)開始,從左向右逐點(diǎn)掃描,每掃描完一行,電子束回到屏幕的左邊下一行的起始位置,在這期間,CRT 對(duì)電子束進(jìn)行消隱,每行結(jié)束時(shí),用行同步信號(hào)進(jìn)行同步;當(dāng)掃描完所有的行,形成一幀,用場(chǎng)同步信號(hào)進(jìn)行場(chǎng)同步,并使掃描回到屏幕左上方,同時(shí)進(jìn)行場(chǎng)消隱,開始下一幀。完成一行掃描的時(shí)間稱為水平掃描時(shí)間,其倒數(shù)稱為行頻率;完成一幀(整屏)掃描的時(shí)間稱為垂直掃描時(shí)間,其倒數(shù)稱為場(chǎng)頻率,即屏幕的刷新頻率,常見(jiàn)的有 60Hz,75Hz 等等,但標(biāo)準(zhǔn)的 VGA 顯示的場(chǎng)頻 60Hz。其掃描示意圖如下圖所示

在對(duì)VGA掃描方式有一個(gè)直觀的感受以后接下來(lái)在看一看VGA接口的詳細(xì)時(shí)序與各個(gè)參數(shù)的定義。VGA的詳細(xì)時(shí)序如下圖所示:

總的來(lái)說(shuō),VGA的時(shí)序主要包括行時(shí)序與場(chǎng)時(shí)序兩個(gè)部分。

其中行時(shí)序主要包括:行同步(Hor Sync) 、行消隱(Hor Back Porch) 、行視頻有效(Hor Active Video)和行前肩(Hor Front Porch)這四個(gè)參數(shù),行時(shí)序的時(shí)序圖如下圖所示

而場(chǎng)時(shí)序主要包括:場(chǎng)同步(Ver Sync) 、場(chǎng)消隱(Ver Back Porch) 、場(chǎng)視頻有效(Ver Active Video)和場(chǎng)前肩(Ver Front Porch)這四個(gè)參數(shù),場(chǎng)時(shí)序的時(shí)序圖如下圖所示

需要注意的有三點(diǎn):

1、行時(shí)序是以”像素”為單位的, 場(chǎng)時(shí)序是以”行”為單位的。

2、VGA 工業(yè)標(biāo)準(zhǔn)顯示模式要求:行同步,場(chǎng)同步都為負(fù)極性,即同步脈沖要求是負(fù)脈沖。

3、VGA 行時(shí)序?qū)π型綍r(shí)間、 消隱時(shí)間、 行視頻有效時(shí)間和行前肩時(shí)間有特定的規(guī)范, 場(chǎng)時(shí)序也是如此。常用VGA 分辨率時(shí)序參數(shù)如下表所示

其中:

Pixel Clock = (Screen Refresh Frequency)*(Hor Active Video + Hor Front Porch + Hor Synv Pulse + Hor Back Porch)* (Ver Active Video + Ver Front Porch + Ver Synv Pulse + Ver Back Porch)

以640x480,60Hz這種分辨率格式來(lái)說(shuō),25.175MHz = 25175000Hz = 60*(640 + 16 + 96 + 48)*(480 + 11 + 2 + 31) = 60 * 800 * 525

三、 目標(biāo)任務(wù)

1、編寫VGA驅(qū)動(dòng)代碼,并用ModelSim對(duì)時(shí)序進(jìn)行仿真,然后下載到開發(fā)板中使屏幕產(chǎn)生彩色條紋

2、在上個(gè)任務(wù)的基礎(chǔ)上,把一張存在ROM里面的圖片數(shù)據(jù)顯示到顯示器上

四、 設(shè)計(jì)思路與Verilog代碼編寫

4.1、 VGA驅(qū)動(dòng)模塊的接口定義與整體設(shè)計(jì)

Verilog編寫的VGA模塊除了Red,Green,Blue三基色、行同步HS以及場(chǎng)同步VS以外還要包括時(shí)鐘、復(fù)位信號(hào)。其框圖如下所示

其中:

I_clk是系統(tǒng)時(shí)鐘;

I_rst_n是系統(tǒng)復(fù)位;

O_hs是行同步信號(hào);

O_vs是場(chǎng)同步信號(hào);

O_red是紅色分量;

O_green是綠色分量;

O_blue是藍(lán)色分量;

上面的模塊框圖中沒(méi)有看到測(cè)試數(shù)據(jù)(彩條或者圖片)的輸入端口,原因是由于VGA的邏輯比較簡(jiǎn)單,所以我準(zhǔn)備把發(fā)送測(cè)試圖案(彩條或者圖片)的邏輯也直接集成到vga_driver模塊中,這樣可能更加方便理解。但是對(duì)于實(shí)際一個(gè)比較復(fù)雜的項(xiàng)目來(lái)說(shuō),最好還是把各個(gè)模塊獨(dú)立開來(lái),這樣更加方便二次移植。在寫代碼之前,先了解一個(gè)關(guān)于圖片的分辨率與位深度的知識(shí)點(diǎn)。

4.2、 圖片的分辨率、圖片的尺寸與位深度

圖片的分辨率指圖像中存儲(chǔ)的信息量,是每英寸圖像內(nèi)有多少個(gè)像素點(diǎn),它決定了位圖圖像細(xì)節(jié)的精細(xì)程度。描述分辨率的單位有:dpi(dots per inch)點(diǎn)每英寸、lpi(line per inch)線每英寸和ppi(pixel per inch)像素每英寸。

圖片的尺寸是指一幅圖片長(zhǎng)度和寬度各占多少像素,我們平常說(shuō)的一張640×480的圖片指的就是這張圖片的長(zhǎng)度有640個(gè)像素點(diǎn),寬度有480個(gè)像素點(diǎn)

位深度是指圖片的每個(gè)像素是用多少位(bit)來(lái)表示的。比如黑白二色的圖像是數(shù)字圖像中最簡(jiǎn)單的一種,它只有黑、白兩種顏色,也就是說(shuō)它的每個(gè)像素只有1位顏色,位深度是1,用2的零次冪來(lái)表示;考慮到位深度平均分給R, G, B和Alpha,而只有RGB可以相互組合成顏色。所以4位顏色的圖,它的位深度是4,只有2的4次冪種顏色,即16種顏色或16種灰度等級(jí) )。8位顏色的圖,位深度就是8,用2的8次冪表示,它含有256種顏色 ( 或256種灰度等級(jí) )。24位顏色可稱之為真彩色,位深度是24,它能組合成2的24次冪種顏色,即:16777216種顏色 ( 或稱千萬(wàn)種顏色 ),超過(guò)了人眼能夠分辨的顏色數(shù)量。當(dāng)我們用24位來(lái)記錄顏色時(shí),實(shí)際上是以2^(8×3),即紅、綠、藍(lán) ( RGB )三基色各以2的8次冪,256種顏色而存在的,三色組合就形成一千六百萬(wàn)種顏色。除了上面這幾種情況以外,有的圖片的位深度是16位,其中紅基色占5位,綠基色占6位,藍(lán)基色占5位,他們一共可以組成2^16中顏色。

在電腦上用選中圖片以后,然后鼠標(biāo)右鍵在菜單中點(diǎn)擊屬性,然后在詳細(xì)信息選項(xiàng)卡中就能查看圖片的各個(gè)詳細(xì)信息了,上面這張圖片的信息如下圖所示

由上面的信息可知這張圖片的大小為128*128。水平分辨率與垂直分辨率為96dpi(dots per inch),位深度為24-bit。

4.3、 原理圖分析

在寫代碼之前,先來(lái)分析一下我的開發(fā)板的VGA接口原理圖。由于FPGA輸出的RGB數(shù)據(jù)為數(shù)字信號(hào),而VGA接口的RGB數(shù)據(jù)為模擬信號(hào),所以需要一個(gè)數(shù)模轉(zhuǎn)換器把FPGA輸出的數(shù)字信號(hào)轉(zhuǎn)化為VGA接口的模擬RGB數(shù)據(jù)輸出。一般情況下,為了保證輸出數(shù)據(jù)的保真度,都會(huì)使用一個(gè)專用的數(shù)模轉(zhuǎn)換芯片(比如ADV7123)來(lái)實(shí)現(xiàn)這個(gè)數(shù)模轉(zhuǎn)換的功能,但是在我的開發(fā)板上為了簡(jiǎn)單起見(jiàn),設(shè)計(jì)了一個(gè)電阻匹配網(wǎng)絡(luò)來(lái)實(shí)現(xiàn)這個(gè)數(shù)模轉(zhuǎn)換的功能,F(xiàn)PGA輸出的RGB三基色數(shù)字信號(hào)一共占16-bit,其中Red分量占5-bit,Green分量占6-bit,Blue分量占5-bit。下面是VGA接口部分的原理圖

4.4、 vga_driver模塊顯示彩條Verilog代碼編寫

有了上面的基礎(chǔ)之后就可以開始著手編寫代碼,現(xiàn)在在回過(guò)頭去看行時(shí)序與場(chǎng)時(shí)序,其實(shí)可以發(fā)現(xiàn)VGA的時(shí)序真的是非常簡(jiǎn)單。

對(duì)行時(shí)序來(lái)說(shuō),只需要定義一個(gè)計(jì)數(shù)器,當(dāng)計(jì)數(shù)器在像素時(shí)鐘的作用下計(jì)滿一行的總點(diǎn)數(shù)后清零,然后利用assign語(yǔ)句在計(jì)數(shù)值為Hor Sync期間把行時(shí)序信號(hào)拉低產(chǎn)生一個(gè)低脈沖就可以了。場(chǎng)時(shí)序與行時(shí)序非常類似,當(dāng)行計(jì)數(shù)器計(jì)滿一行了場(chǎng)計(jì)數(shù)器才加1,當(dāng)計(jì)滿一場(chǎng)的時(shí)間后,計(jì)數(shù)值清零,然后利用assign語(yǔ)句在Ver Sync期間把場(chǎng)時(shí)序信號(hào)拉低產(chǎn)生一個(gè)低脈沖就OK了。

有了行時(shí)序與場(chǎng)時(shí)序以后,接下來(lái)就是在Hor Active Video和Ver Active Video均有效的期間往Red,Green,Blue三個(gè)分量送數(shù)據(jù),數(shù)據(jù)就會(huì)在在屏幕上顯示出來(lái)了。而Hor Active Video有效的期間正是行計(jì)數(shù)器的計(jì)數(shù)值在大于(Hor Sync + Hor Back Porch),小于(Hor Sync + Hor Back Porch + Hor Active Video)的時(shí)候,而Ver Active Video有效的期間正是場(chǎng)計(jì)數(shù)器的計(jì)數(shù)值在大于(Ver Sync + Ver Back Porch),小于(Ver Sync + Ver Back Porch + Ver Active Video)的時(shí)候,所以在代碼里面可以利用assign語(yǔ)句產(chǎn)生一個(gè)激活標(biāo)志,當(dāng)激活標(biāo)志為高的時(shí)候給Red,Green,Blue三個(gè)分量送數(shù)據(jù),數(shù)據(jù)就會(huì)在屏幕顯示出來(lái)了。

下面以分辨率為640x480為例來(lái)編寫vga_driver的代碼,由前面的分辨率時(shí)序參數(shù)表可知,640x480分辨率的像素時(shí)鐘為25.175Hz,但實(shí)際并不需要這么精確的時(shí)鐘頻率,我們?nèi)?5MHz就可以了,我的開發(fā)板的時(shí)鐘頻率為50MHz,所以只需要簡(jiǎn)單的寫一個(gè)二分頻邏輯就可以得到這個(gè)像素時(shí)鐘了。如果你想顯示其他分辨率的圖片,比如800x600分辨率的時(shí)鐘頻率是40MHz,這時(shí)候就需要用FPGA內(nèi)部的Clocking Wizard IP核來(lái)得到這個(gè)40MHz的時(shí)鐘,Clocking Wizard IP核內(nèi)部回調(diào)用FPGA的PLL資源對(duì)輸入頻率進(jìn)行處理來(lái)得到想要的輸出頻率。

下面是VGA接口產(chǎn)生彩條的完整代碼

module vga_driver(    input                   I_clk   , // 系統(tǒng)50MHz時(shí)鐘    input                   I_rst_n , // 系統(tǒng)復(fù)位    output   reg   [4:0]    O_red   , // VGA紅色分量    output   reg   [5:0]    O_green , // VGA綠色分量    output   reg   [4:0]    O_blue  , // VGA藍(lán)色分量    output                  O_hs    , // VGA行同步信號(hào)    output                  O_vs      // VGA場(chǎng)同步信號(hào));// 分辨率為640*480時(shí)行時(shí)序各個(gè)參數(shù)定義parameter       C_H_SYNC_PULSE      =   96  ,                C_H_BACK_PORCH      =   48  ,                C_H_ACTIVE_TIME     =   640 ,                C_H_FRONT_PORCH     =   16  ,                C_H_LINE_PERIOD     =   800 ;// 分辨率為640*480時(shí)場(chǎng)時(shí)序各個(gè)參數(shù)定義parameter       C_V_SYNC_PULSE      =   2   ,                C_V_BACK_PORCH      =   33  ,                C_V_ACTIVE_TIME     =   480 ,                C_V_FRONT_PORCH     =   10  ,                C_V_FRAME_PERIOD    =   525 ;                parameter       C_COLOR_BAR_WIDTH   =   C_H_ACTIVE_TIME / 8  ;reg [11:0]      R_h_cnt         ; // 行時(shí)序計(jì)數(shù)器reg [11:0]      R_v_cnt         ; // 列時(shí)序計(jì)數(shù)器reg             R_clk_25M       ;wire            W_active_flag   ; // 激活標(biāo)志,當(dāng)這個(gè)信號(hào)為1時(shí)RGB的數(shù)據(jù)可以顯示在屏幕上////////////////////////////////////////////////////////////////////功能:產(chǎn)生25MHz的像素時(shí)鐘//////////////////////////////////////////////////////////////////always @(posedge I_clk ornegedge I_rst_n)begin    if(!I_rst_n)        R_clk_25M   <=  1"b0        ;    else        R_clk_25M   <=  ~R_clk_25M  ;end////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 功能:產(chǎn)生行時(shí)序//////////////////////////////////////////////////////////////////always @(posedge R_clk_25M or negedge I_rst_n)begin    if(!I_rst_n)        R_h_cnt <=  12"d0   ;    else if(R_h_cnt == C_H_LINE_PERIOD - 1"b1)        R_h_cnt <=  12"d0   ;    else        R_h_cnt <=  R_h_cnt + 1"b1  ;end                assign O_hs =   (R_h_cnt < C_H_SYNC_PULSE) ? 1"b0 : 1"b1    ;////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 功能:產(chǎn)生場(chǎng)時(shí)序//////////////////////////////////////////////////////////////////always @(posedge R_clk_25M or negedge I_rst_n)begin    if(!I_rst_n)        R_v_cnt <=  12"d0   ;    else if(R_v_cnt == C_V_FRAME_PERIOD - 1"b1)        R_v_cnt <=  12"d0   ;    else if(R_h_cnt == C_H_LINE_PERIOD - 1"b1)        R_v_cnt <=  R_v_cnt + 1"b1  ;    else        R_v_cnt <=  R_v_cnt ;end                assign O_vs =   (R_v_cnt < C_V_SYNC_PULSE) ? 1"b0 : 1"b1    ;//////////////////////////////////////////////////////////////////  assign W_active_flag =  (R_h_cnt >= (C_H_SYNC_PULSE + C_H_BACK_PORCH                  ))  &&                        (R_h_cnt <= (C_H_SYNC_PULSE + C_H_BACK_PORCH + C_H_ACTIVE_TIME))  &&                         (R_v_cnt >= (C_V_SYNC_PULSE + C_V_BACK_PORCH                  ))  &&                        (R_v_cnt <= (C_V_SYNC_PULSE + C_V_BACK_PORCH + C_V_ACTIVE_TIME))  ;//////////////////////////////////////////////////////////////////// 功能:把顯示器屏幕分成8個(gè)縱列,每個(gè)縱列的寬度是80//////////////////////////////////////////////////////////////////always @(posedge R_clk_25M or negedge I_rst_n)begin    if(!I_rst_n)        begin            O_red   <=  5"b00000    ;            O_green <=  6"b000000   ;            O_blue  <=  5"b00000    ;        end    else if(W_active_flag)        begin            if(R_h_cnt < (C_H_SYNC_PULSE + C_H_BACK_PORCH + C_COLOR_BAR_WIDTH)) // 紅色彩條                begin                    O_red   <=  5"b11111    ; // 紅色彩條把紅色分量全部給1,綠色和藍(lán)色給0                    O_green <=  6"b000000   ;                    O_blue  <=  5"b00000    ;                end            else if(R_h_cnt < (C_H_SYNC_PULSE + C_H_BACK_PORCH + C_COLOR_BAR_WIDTH*2)) // 綠色彩條                begin                    O_red   <=  5"b00000    ;                    O_green <=  6"b111111   ; // 綠色彩條把綠色分量全部給1,紅色和藍(lán)色分量給0                    O_blue  <=  5"b00000    ;                end             else if(R_h_cnt < (C_H_SYNC_PULSE + C_H_BACK_PORCH + C_COLOR_BAR_WIDTH*3)) // 藍(lán)色彩條                begin                    O_red   <=  5"b00000    ;                    O_green <=  6"b000000   ;                    O_blue  <=  5"b11111    ; // 藍(lán)色彩條把藍(lán)色分量全部給1,紅色和綠分量給0                end             else if(R_h_cnt < (C_H_SYNC_PULSE + C_H_BACK_PORCH + C_COLOR_BAR_WIDTH*4)) // 白色彩條                begin                    O_red   <=  5"b11111    ; // 白色彩條是有紅綠藍(lán)三基色混合而成                    O_green <=  6"b111111   ; // 所以白色彩條要把紅綠藍(lán)三個(gè)分量全部給1                    O_blue  <=  5"b11111    ;                end             else if(R_h_cnt < (C_H_SYNC_PULSE + C_H_BACK_PORCH + C_COLOR_BAR_WIDTH*5)) // 黑色彩條                begin                    O_red   <=  5"b00000    ; // 黑色彩條就是把紅綠藍(lán)所有分量全部給0                    O_green <=  6"b000000   ;                    O_blue  <=  5"b00000    ;                end             else if(R_h_cnt < (C_H_SYNC_PULSE + C_H_BACK_PORCH + C_COLOR_BAR_WIDTH*6)) // 黃色彩條                begin                    O_red   <=  5"b11111    ; // 黃色彩條是有紅綠兩種顏色混合而成                    O_green <=  6"b111111   ; // 所以黃色彩條要把紅綠兩個(gè)分量給1                    O_blue  <=  5"b00000    ; // 藍(lán)色分量給0                end             else if(R_h_cnt < (C_H_SYNC_PULSE + C_H_BACK_PORCH + C_COLOR_BAR_WIDTH*7)) // 紫色彩條                begin                    O_red   <=  5"b11111    ; // 紫色彩條是有紅藍(lán)兩種顏色混合而成                    O_green <=  6"b000000   ; // 所以紫色彩條要把紅藍(lán)兩個(gè)分量給1                    O_blue  <=  5"b11111    ; // 綠色分量給0                end             else                              // 青色彩條                begin                    O_red   <=  5"b00000    ; // 青色彩條是由藍(lán)綠兩種顏色混合而成                    O_green <=  6"b111111   ; // 所以青色彩條要把藍(lán)綠兩個(gè)分量給1                    O_blue  <=  5"b11111    ; // 紅色分量給0                end                           end    else        begin            O_red   <=  5"b00000    ;            O_green <=  6"b000000   ;            O_blue  <=  5"b00000    ;        end           end/*////////////////////////////////////////////////////////////////// 功能:產(chǎn)生黑白相間的格子圖案////////////////////////////////////////////////////////////////always @(posedge R_clk_25M or negedge I_rst_n)begin    if(!I_rst_n)        begin            O_red   <=  5"b00000    ;            O_green <=  6"b000000   ;            O_blue  <=  5"b00000    ;        end    else if(W_active_flag)        begin            if((R_h_cnt[4]==1"b1) ^ (R_v_cnt[4]==1"b1))                begin                    O_red   <=  5"b00000    ;                    O_green <=  6"b000000   ;                    O_blue  <=  5"b00000    ;                end            else                begin                    O_red   <=  5"b11111    ;                    O_green <=  6"b111111   ;                    O_blue  <=  5"b11111    ;                end        end    else        begin            O_red   <=  5"b00000    ;            O_green <=  6"b000000   ;            O_blue  <=  5"b00000    ;        endend*/endmodule

整個(gè)代碼的編寫思路與上面的分析基本一致。除了顯示彩條以外,還可以顯示黑白格子,代碼在上面也已經(jīng)給出。更多更有趣的圖案大家可以自己多試一下。在下載到開發(fā)板之前先用ModelSim對(duì)整個(gè)邏輯進(jìn)行一下仿真,在測(cè)試激勵(lì)文件里面只需要給時(shí)鐘和復(fù)位加上激勵(lì)就可以了,非常簡(jiǎn)單,我就不貼激勵(lì)文件的代碼了,下圖是ModelSim仿真圖

具體的時(shí)序細(xì)節(jié)大家可以自己仿的試一下,整個(gè)時(shí)序都是沒(méi)問(wèn)題的。

下面是代碼下載到我的開發(fā)板中顯示器顯示的彩條圖案

4.5、 如何把一張圖片轉(zhuǎn)化為存放在ROM中的.coe文件

顯示完彩條以后,接下來(lái)就是把一張圖片顯示在液晶顯示器上。完成這個(gè)任務(wù)的第一步就是要把一張圖片轉(zhuǎn)化為ROM可以導(dǎo)入的.coe文件,第二部就是把ROM中的圖片數(shù)據(jù)在有效區(qū)域輸出出來(lái),這樣就可以在圖片上顯示一張圖片了。由于我的FPGA型號(hào)為XC6SLX45-2CSG324,它內(nèi)部的BRAM資源十分有限,無(wú)法存儲(chǔ)一張完整640*480分辨率的圖片數(shù)據(jù),所以接下來(lái)的實(shí)驗(yàn)我會(huì)把上文那張128x128的圣誕老人的圖片顯示在液晶顯示器上。如果你的FPGA內(nèi)部資源足夠的話,你可以以我這個(gè)例子作為參考把你想顯示的圖片顯示出來(lái)。

為了把圖片轉(zhuǎn)化為.coe文件存放在ROM,這時(shí)可以利用Matlab軟件先把圖片轉(zhuǎn)化為一個(gè)三維矩陣,然后把三維矩陣中的Red,Green,Blue分量的顏色數(shù)據(jù)提取出來(lái)按照5-bit Red,6-bit Green, 5-bit Blue的格式進(jìn)行拼接,最后寫入到.coe文件中,Matlab的完整代碼如下:

clearclc% 利用imread函數(shù)把圖片轉(zhuǎn)化為一個(gè)三維矩陣image_array = imread("333.jpg");% 利用size函數(shù)把圖片矩陣的三個(gè)維度大小計(jì)算出來(lái)% 第一維為圖片的高度,第二維為圖片的寬度,第三維為圖片的RGB分量[height,width,z]=size(image_array);   % 128*128*3% imshow(image_array); % 顯示圖片red   = image_array(:,:,1); % 提取紅色分量,數(shù)據(jù)類型為uint8green = image_array(:,:,2); % 提取綠色分量,數(shù)據(jù)類型為uint8blue  = image_array(:,:,3); % 提取藍(lán)色分量,數(shù)據(jù)類型為uint8% 把上面得到了各個(gè)分量重組成一個(gè)1維矩陣,由于reshape函數(shù)重組矩陣的% 時(shí)候是按照列進(jìn)行重組的,所以重組前需要先把各個(gè)分量矩陣進(jìn)行轉(zhuǎn)置以% 后在重組% 利用reshape重組完畢以后,由于后面需要對(duì)數(shù)據(jù)拼接,所以為了避免溢出% 這里把uint8類型的數(shù)據(jù)擴(kuò)大為uint32類型r = uint32(reshape(red"   , 1 ,height*width));g = uint32(reshape(green" , 1 ,height*width));b = uint32(reshape(blue"  , 1 ,height*width));% 初始化要寫入.coe文件中的RGB顏色矩陣rgb=zeros(1,height*width);% 因?yàn)閷?dǎo)入的圖片是24-bit真彩色圖片,每個(gè)像素占用24-bit,其中RGB分別占用8-bit% 而我這里需要的是16-bit,其中R為5-bit,G為6-bit,B為5-bit,所以需要在這里對(duì)% 24-bit的數(shù)據(jù)進(jìn)行重組與拼接% bitshift()函數(shù)的作用是對(duì)數(shù)據(jù)進(jìn)行移位操作,其中第一個(gè)參數(shù)是要進(jìn)行移位的數(shù)據(jù),第二個(gè)參數(shù)為負(fù)數(shù)表示向右移,為% 正數(shù)表示向左移,更詳細(xì)的用法直接在Matlab命令窗口輸入 doc bitshift 進(jìn)行查看% 所以這里對(duì)紅色分量先右移3位取出高5位,然后左移11位作為ROM中RGB數(shù)據(jù)的第15-bit到第11-bit% 對(duì)綠色分量先右移2位取出高6位,然后左移5位作為ROM中RGB數(shù)據(jù)的第10-bit到第5-bit% 對(duì)藍(lán)色分量先右移3位取出高5位,然后左移0位作為ROM中RGB數(shù)據(jù)的第4-bit到第0-bitfor i = 1:height*width    rgb(i) = bitshift(bitshift(r(i),-3),11) + bitshift(bitshift(g(i),-2),5) + bitshift(bitshift(b(i),-3),0);endfid = fopen( "image.coe", "w+" );% .coe文件的最前面一行必須為這個(gè)字符串,其中16表示16進(jìn)制fprintf( fid, "memory_initialization_radix=16;");% .coe文件的第二行必須為這個(gè)字符串fprintf( fid, "memory_initialization_vector =");% 把rgb數(shù)據(jù)的前 height*width-1  個(gè)數(shù)據(jù)寫入.coe文件中,每個(gè)數(shù)據(jù)之間用逗號(hào)隔開fprintf( fid, "%x,",rgb(1:end-1));% 把rgb數(shù)據(jù)的最后一個(gè)數(shù)據(jù)寫入.coe文件中,并用分號(hào)結(jié)尾fprintf( fid, "%x;",rgb(end));fclose( fid ); % 關(guān)閉文件指針

基本上每行我都做了詳細(xì)的注釋,最后我在強(qiáng)調(diào)幾點(diǎn):

1、運(yùn)行這段代碼的時(shí)候必須把圖片文件和Matlab的.m文件放在同一目錄。imread()函數(shù)的參數(shù)是一個(gè)字符串,這個(gè)字符串是圖片的名字。如果圖片與Matlab的.m文件不再同一個(gè)目錄,則imread()里面需要填寫圖片的絕對(duì)路徑。

2、在Matlab對(duì)矩陣進(jìn)行轉(zhuǎn)置只需要在矩陣的右邊打一個(gè)單引號(hào)就可以了

3、Matlab執(zhí)行循環(huán)的效率非常低,所以我在數(shù)據(jù)進(jìn)行重組與拼接之前把數(shù)據(jù)用reshape函數(shù)轉(zhuǎn)化為1維矩陣并用uint32函數(shù)擴(kuò)大了數(shù)據(jù)的范圍,這樣的好處是重組拼接的時(shí)候只需要一個(gè)for循環(huán)就ok。如果你覺(jué)得用reshape和uint32這兩個(gè)函數(shù)對(duì)數(shù)據(jù)進(jìn)行預(yù)處理太麻煩了,那么你可以使用一個(gè)二維for循環(huán)進(jìn)行處理對(duì)128*128的矩陣的行和列分別處理。因?yàn)檫@里數(shù)據(jù)量不算大,所以用單個(gè)for循環(huán)和二維for循環(huán)區(qū)別不太大。

4、如果你的開發(fā)板硬件支持顯示24-bit真彩色圖片,那么上面的代碼中你就不需要對(duì)RGB分量分別右移進(jìn)行截取操作,直接左移位并相加(拼接)就可以了

4.6、 vga_driver模塊顯示圖片Verilog代碼編寫

有了之前顯示彩條的基礎(chǔ)以后,這個(gè)實(shí)驗(yàn)就非常簡(jiǎn)單了,只要在把顯示彩條的邏輯修改為從ROM讀數(shù)據(jù)就可以了。而ROM的配置過(guò)程如下所示:

1、選擇Block Memory Generator

2、選擇類型為Single Port ROM

3、選擇ROM的Read Width為16,Read Depth為16384(128*128=16384)

4、導(dǎo)入用Matlab生成的.coe圖片文件

5、其他的所有參數(shù)我都保持默認(rèn),如果你需要有其他特殊需求的話可以按需修改。

配置好ROM就可以修改發(fā)送RGB數(shù)據(jù)的邏輯了,完整的代碼如下:

module vga_driver(    input                   I_clk   , // 系統(tǒng)50MHz時(shí)鐘    input                   I_rst_n , // 系統(tǒng)復(fù)位    output   reg   [4:0]    O_red   , // VGA紅色分量    output   reg   [5:0]    O_green , // VGA綠色分量    output   reg   [4:0]    O_blue  , // VGA藍(lán)色分量    output                  O_hs    , // VGA行同步信號(hào)    output                  O_vs      // VGA場(chǎng)同步信號(hào));// 分辨率為640*480時(shí)行時(shí)序各個(gè)參數(shù)定義parameter       C_H_SYNC_PULSE      =   96  ,                C_H_BACK_PORCH      =   48  ,                C_H_ACTIVE_TIME     =   640 ,                C_H_FRONT_PORCH     =   16  ,                C_H_LINE_PERIOD     =   800 ;// 分辨率為640*480時(shí)場(chǎng)時(shí)序各個(gè)參數(shù)定義parameter       C_V_SYNC_PULSE      =   2   ,                C_V_BACK_PORCH      =   33  ,                C_V_ACTIVE_TIME     =   480 ,                C_V_FRONT_PORCH     =   10  ,                C_V_FRAME_PERIOD    =   525 ;parameter       C_IMAGE_WIDTH       =   128     ,                C_IMAGE_HEIGHT      =   128     ,                C_IMAGE_PIX_NUM     =   16384   ;reg     [11:0]      R_h_cnt         ; // 行時(shí)序計(jì)數(shù)器reg     [11:0]      R_v_cnt         ; // 列時(shí)序計(jì)數(shù)器reg                 R_clk_25M       ; // 25MHz的像素時(shí)鐘reg     [13:0]      R_rom_addr      ; // ROM的地址wire    [15:0]      W_rom_data      ; // ROM中存儲(chǔ)的數(shù)據(jù)wire            W_active_flag   ; // 激活標(biāo)志,當(dāng)這個(gè)信號(hào)為1時(shí)RGB的數(shù)據(jù)可以顯示在屏幕上////////////////////////////////////////////////////////////////////功能:產(chǎn)生25MHz的像素時(shí)鐘//////////////////////////////////////////////////////////////////always @(posedge I_clk or negedge I_rst_n)begin    if(!I_rst_n)        R_clk_25M   <=  1"b0        ;    else        R_clk_25M   <=  ~R_clk_25M  ;end////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 功能:產(chǎn)生行時(shí)序//////////////////////////////////////////////////////////////////always @(posedge R_clk_25M or negedge I_rst_n)begin    if(!I_rst_n)        R_h_cnt <=  12"d0   ;    else if(R_h_cnt == C_H_LINE_PERIOD - 1"b1)        R_h_cnt <=  12"d0   ;    else        R_h_cnt <=  R_h_cnt + 1"b1  ;end                assign O_hs =   (R_h_cnt < C_H_SYNC_PULSE) ? 1"b0 : 1"b1    ;////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 功能:產(chǎn)生場(chǎng)時(shí)序//////////////////////////////////////////////////////////////////always @(posedge R_clk_25M or negedge I_rst_n)begin    if(!I_rst_n)        R_v_cnt <=  12"d0   ;    else if(R_v_cnt == C_V_FRAME_PERIOD - 1"b1)        R_v_cnt <=  12"d0   ;    else if(R_h_cnt == C_H_LINE_PERIOD - 1"b1)        R_v_cnt <=  R_v_cnt + 1"b1  ;    else        R_v_cnt <=  R_v_cnt ;end                assign O_vs =   (R_v_cnt < C_V_SYNC_PULSE) ? 1"b0 : 1"b1    ;//////////////////////////////////////////////////////////////////  // 產(chǎn)生有效區(qū)域標(biāo)志,當(dāng)這個(gè)信號(hào)為高時(shí)往RGB送的數(shù)據(jù)才會(huì)顯示到屏幕上assign W_active_flag =  (R_h_cnt >= (C_H_SYNC_PULSE + C_H_BACK_PORCH                     ))  &&                        (R_h_cnt <= (C_H_SYNC_PULSE + C_H_BACK_PORCH + C_H_ACTIVE_TIME     ))  &&                         (R_v_cnt >= (C_V_SYNC_PULSE + C_V_BACK_PORCH                    ))  &&                        (R_v_cnt <= (C_V_SYNC_PULSE + C_V_BACK_PORCH + C_V_ACTIVE_TIME     ))  ;//////////////////////////////////////////////////////////////////// 功能:把ROM里面的圖片數(shù)據(jù)輸出//////////////////////////////////////////////////////////////////always @(posedge R_clk_25M or negedge I_rst_n)begin    if(!I_rst_n)        R_rom_addr  <=  14"d0 ;    else if(W_active_flag)        begin            if(R_h_cnt >= (C_H_SYNC_PULSE + C_H_BACK_PORCH                        )  &&                R_h_cnt <= (C_H_SYNC_PULSE + C_H_BACK_PORCH + C_IMAGE_WIDTH  - 1"b1)  &&               R_v_cnt >= (C_V_SYNC_PULSE + C_V_BACK_PORCH                        )  &&                R_v_cnt <= (C_V_SYNC_PULSE + C_V_BACK_PORCH + C_IMAGE_HEIGHT - 1"b1)  )                begin                    O_red       <= W_rom_data[15:11]    ; // 紅色分量                    O_green     <= W_rom_data[10:5]     ; // 綠色分量                    O_blue      <= W_rom_data[4:0]      ; // 藍(lán)色分量                    if(R_rom_addr == C_IMAGE_PIX_NUM - 1"b1)                        R_rom_addr  <=  14"d0 ;                    else                        R_rom_addr  <=  R_rom_addr  +  1"b1 ;                end            else                begin                    O_red       <=  5"d0        ;                    O_green     <=  6"d0        ;                    O_blue      <=  5"d0        ;                    R_rom_addr  <=  R_rom_addr  ;                end                                  end    else        begin            O_red       <=  5"d0        ;            O_green     <=  6"d0        ;            O_blue      <=  5"d0        ;            R_rom_addr  <=  R_rom_addr  ;        end          endrom_image U_rom_image (  .clka(R_clk_25M), // input clka  .addra(R_rom_addr), // input [13 : 0] addra  .douta(W_rom_data) // output [15 : 0] douta);endmodule

綁定管腳生成bit文件以后下載到開發(fā)板中液晶顯示器上就出現(xiàn)了圣誕老人的圖片

至此,整個(gè)VGA的實(shí)驗(yàn)全部完成。

五、 進(jìn)一步思考

5.1、 如何修改屏幕背景為其他顏色(比如藍(lán)色)

上面已經(jīng)可以讓一張圖片正常在屏幕上顯示出來(lái)了,而圖片以外的區(qū)域是黑色的,如果我們想把圖片以外的區(qū)域改成藍(lán)色的其實(shí)很簡(jiǎn)單,只需要圖片以外區(qū)域的RGB分量的Red,Green分量給0,Blue分量給1就可以了

5.2、如何讓128*128圣誕老人的圖片在屏幕中間

上圖的圣誕老人圖片顯示在屏幕的左上角,如果我們想把他顯示在圖片中間位置只需要在

if(R_h_cnt >= (C_H_SYNC_PULSE + C_H_BACK_PORCH + C_H_OFFSET                          )  &&                R_h_cnt <= (C_H_SYNC_PULSE + C_H_BACK_PORCH + C_H_OFFSET + C_IMAGE_WIDTH  - 1"b1    )  &&               R_v_cnt >= (C_V_SYNC_PULSE + C_V_BACK_PORCH + C_V_OFFSET                           )  &&                R_v_cnt <= (C_V_SYNC_PULSE + C_V_BACK_PORCH + C_V_OFFSET + C_IMAGE_HEIGHT - 1"b1    )  )

這個(gè)判斷語(yǔ)句中加上一個(gè)固定的C_H_OFFSET與C_V_OFFSET偏移分量就可以了,當(dāng)然這個(gè)偏移分量不能太大,否則會(huì)使圖片跑到屏幕外面

5.3、 如何讓128*128圣誕老人的圖片在屏幕上動(dòng)起來(lái),并且碰到顯示器邊框以后就反彈

上面已經(jīng)知道修改圖片的位置的方法以后,我們可以把圖片的行和列的偏移量設(shè)置成一個(gè)reg變量,然后在每一幀結(jié)束以后修改這個(gè)偏移量就可以了,而場(chǎng)脈沖的下降沿可以看做每一幀結(jié)束的標(biāo)志位,所以為了實(shí)現(xiàn)這個(gè)功能需要寫一個(gè)檢測(cè)場(chǎng)脈沖下降沿的邏輯。

至于碰到屏幕以后產(chǎn)生“反彈”的效果實(shí)際上就是一個(gè)狀態(tài)機(jī),這個(gè)狀態(tài)機(jī)一共有四個(gè)狀態(tài):

狀態(tài)2’b00:圖片向右下方移動(dòng)

狀態(tài)2’b01:圖片向右上方移動(dòng)

狀態(tài)2’b10:圖片向左下方移動(dòng)

狀態(tài)2’b11:圖片向左上方移動(dòng)

這個(gè)狀態(tài)機(jī)很容易就抽象出來(lái)了,但關(guān)鍵是狀態(tài)的跳變一定要理清楚了,下面這個(gè)狀態(tài)機(jī)的狀態(tài)跳變圖

上面這個(gè)狀態(tài)轉(zhuǎn)換圖清晰的描述了圖片在屏幕上運(yùn)動(dòng)的狀態(tài)切換圖,其中每次狀態(tài)切換都是在場(chǎng)脈沖的下降沿觸發(fā),這樣才能保證圖片在運(yùn)動(dòng)過(guò)程中不會(huì)閃爍。

完整的代碼如下:

module vga_driver(    input                   I_clk   , // 系統(tǒng)50MHz時(shí)鐘    input                   I_rst_n , // 系統(tǒng)復(fù)位    output   reg   [4:0]    O_red   , // VGA紅色分量    output   reg   [5:0]    O_green , // VGA綠色分量    output   reg   [4:0]    O_blue  , // VGA藍(lán)色分量    output                  O_hs    , // VGA行同步信號(hào)    output                  O_vs      // VGA場(chǎng)同步信號(hào));// 分辨率為640*480時(shí)行時(shí)序各個(gè)參數(shù)定義parameter       C_H_SYNC_PULSE      =   96      ,                C_H_BACK_PORCH      =   48      ,                C_H_ACTIVE_TIME     =   640     ,                C_H_FRONT_PORCH     =   16      ,                C_H_LINE_PERIOD     =   800     ;                                                // 分辨率為640*480時(shí)場(chǎng)時(shí)序各個(gè)參數(shù)定義parameter       C_V_SYNC_PULSE      =   2       ,                C_V_BACK_PORCH      =   33      ,                C_V_ACTIVE_TIME     =   480     ,                C_V_FRONT_PORCH     =   10      ,                C_V_FRAME_PERIOD    =   525     ;                                                parameter       C_IMAGE_WIDTH       =   128     ,                C_IMAGE_HEIGHT      =   128     ,                C_IMAGE_PIX_NUM     =   16384   ;                reg     [11:0]      R_h_cnt         ; // 行時(shí)序計(jì)數(shù)器reg     [11:0]      R_v_cnt         ; // 列時(shí)序計(jì)數(shù)器reg                 R_clk_25M       ;reg     [13:0]      R_rom_addr      ; // ROM的地址wire    [15:0]      W_rom_data      ; // ROM中存儲(chǔ)的數(shù)據(jù)reg     [11:0]      R_h_pos         ; // 圖片在屏幕上顯示的水平位置,當(dāng)它為0時(shí),圖片貼緊屏幕的左邊沿reg     [11:0]      R_v_pos         ; // 圖片在屏幕上顯示的垂直位置,當(dāng)它為0時(shí),圖片貼緊屏幕的上邊沿reg                 R_vs_reg1       ;reg                 R_vs_reg2       ;wire                W_vs_neg        ; // 場(chǎng)脈沖下降沿標(biāo)志reg     [1:0]       R_state         ;wire                W_active_flag   ; // 激活標(biāo)志,當(dāng)這個(gè)信號(hào)為1時(shí)RGB的數(shù)據(jù)可以顯示在屏幕上////////////////////////////////////////////////////////////////////功能:產(chǎn)生25MHz的像素時(shí)鐘//////////////////////////////////////////////////////////////////always @(posedge I_clk or negedge I_rst_n)begin    if(!I_rst_n)        R_clk_25M   <=  1"b0        ;    else        R_clk_25M   <=  ~R_clk_25M  ;end////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 功能:產(chǎn)生行時(shí)序//////////////////////////////////////////////////////////////////always @(posedge R_clk_25M or negedge I_rst_n)begin    if(!I_rst_n)        R_h_cnt <=  12"d0   ;    else if(R_h_cnt == C_H_LINE_PERIOD - 1"b1)        R_h_cnt <=  12"d0   ;    else        R_h_cnt <=  R_h_cnt + 1"b1  ;end                assign O_hs =   (R_h_cnt < C_H_SYNC_PULSE) ? 1"b0 : 1"b1    ;////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 功能:產(chǎn)生場(chǎng)時(shí)序//////////////////////////////////////////////////////////////////always @(posedge R_clk_25M or negedge I_rst_n)begin    if(!I_rst_n)        R_v_cnt <=  12"d0   ;    else if(R_v_cnt == C_V_FRAME_PERIOD - 1"b1)        R_v_cnt <=  12"d0   ;    else if(R_h_cnt == C_H_LINE_PERIOD - 1"b1)        R_v_cnt <=  R_v_cnt + 1"b1  ;    else        R_v_cnt <=  R_v_cnt ;end                assign O_vs =   (R_v_cnt < C_V_SYNC_PULSE) ? 1"b0 : 1"b1    ;//////////////////////////////////////////////////////////////////  assign W_active_flag =  (R_h_cnt >= (C_H_SYNC_PULSE + C_H_BACK_PORCH                      ))  &&                        (R_h_cnt <= (C_H_SYNC_PULSE + C_H_BACK_PORCH + C_H_ACTIVE_TIME     ))  &&                         (R_v_cnt >= (C_V_SYNC_PULSE + C_V_BACK_PORCH                      ))  &&                        (R_v_cnt <= (C_V_SYNC_PULSE + C_V_BACK_PORCH + C_V_ACTIVE_TIME     ))  ;//////////////////////////////////////////////////////////////////// 功能:把ROM中的圖片數(shù)據(jù)顯示到屏幕上//////////////////////////////////////////////////////////////////always @(posedge R_clk_25M or negedge I_rst_n)begin    if(!I_rst_n)        R_rom_addr  <=  14"d0 ;    else if(W_active_flag)        begin            if(R_h_cnt >= (C_H_SYNC_PULSE + C_H_BACK_PORCH + R_h_pos                        )  &&                R_h_cnt <= (C_H_SYNC_PULSE + C_H_BACK_PORCH + R_h_pos + C_IMAGE_WIDTH  - 1"b1)  &&               R_v_cnt >= (C_V_SYNC_PULSE + C_V_BACK_PORCH + R_v_pos                        )  &&                R_v_cnt <= (C_V_SYNC_PULSE + C_V_BACK_PORCH + R_v_pos + C_IMAGE_HEIGHT - 1"b1)  )                begin                    O_red       <= W_rom_data[15:11]    ;                    O_green     <= W_rom_data[10:5]     ;                    O_blue      <= W_rom_data[4:0]      ;                    if(R_rom_addr == C_IMAGE_PIX_NUM - 1"b1)                        R_rom_addr  <=  14"d0 ;                    else                        R_rom_addr  <=  R_rom_addr  +  1"b1 ;                end            else                begin                    O_red       <=  5"d0        ;                    O_green     <=  6"d0        ;                    O_blue      <=  5"d0        ;                    R_rom_addr  <=  R_rom_addr  ;                end                                  end    else        begin            O_red       <=  5"d0        ;            O_green     <=  6"d0        ;            O_blue      <=  5"d0        ;            R_rom_addr  <=  R_rom_addr  ;        end          end//////////////////////////////////////////////////////////////////// 功能:產(chǎn)生場(chǎng)脈沖的下降沿標(biāo)志,在這個(gè)標(biāo)志用來(lái)修改R_h_pos和R_v_pos//       兩個(gè)參數(shù),從而改變圖片的位置//////////////////////////////////////////////////////////////////always @(posedge R_clk_25M or negedge I_rst_n)begin    if(!I_rst_n)        begin            R_vs_reg1   <=  1"b0        ;            R_vs_reg2   <=  1"b0        ;        end    else        begin            R_vs_reg1   <=  O_vs        ;            R_vs_reg2   <=  R_vs_reg1   ;        end         endassign W_vs_neg = ~R_vs_reg1 & R_vs_reg2 ;//////////////////////////////////////////////////////////////////// 功能:使圖片移動(dòng)的狀態(tài)機(jī)//////////////////////////////////////////////////////////////////always@(posedge R_clk_25M or negedge I_rst_n)begin    if(!I_rst_n)        begin            R_h_pos <=  12"d0   ;            R_v_pos <=  12"d0   ;            R_state <=  2"b00   ;        end    else if(W_vs_neg)        begin             case(R_state)                2"b00: // 圖片往右下方移動(dòng)                    begin                         R_h_pos     <=  R_h_pos + 1 ;                        R_v_pos     <=  R_v_pos + 1 ;                        if(R_h_pos + C_IMAGE_WIDTH == C_H_ACTIVE_TIME) // 如果碰到右邊框                            R_state <=  2"b10       ;                        else if((R_v_pos + C_IMAGE_HEIGHT) == C_V_ACTIVE_TIME) // 如果碰到下邊框                            R_state <=  2"b01       ;                    end                2"b01: // 圖片往右上方移動(dòng)                    begin                         R_h_pos     <=  R_h_pos + 1 ;                        R_v_pos     <=  R_v_pos - 1 ;                        if(R_h_pos + C_IMAGE_WIDTH == C_H_ACTIVE_TIME) // 如果碰到右邊框                            R_state <=  2"b11       ;                        else if(R_v_pos == 1)     // 如果碰到上邊框                            R_state <=  2"b00       ;                    end                2"b10: // 圖片往左下方移動(dòng)                    begin                         R_h_pos     <=  R_h_pos - 1 ;                        R_v_pos     <=  R_v_pos + 1 ;                        if(R_h_pos == 1)    // 如果碰到左邊框                            R_state <=  2"b00       ;                        else if(R_v_pos + C_IMAGE_HEIGHT == C_V_ACTIVE_TIME) // 如果碰到下邊框                            R_state <=  2"b11       ;                    end                2"b11: // 圖片往左上方移動(dòng)                    begin                         R_h_pos     <=  R_h_pos - 1 ;                        R_v_pos     <=  R_v_pos - 1 ;                        if(R_h_pos == 1)    // 如果碰到上邊框                            R_state <=  2"b01       ;                        else if(R_v_pos == 1) // 如果碰到左邊框                            R_state <=  2"b10       ;                    end                default:R_state <=  2"b00           ;            endcase               endend      rom_image U_rom_image (  .clka(R_clk_25M), // input clka  .addra(R_rom_addr), // input [13 : 0] addra  .douta(W_rom_data) // output [15 : 0] douta);endmodule

代碼里面碰到左邊框和碰到上邊框的判決方式為if(R_h_pos == 1)和if(R_h_pos == 1),我之所以設(shè)置為1而不設(shè)置為0是因?yàn)槲野l(fā)現(xiàn)設(shè)置為0的時(shí)候當(dāng)圖片碰到左邊框和上邊框會(huì)閃爍一下,具體原因大家自己分析一下。

六、 總結(jié)

VGA的時(shí)序是非常適合初學(xué)者入門的,相對(duì)于其他接口時(shí)序來(lái)說(shuō),VGA時(shí)序確實(shí)是最簡(jiǎn)單的,所以初學(xué)者最好能自己把代碼從頭到尾敲一遍,然后用ModelSim仿一下,看一看中間變量的波形以加深對(duì)VGA時(shí)序的理解。

編輯:黃飛

標(biāo)簽:

上一篇:基于SVPWM以及實(shí)際MCU定時(shí)器輸出脈沖的中心對(duì)齊模式
下一篇:最后一頁(yè)