【黑金原創教程】【FPGA那些事兒-驅動篇I 】實驗二十七:TFT模塊 - 顯示


實驗二十七:TFT模塊 - 顯示

所謂TFT(Thin Film Transistor)就是眾多LCD當中,其中一種支持顏色的LCD,相較古老的點陣LCD(12864笑),它可謂高級了。黑金的TFT LCD除了320×240大小以外,內置SSD1289控制器,同時也是獨立模塊。事實上,無論是驅動點陣LCD還是TFT LCD,結果都是配置里邊的控制器,差別就在於控制器的復雜程度而已。不管怎么樣,如果只是單純地顯示內容,SSD1289控制器也不會難道那里去。

未進入主題之前,請容許筆者補足一下循環操作的內容。首先筆者必須強調,循環操作與循環操作模式本身是不同性質的東西,操作模式是為了優化操作才存在這個世界上,例如空間換時鍾,或者時鍾換空間等優化傾向。反之,循環操作類似關鍵字for,while,do ... while等代碼意義上的重復運行。

筆者曾在實驗二十一說過,順序語言for,while,do ... while等的鍵字,它們的作用主要是簡化代碼的重復性。不過很遺憾的是,這些關鍵字並不怎么喜歡描述語言,所以循環操作一直是描述語言的痛。對此,筆者才慫恿描述語言,先模仿順序操作,再模仿循環操作。這種操縱它人的背德感,筆者無比滿足與喜悅 ... 嘻哈嘻哈(興奮聲)。

相較先判斷后執行,筆者比較傾向先執行后判斷,結果 do ... while 是描述語言最佳的模仿對象。舉例而言,如代碼27.1所示:

// C 語言 do ... while循環
do { FuncA(); i++; } while( i < 4  )
   
i 為 0, 執行函數A,i遞增為1,i小於4,如是繼續;
i 為1, 執行函數A,i遞增為2,i小於4,如是繼續;
i 為2, 執行函數A,i遞增為3,i小於4,如是繼續;
i 為3, 執行函數A,i遞增為4,i小於4,不是則結束操作。

代碼27.1

如代碼27.1所示, C語言使用 do ... while 重復執行函數A,其中i控制循環,i < 4為執行條件。i為0~3期間,總共執行4次函數A。至於 Verilog 可以這樣模仿代碼27.1,結果如代碼27.2所示:

// Verilog 語言
case( i )
 
    0:
    if(DoneA)begin isStart <= 1’b0; i <= i + 1’b1; end
    else begin isStart <= 1’b1; end
 
    1:
    if( C1 == 4 -1) begin C1 <= 8’d0; i <= i + 1’b1; end
else begin C1 <= C1 + 1’b1; i <= 4’d0; end
 
步驟0,i為0,執行功能A; 步驟1,i為0,條件不成立,i遞增為1,返回步驟;
步驟0,i為1,執行功能A; 步驟1,i為1,條件不成立,i遞增為2,返回步驟;
步驟0,i為2,執行功能A; 步驟1,i為2,條件不成立,i遞增為3,返回步驟;
步驟0,i為3,執行功能A; 步驟1,i為3,條件成立,i清零,繼續步驟;

代碼27.2

如代碼27.2所示,那是Verilog模仿do .. while循環的結果。代碼27.1與代碼27.2之間最大的差別就是后者(描述語言)必須自行建立順序結構,執行建立循環操作。雖說,描述語言什么都要自己動手,非常勞動。不過,只要模仿起來似模似樣,描述語言也有循環利器。為了磨尖這把利器,筆者需要改動一下代碼27.2,結果如代碼27.3所示:

// Verilog 語言
case( i )
 
    0:
    if(DoneA)begin isStart <= 1’b0; Go <= i; i <= i + 1’b1; end
    else begin isStart <= 1’b1; end
 
    1:
    if( C1 == 4 -1) begin C1 <= 8’d0; i <= i + 1’b1; end
else begin C1 <= C1 + 1’b1; i <= Go; end

代碼27.3

如代碼27.3所示,筆者為循環加入返回作用的Go。步驟0之際,功能A執行完畢,Go便會暫存當前步驟,然后i繼續步驟。步驟1之際,如果if條件不成立,C1遞增,i返回Go指向的地方。反之,如果if條件成立,C1會清零,然后i繼續步驟。好奇的朋友一定會覺得疑惑,這樣作究竟有什么好處?

嘻嘻!好處可多了 ... 筆者這樣做除了讓代碼變漂亮一些以外,我們還能實現夢寐以求的嵌套循環,並且不失代碼的表達能力,舉例而言,如代碼27.4:

// C語言, 2層嵌套for
for( C2 = 0;C2 < 8;C2++ )
    for( C1 = 0;C1 < 4;C1 ++ )
        FuncA();   

代碼27.4

如代碼27.4所示,哪里有一組嵌套for,C1控制函數A執行4次,C2則控制C1執行8次,結果函數A一共執行32次。一般而言,描述語言是很難實現這種嵌套for,即使僥幸成功,我們也得付出巨大的代價。如今仿順序操作在后面撐腰,慘痛已經成為過去式的悲劇 ... 廢話不多說,讓我們瞧瞧低級建模II的力量吧!少年少女們!

// Verilog 語言
case( i )
 
    0:
    if(DoneA)begin isStart <= 1’b0; Go <= i; i <= i + 1’b1; end
    else begin isStart <= 1’b1; end
 
    1:
    if( C1 == 4 -1) begin C1 <= 8’d0; i <= i + 1’b1; end
else begin C1 <= C1 + 1’b1; i <= Go; end
 
    2:
    if( C2 == 8 -1) begin C2 <= 8’d0; i <= i + 1’b1; end
else begin C2 <= C2 + 1’b1; i <= Go; end

代碼27.5

如代碼27.5所示,我們只要在步驟1的下面再添加一段代碼即可,其中C1控制步驟0執行4次,步驟2則控制步驟1執行8次。操作期間,如果C1不滿足便會返回步驟0,反之繼續步驟;如果C2不滿足也會返回步驟0,反之繼續。步驟之間不停來回跳轉,如此一來,功能A總共執行32次。

即使對象是3層for嵌套,我們照搬無誤,如代碼27.6所示:

// C語言, 3層嵌套for
for( C3 = 0;C3 < 8;C3++ )
    for( C2 = 0;C2 < 8;C2++ )
        for( C1 = 0;C1 < 4;C1 ++ )
            FuncA();

// Verilog 語言
case( i )

    0:
    if(DoneA)begin isStart <= 1’b0; Go <= i; i <= i + 1’b1; end
    else begin isStart <= 1’b1; end

    1:
    if( C1 == 4 -1) begin C1 <= 8’d0; i <= i + 1’b1; end
else begin C1 <= C1 + 1’b1; i <= Go; end

    2:
    if( C2 == 8 -1) begin C2 <= 8’d0; i <= i + 1’b1; end
else begin C2 <= C2 + 1’b1; i <= Go; end

    3:
    if( C3 == 10 -1) begin C3 <= 8’d0; i <= i + 1’b1; end
else begin C3 <= C3 + 1’b1; i <= Go; end

代碼27.6

如代碼27.6所示,C語言一共擁有3層for嵌套,反之Verilog只要再添加步驟3即可。期間,C3控制C2執行10次,C2控制C1執行8次,C1則控制功能A執行4次 ... 如此一來,功能A一共執行320次。如果一鼓作氣下去的話,不管循環for有多少層也勢如破竹。讀者是不是覺得很神奇呢?然而,最神奇的地方是,步驟依然從上之下解讀。

// C語言, 3層嵌套for
for( C3 = 0;C3 < 8;C3++ )
for( C2 = 0;C2 < 8;C2++ )
    {
            for( C1 = 0; C1 < 4; C1 ++ ) FuncA();
            FuncB();
        }

代碼27.7

假設筆者稍微頑皮一點,讓C2控制C1以外,也讓它控制函數B。對此,Verilog又該怎樣描述呢?

// Verilog 語言
case( i )
 
    0:
    if(DoneA)begin isStart[1] <= 1’b0; Go <= i; i <= i + 1’b1; end
    else begin isStart[1] <= 1’b1; end
 
    1:
    if( C1 == 4 -1) begin C1 <= 8’d0; i <= i + 1’b1; end
else begin C1 <= C1 + 1’b1; i <= Go; end
 
2:
if( DoneB ) begin isStart[0] <= 1’b0; i <= i + 1’b1; end
else begin isStart[0] <= 1’b1; end 
 
    3:
    if( C2 == 8 -1) begin C2 <= 8’d0; i <= i + 1’b1; end
else begin C2 <= C2 + 1’b1; i <= Go; end
 
    4:
    if( C3 == 10 -1) begin C3 <= 8’d0; i <= i + 1’b1; end
else begin C3 <= C3 + 1’b1; i <= Go; end

代碼27.8

如代碼27.8所示,我們只要在C2~C1之間再插入一段步驟即可。期間,步驟2先執行功能B,然后再進入步驟3。如此一來,功能A一共執行320次,然而功能B一共執行80次。好奇的同學一定會覺得奇怪,怎么說說TFT LCD忽然插入循環操作的話題呢?原因很單純,因為繪圖功能必須借用循環操作才行。因此,筆者事先為讀者洗白白了 ... 好了,理解完畢以后,我們便可以進入主題了?

clip_image002

圖27.1 TFT連線FPGA。

一般而言,如果TFT LCD 只是單純地顯示圖像,然后FPGA也是單純地寫入地址或者寫入圖像信息 ... 對此,它們之間所需的連線並不多。如圖27.1所示,RST信號用來復位TFT LCD,RS信號用來分辨寫入數據是命令還是圖像信息,CS是使能信號,WR是寫入有效信號,RD是讀出有效信號,DB則是數據信號。此外,為了簡化設計,FPGA只需負責寫數據而已,所以連線箭頭往左一面倒。

clip_image004

圖27.2 控制器SSD1289的寫時序。

圖27.1是控制器SSD1289的寫命令還有寫數據的時序圖,左圖是寫命令,右圖則是寫數據。寫命令與寫數據的區別就在乎 RS信號拉低還是拉高而已,寫命令拉低 RS,寫數據則拉高RS。如果我們不打算復位控制器,RST信號可以常年拉高。此外,筆者也說過FPGA只寫不讀,所以WR信號常年拉低,RD信號則是常年拉高。余下只有CS信號還有DB信號而已。

在這里,CS信號充當時鍾信號,因為CS信號由低變高所產生的上升沿會使 DB信號的內容打入控制器里面。為使上升沿穩如泰山,我們必須滿足TCSL與TCSH這兩只時間要求。根據手冊,TCSL最少為50ns,TCSH最少則是500ns,因此CS最小周期需要550ns,或者說該控制器擁有速率1.818181 Mhz。此外,TDSW與TDHW都是常見的TSETUP與THOLD,手冊顯示只有5ns而已,所以我們可以無視。

parameter TCSL = 3, TCSH = 25; // tCSL = 50ns, tCSH = 500ns;

代碼27.9

1.     case( i )
2.                        
3.            0:
4.             if( C1 == TCSL -1 ) begin C1 <= 5'd0; i <= i + 1'b1; end
5.            else begin { rRS,rCS } <= 2'b00; D1 <= { 8'd0, iAddr }; C1 <= C1 + 1'b1; end
6.                         
7.            1:
8.             if( C1 == TCSH -1 ) begin C1 <= 5'd0; i <= i + 1'b1; end
9.            else begin { rRS,rCS } <= 2'b01; C1 <= C1 + 1'b1; end

代碼27.10

如代碼27.10所示,那是寫命令操作。CS在閑置狀態下都處於拉高狀態,步驟0拉低RS與CS,還有賦值命令(地址),然后等待 TCSL。步驟2拉高CS之余,它也等待TCSH。

1.    case( i )
2.                        
3.        0:
4.         if( C1 == TCSL -1 ) begin C1 <= 5'd0; i <= i + 1'b1; end
5.        else begin { rRS,rCS } <= 2'b10; D1 <= iData; C1 <= C1 + 1'b1; end
6.                         
7.        1:
8.         if( C1 == TCSH -1 ) begin C1 <= 5'd0; i <= i + 1'b1; end
9.        else begin { rRS,rCS } <= 2'b11; C1 <= C1 + 1'b1; end

代碼27.11

如代碼27.11所示,那是寫數據。默認下,CS處於高電平。步驟0,拉高RS之余也拉低CS,准備寫命令之后,便等待 TCSL。步驟1,拉高RS之余也拉高CS,然后等待TCSH。基本上,控制器SSD1289的寫時序就是那么簡單而已,接下來就是該控制器的配置信息。

控制器SSD1289內置超過50個配置寄存器,如果逐個解釋,筆者一定會捏爆自己的蛋蛋 ... 對此,筆者僅對看懂又重要的寄存器解釋而已。

clip_image006

圖27.3 Oscillator配置寄存器。

如圖27.3所示,那是Oscillator配置寄存器。它可謂是最簡單的寄存器,IB0為1,內部的晶振就開始鼓動,反之亦然。

clip_image008

圖27.4 Driver Output Control 配置寄存器。

如圖27.4所示,那是 Driver Output Control 配置寄存器。MUX0~8用來設置TFT的顯示高度(Vertical),最大為319(從0開始計算)或者0x13F。BGR用來設置顏色的排序,BGR為1,顏色排序為 <R><G><B>,為0則是 <B><G><R>。

clip_image010

圖27.5 Sleep Mode 配置寄存器。

如圖27.5所示,那是Sleep Mode 配置寄存器,其中IBO為1表示控制器在睡覺。我們只要將其設置為0,該控制器就會起床。

clip_image012

圖27.6 Entry Mode 配置寄存器

圖27.6所示是 Entry Mode 配置寄存器,它可謂是重量級的配置寄存器之一。DFM表示色彩的分辨率, DFM為2’b11 就是16位RGB(65k顏色),DFM為2’b10就是18位RGB(262k)。如果選擇16位RGB,我們可以無視 TY。DMode為2’b00,表示顯示ram里邊的圖像信息。

clip_image014

圖27.7 掃描次序。

再者就是 ID與AM了,根據配置內容不同,控制器也會產生不同的掃描次序,結果如圖27.7所示。筆者選擇先左至右,后上至下的掃描次序,目的是為了迎合 VGA的掃描次序,所以AM設置為0,ID則是 2’b11。

clip_image016

圖27.8 Horizontal Porch 配置寄存器

圖27.8顯示Horizontal Porch 配置寄存器的內容,其中 XL是HSYNC的數據段長度,HBP則是起始段還有准備段的長度。讀者是否還記得 VGA 時序?我們利用 HSYNC信號還有 VSYNC信號控制VGA的顯示標准。同樣,TFT LCD 內部也使用了 VGA時序,不過驅動對象卻是控制器 SSD 1289。

clip_image018

圖27.9 TFT LCD 內部的 HSYNC 時序。

如圖27.9所示,那是TFT LCD 內部的 HSYNC 時序。其中 HBP 配置起始段還有准備段的長度,HBP默認下為30,配置信息則是 8’b1C。換之,XL用來控制數據段的長度,而且 240 即是默認值也是最大值,配置信息則是 8’hEF。

clip_image020

圖27.9 Vertical Porch 配置寄存器。

圖27.9顯示Vertical Porch 配置寄存器的內容,亦即控制內部的 VSYNC信號。VFP用來配置結束段的長度,VBP則是配置起始段還有准備段的長度。

clip_image022

圖27.10 TFT LCD 內部的CSYNC時序。

如圖27.10所示,VBP的默認長度為4個HSYNC的下降沿(起始段),結果默認值為4,配置信息則是 8’h03。換之,VFP的默認長度為1個HSYNC周期,所以默認值為1,配置信息則是8’h00。至於VSYNC的數據段則在Driver Output Control 哪里配置。

clip_image024

圖27.11 Display Control 配置寄存器。

圖27.11是Display Control 配置寄存器,而重點內容就在D1與D0。D1控制屏幕開關,值1顯示,值0關閉。D0控制控制器的活動狀態,值1干活,值0掛機。為此,屏幕正常活動的時候 D1與D0 必須設為 2’b11。

clip_image026

圖27.12 Gate Scan Position 配置寄存器。

圖27.12是Gate Scan Position 配置寄存器,其中SCN表示掃描的起始位置。

clip_image028

圖27.13 默認掃描起始位置(左),配置過后的掃描起始位置(右)。

如圖27.13所示,左圖是默認下的起始位置,右圖則是從29行開始掃描,結果文字信息與圖標信息調轉位置了。所以,SCN一般都設為0值。

clip_image030

圖27.14 1st Screen Driving Position配置寄存器。

圖27.14是 1st Screen Driving Position 配置寄存器。黑金所用的 TFT LCD,主要分為主屏幕(First Screen)還有次屏幕(Second Screen)。主屏幕一般作為主要顯示源,而且掃描也是一行一行執行,期間SS1表示掃描的開始位置,SE1則表示掃描的結束位置。默認下,SS1 為 0,配置信息則是 9’d0,SE1為319,配置信息則是 9’h13F。

clip_image032

圖27.15 Horizontal RAM Address Position配置寄存器。

圖27.15是 Horizontal RAM Address Position 配置寄存器。HSA表示有效的起始列,默認下為0,配置信息則是 8’h00。換之,HEA表示有效的結束列,默認下為239,配置信息則是 8’hEF。

clip_image034

圖27.16 Vertical RAM Address Position配置寄存器

圖27.16是 Vertical RAM Address Position 配置寄存器。VSA表示有效的起始行,默認下為0,配置信息則是 9’h000。VEA表示有效的結束行,默認下為 319,配置信息則是9’h13F。

感覺上,Horizontal RAM Address Position 好比vga_ctrlmod 的 isX,Vertical RAM Address Position 好比 isY,兩者求與后便構成 isReady。例如實驗二十六,顯示空間雖然有1024 × 768的范圍,不過筆者只用左上一角顯示 128 × 96 的圖像而已。其中,列0至列127之間為有效 X,行0至行95之間為有效Y,結果兩者構成有效的顯示區域。

clip_image036

圖27.17 RAM Write Data Mask配置寄存器。

圖27.17是 RAM Write Data Mask 配置寄存器,WMR表示紅色源的屏蔽信息,WMG表示綠色源的屏蔽信息,WMB則是藍色源的屏蔽信息。值1表示屏蔽有效,值0表示屏蔽有效。屏蔽一旦啟動,相關的顏色位便會寫入失效。其實這些家伙並沒有多大用處,筆者也是循例介紹而已。

clip_image038

圖27.18 RAM Address set配置寄存器

圖27.18是 RAM Address set 配置寄存器,XAD表示列計數器的內容,YAD則表示行計數器的內容。寫數據期間,CS每次上升沿都使 XAD遞增,直至239為止便會清零(默認下),然后遞增YAD。默認下,YAD遞增到319也會清零。每當寫數據之前,我們都會順手將它們設為0值。

clip_image040

圖27.19 Write Data to CGRAM 寄存器。

圖27.19是Write Data to CGRAM 寄存器。根據手冊,寫圖像信息之前,我們必須事先設置寫數據的目標地址 ... 然而,那個目標地址就是 Write Data to GRAM 寄存器。隨后,寫入信息再由控制器寫入內部CGRAM。基本上,有用又看懂的寄存器就是這些而已,接下來讓我們來瞧瞧如何初始化控制器SSD 1289。

因為犯懶的關系,筆者嘗試將寫命令和寫數據捆綁在一起,結果如代碼27.12所示:

1.    case( i )
2.                        
3.            0:
4.             if( C1 == TCSL -1 ) begin C1 <= 5'd0; i <= i + 1'b1; end
5.            else begin { rRS,rCS } <= 2'b00; D1 <= { 8'd0, iAddr }; C1 <= C1 + 1'b1; end
6.                         
7.            1:
8.             if( C1 == TCSH -1 ) begin C1 <= 5'd0; i <= i + 1'b1; end
9.            else begin { rRS,rCS } <= 2'b01; C1 <= C1 + 1'b1; end
10.    
11.            2:
12.             if( C1 == TCSL -1 ) begin C1 <= 5'd0; i <= i + 1'b1; end
13.            else begin { rRS,rCS } <= 2'b10; D1 <= iData; C1 <= C1 + 1'b1; end
14.                         
15.            3:
16.             if( C1 == TCSH -1 ) begin C1 <= 5'd0; i <= i + 1'b1; end
17.            else begin { rRS,rCS } <= 2'b11; C1 <= C1 + 1'b1; end

代碼27.12

如代碼27.12所示,步驟0~1是寫命令(地址),步驟2~3則是寫數據。如此一來,筆者只要調用一次該功能便能同時執行寫命令還有寫數據。

根據官方源碼,控制器SSD1289的初始化過程是很長很臭的,對此讓筆者N行,N行慢慢解釋吧。

1.    case( i )
2.                    
3.        0: // Oscillator, On
4.        if( iDone ) begin isDo[2] <= 1'b0; i <= i + 1'b1; end
5.        else begin isDo[2] <= 1'b1; D1 <= 8'h00; D2 <= 16'h0001; end

代碼27.13

假設拉高 isDo[2] 調用代碼27.12,步驟0開啟使能晶振。

1.        1: // Power control 1
2.        if( iDone ) begin isDo[2] <= 1'b0; i <= i + 1'b1; end
3.        else begin isDo[2] <= 1'b1; D1 <= 8'h03; D2 <= 16'h6664; end
4.                         
5.        2: // Power control 2
6.        if( iDone ) begin isDo[2] <= 1'b0; i <= i + 1'b1; end
7.        else begin isDo[2] <= 1'b1; D1 <= 8'h0C; D2 <= 16'h0000; end
8.                         
9.        3: // Power control 3
10.        if( iDone ) begin isDo[2] <= 1'b0; i <= i + 1'b1; end
11.        else begin isDo[2] <= 1'b1; D1 <= 8'h0D; D2 <= 16'h080C; end
12.                         
13.        4: // Power control 4
14.        if( iDone ) begin isDo[2] <= 1'b0; i <= i + 1'b1; end
15.        else begin isDo[2] <= 1'b1; D1 <= 8'h0E; D2 <= 16'h2B00; end
16.                         
17.        5: // Power control 5
18.        if( iDone ) begin isDo[2] <= 1'b0; i <= i + 1'b1; end
19.        else begin isDo[2] <= 1'b1; D1 <= 8'h1E; D2 <= 16'h00B0; end

代碼27.14

步驟1~5是相關的電源配置信息,別問筆者為什么,筆者也是照搬而已。

1.        6: // Driver output control, MUX = 319, <R><G><B>
2.        if( iDone ) begin isDo[2] <= 1'b0; i <= i + 1'b1; end
3.        else begin isDo[2] <= 1'b1; D1 <= 8'h01; D2 <= 16'h2B3F; end
4.                         
5.        7: // LCD driving waveform control
6.        if( iDone ) begin isDo[2] <= 1'b0; i <= i + 1'b1; end
7.        else begin isDo[2] <= 1'b1; D1 <= 8'h02; D2 <= 16'h0600; end
8.                         
9.        8: // Sleep mode, weak-up
10.        if( iDone ) begin isDo[2] <= 1'b0; i <= i + 1'b1; end
11.        else begin isDo[2] <= 1'b1; D1 <= 8'h10; D2 <= 16'h0000; end
12.                         
13.        9: // Entry mode, 65k color, DM = 2’b00, AM = 0, ID = 2’b11 
14.        if( iDone ) begin isDo[2] <= 1'b0; i <= i + 1'b1; end
15.        else begin isDo[2] <= 1'b1; D1 <= 8'h11; D2 <= 16'h6070; end

代碼27.15

步驟6用來配置VSYNC的數據段長度之余,也設置 RGB的排序。步驟7不清楚,步驟8喚醒控制器。步驟9用來配置 16 位RGB,還有掃描次序。

1.        10: // Compare register
2.        if( iDone ) begin isDo[2] <= 1'b0; i <= i + 1'b1; end
3.        else begin isDo[2] <= 1'b1; D1 <= 8'h05; D2 <= 16'h0000; end
4.                    
5.        11: // Compare register
6.        if( iDone ) begin isDo[2] <= 1'b0; i <= i + 1'b1; end
7.        else begin isDo[2] <= 1'b1; D1 <= 8'h06; D2 <= 16'h0000; end
8.                         
9.        12: // Horizontal porch, HBP = 30, XL = 239
10.        if( iDone ) begin isDo[2] <= 1'b0; i <= i + 1'b1; end
11.        else begin isDo[2] <= 1'b1; D1 <= 8'h16; D2 <= 16'hEF1C; end
12.                         
13.        13: // Vertical porch, VBP = 1, VBP = 4
14.        if( iDone ) begin isDo[2] <= 1'b0; i <= i + 1'b1; end
15.        else begin isDo[2] <= 1'b1; D1 <= 8'h17; D2 <= 16'h0003; end
16.                         
17.        14: // Display control, 8 color mode, display on, operation on
18.        if( iDone ) begin isDo[2] <= 1'b0; i <= i + 1'b1; end
19.        else begin isDo[2] <= 1'b1; D1 <= 8'h07; D2 <= 16'h0233; end

代碼27.16

步驟10~11 不清楚,步驟12用來配置 HSYNC,步驟13則用來配置 VSYNC。步驟14用來配置顯示器開啟,控制器進入活動。

1.        15: // Frame cycle control
2.        if( iDone ) begin isDo[2] <= 1'b0; i <= i + 1'b1; end
3.        else begin isDo[2] <= 1'b1; D1 <= 8'h0B; D2 <= 16'h0000; end
4.                         
5.        16: // Gate scan position, SCN = 0
6.        if( iDone ) begin isDo[2] <= 1'b0; i <= i + 1'b1; end
7.        else begin isDo[2] <= 1'b1; D1 <= 8'h0F; D2 <= 16'h0000; end
8.                         
9.        17: // Vertical scroll control
10.        if( iDone ) begin isDo[2] <= 1'b0; i <= i + 1'b1; end
11.        else begin isDo[2] <= 1'b1; D1 <= 8'h41; D2 <= 16'h0000; end
12.                         
13.        18: // Vertical scroll control
14.        if( iDone ) begin isDo[2] <= 1'b0; i <= i + 1'b1; end
15.        else begin isDo[2] <= 1'b1; D1 <= 8'h42; D2 <= 16'h0000; end

代碼27.16

步驟15不清楚,步驟16配置掃描的起始位置。步驟17~18好像與拖動效果有關,具體內容並不清楚,也不使用。

1.        19: // 1st screen driving position, G0~
2.        if( iDone ) begin isDo[2] <= 1'b0; i <= i + 1'b1; end
3.        else begin isDo[2] <= 1'b1; D1 <= 8'h48; D2 <= 16'h0000; end
4.                         
5.        20: // 1st screen driving position, ~G319
6.        if( iDone ) begin isDo[2] <= 1'b0; i <= i + 1'b1; end
7.        else begin isDo[2] <= 1'b1; D1 <= 8'h49; D2 <= 16'h013F; end
8.                         
9.        21: // 2nd screen driving position
10.        if( iDone ) begin isDo[2] <= 1'b0; i <= i + 1'b1; end
11.        else begin isDo[2] <= 1'b1; D1 <= 8'h4A; D2 <= 16'h0000; end
12.                         
13.        22: // 2nd screen driving position
14.        if( iDone ) begin isDo[2] <= 1'b0; i <= i + 1'b1; end
15.        else begin isDo[2] <= 1'b1; D1 <= 8'h4B; D2 <= 16'h0000; end

代碼27.17

步驟19~20用來配置主屏幕的掃描范圍,步驟21~22則是用來配置次屏幕的掃描范圍,不過只有主屏幕被使用而已。

1.        23: // Horizontal RAM address position, HSA = 0 HEA = 239
2.        if( iDone ) begin isDo[2] <= 1'b0; i <= i + 1'b1; end
3.        else begin isDo[2] <= 1'b1; D1 <= 8'h44; D2 <= 16'hEF00; end
4.                         
5.        24: // Vertical RAM address position, VSA = 0 
6.        if( iDone ) begin isDo[2] <= 1'b0; i <= i + 1'b1; end
7.        else begin isDo[2] <= 1'b1; D1 <= 8'h45; D2 <= 16'h0000; end
8.                         
9.        25: // Vertical RAM address position, VEA = 319 
10.        if( iDone ) begin isDo[2] <= 1'b0; i <= i + 1'b1; end
11.        else begin isDo[2] <= 1'b1; D1 <= 8'h46; D2 <= 16'h013F; end

代碼27.18

步驟23用來配置有效列,步驟24~25則是用來配置有效行。

1.        26: // Gamma control, PKP0~1
2.        if( iDone ) begin isDo[2] <= 1'b0; i <= i + 1'b1; end
3.        else begin isDo[2] <= 1'b1; D1 <= 8'h30; D2 <= 16'h0707; end
4.                         
5.        27: // Gamma control, PKP2~3
6.        if( iDone ) begin isDo[2] <= 1'b0; i <= i + 1'b1; end
7.        else begin isDo[2] <= 1'b1; D1 <= 8'h31; D2 <= 16'h0204; end
8.                         
9.        28: // Gamma control, PKP4~5
10.        if( iDone ) begin isDo[2] <= 1'b0; i <= i + 1'b1; end
11.        else begin isDo[2] <= 1'b1; D1 <= 8'h32; D2 <= 16'h0204; end
12.                         
13.        29: // Gamma control, PRP0~1
14.        if( iDone ) begin isDo[2] <= 1'b0; i <= i + 1'b1; end
15.        else begin isDo[2] <= 1'b1; D1 <= 8'h33; D2 <= 16'h0502; end
16.                         
17.        30: // Gamma control, PKN0~1
18.        if( iDone ) begin isDo[2] <= 1'b0; i <= i + 1'b1; end
19.        else begin isDo[2] <= 1'b1; D1 <= 8'h34; D2 <= 16'h0507; end
20.                         
21.        31: // Gamma control, PKN2~3
22.        if( iDone ) begin isDo[2] <= 1'b0; i <= i + 1'b1; end
23.        else begin isDo[2] <= 1'b1; D1 <= 8'h35; D2 <= 16'h0204; end
24.                         
25.        32: // Gamma control, PKN4~5
26.        if( iDone ) begin isDo[2] <= 1'b0; i <= i + 1'b1; end
27.        else begin isDo[2] <= 1'b1; D1 <= 8'h36; D2 <= 16'h0204; end
28.                         
29.        33: // Gamma control, PRN0~1
30.        if( iDone ) begin isDo[2] <= 1'b0; i <= i + 1'b1; end
31.        else begin isDo[2] <= 1'b1; D1 <= 8'h37; D2 <= 16'h0502; end
32.                         
33.        34: // Gamma control, VRP0~1
34.        if( iDone ) begin isDo[2] <= 1'b0; i <= i + 1'b1; end
35.        else begin isDo[2] <= 1'b1; D1 <= 8'h3A; D2 <= 16'h0302; end
36.                         
37.        35: // Gamma control, VRN0~1
38.        if( iDone ) begin isDo[2] <= 1'b0; i <= i + 1'b1; end
39.        else begin isDo[2] <= 1'b1; D1 <= 8'h3B; D2 <= 16'h0302; end

代碼27.19

步驟26~35好像是用來配置屏幕的亮度或者對比度,雖然手冊有詳細的介紹與公式,不過真心看不懂,所以照搬而已。

1.        36: // RAM write data mask
2.        if( iDone ) begin isDo[2] <= 1'b0; i <= i + 1'b1; end
3.        else begin isDo[2] <= 1'b1; D1 <= 8'h23; D2 <= 16'h0000; end
4.                         
5.        37: // RAM write data mask
6.        if( iDone ) begin isDo[2] <= 1'b0; i <= i + 1'b1; end
7.        else begin isDo[2] <= 1'b1; D1 <= 8'h24; D2 <= 16'h0000; end
8.                         
9.        38: // Unknown
10.        if( iDone ) begin isDo[2] <= 1'b0; i <= i + 1'b1; end
11.        else begin isDo[2] <= 1'b1; D1 <= 8'h25; D2 <= 16'h8000; end
12.                         
13.        39: // RAM address set, horizontal(X)
14.        if( iDone ) begin isDo[2] <= 1'b0; i <= i + 1'b1; end
15.        else begin isDo[2] <= 1'b1; D1 <= 8'h4E; D2 <= 16'h0000; end
16.                         
17.        40: // RAM address set, vertical(Y)
18.        if( iDone ) begin isDo[2] <= 1'b0; i <= i + 1'b1; end
19.        else begin isDo[2] <= 1'b1; D1 <= 8'h4F; D2 <= 16'h0000; end

代碼27.20

步驟36~37用來配置顏色源的屏蔽信息,步驟38不明因為手冊沒有解釋,步驟39~40則是用來配置 X計數器與Y計數器的初值。基本上,控制器SSD1289的初始化就這樣而已。准備知識理解完畢以后,我們便可以開始建模了。

clip_image042

圖27.20 TFT基礎模塊的建模圖。

圖27.20是TFT基礎模塊的建模圖,TFT功能模塊作有三位寬的溝通信號,結果表示它有3項功能,[2]為寫命令與寫數據,[1]為寫命令,[0]為寫數據。換之,右邊則是驅動TFT LCD的頂層信號。至於TFT控制模塊除了調用功能模塊以外,左邊也有3位寬的溝通信號,其中[0]為初始化,[1]為清屏,[2]為畫圖。

tft_funcmod.v

clip_image044

圖27.21 TFT功能模塊的建模圖。

圖27.21是TFT功能模塊的建模圖,它雖然渾身布滿箭頭,不過它還是簡單易懂的好家伙。左邊的 Start/Done 有3位寬,恰好表示它有3個功能,其中[2]為寫命令再寫數據,[1]為寫命令,[0]則是寫數據。iAddr是寫入命令所需的內容,iData則是寫數據所需的內容。

1.    module tft_funcmod
2.    (
3.         input CLOCK, RESET,
4.         output TFT_RS,     // 1 = Data, 0 = Command(Register)
5.         output TFT_CS_N,  
6.         output TFT_WR_N,
7.         output TFT_RD_N,
8.         output [15:0]TFT_DB,
9.         input [2:0]iStart,  
10.         output oDone,
11.         input [7:0]iAddr,
12.         input [15:0]iData
13.    );
14.         parameter TCSL = 3, TCSH = 25; // tCSL = 50ns, tCSH = 500ns;
15.         

以上內容為相關的出入端聲明還有相關的常量聲明。

16.         reg [3:0]i;
17.         reg [4:0]C1;
18.         reg [15:0]D1;
19.         reg rRS,rCS,rWR,rRD;
20.         reg isDone;
21.         
22.        always @ ( posedge CLOCK or negedge RESET )
23.            if( !RESET )
24.                  begin
25.                         i <= 4'd0;
26.                         C1 <= 5'd0;
27.                         D1 <= 16'd0;
28.                         { rRS, rCS, rWR, rRD } <= 4'b1101;
29.                         isDone <= 1'b0;
30.                    end

以上內容為相關的寄存器聲明還有復位操作,其中第28行表示 WR 常年拉低,RD則是常年拉高。

31.              else if( iStart[2] ) // Write command and data
32.                  case( i )
33.                        
34.                         0:
35.                          if( C1 == TCSL -1 ) begin C1 <= 5'd0; i <= i + 1'b1; end
36.                         else begin { rRS,rCS } <= 2'b00; D1 <= { 8'd0, iAddr }; C1 <= C1 + 1'b1; end
37.                         
38.                         1:
39.                          if( C1 == TCSH -1 ) begin C1 <= 5'd0; i <= i + 1'b1; end
40.                         else begin { rRS,rCS } <= 2'b01; C1 <= C1 + 1'b1; end
41.                         
42.                         2:
43.                          if( C1 == TCSL -1 ) begin C1 <= 5'd0; i <= i + 1'b1; end
44.                         else begin { rRS,rCS } <= 2'b10; D1 <= iData; C1 <= C1 + 1'b1; end
45.                         
46.                         3:
47.                          if( C1 == TCSH -1 ) begin C1 <= 5'd0; i <= i + 1'b1; end
48.                         else begin { rRS,rCS } <= 2'b11; C1 <= C1 + 1'b1; end
49.                         
50.                         4:
51.                         begin isDone <= 1'b1; i <= i + 1'b1; end
52.                         
53.                         5:
54.                         begin isDone <= 1'b0; i <= 4'd0; end
55.                        
56.                    endcase

以上內容為寫命令再寫數據。

57.              else if( iStart[1] ) // Write command 
58.                  case( i )
59.                        
60.                         0:
61.                          if( C1 == TCSL -1 ) begin C1 <= 5'd0; i <= i + 1'b1; end
62.                         else begin { rRS,rCS } <= 2'b00; D1 <= { 8'd0, iAddr }; C1 <= C1 + 1'b1; end
63.                         
64.                         1:
65.                          if( C1 == TCSH -1 ) begin C1 <= 5'd0; i <= i + 1'b1; end
66.                         else begin { rRS,rCS } <= 2'b01; C1 <= C1 + 1'b1; end
67.                         
68.                         2:
69.                         begin isDone <= 1'b1; i <= i + 1'b1; end
70.                         
71.                         3:
72.                         begin isDone <= 1'b0; i <= 4'd0; end
73.                        
74.                    endcase

以上內容為寫命令。

75.             else if( iStart[0] ) // Write data
76.                  case( i )
77.                        
78.                         0:
79.                          if( C1 == TCSL -1 ) begin C1 <= 5'd0; i <= i + 1'b1; end
80.                         else begin { rRS,rCS } <= 2'b10; D1 <= iData; C1 <= C1 + 1'b1; end
81.                         
82.                         1:
83.                          if( C1 == TCSH -1 ) begin C1 <= 5'd0; i <= i + 1'b1; end
84.                         else begin { rRS,rCS } <= 2'b11; C1 <= C1 + 1'b1; end
85.                         
86.                         2:
87.                         begin isDone <= 1'b1; i <= i + 1'b1; end
88.                         
89.                         3:
90.                         begin isDone <= 1'b0; i <= 4'd0; end
91.                        
92.                    endcase
93.                    

以上內容為寫數據。

94.         assign TFT_DB = D1;
95.         assign TFT_RS = rRS;
96.         assign TFT_CS_N = rCS;
97.         assign TFT_WR_N = rWR;
98.         assign TFT_RD_N = rRD;
99.         assign oDone = isDone;
100.            
101.    endmodule

以上內容為相關的輸出驅動聲明。

tft_ctrlmod.v

clip_image046

圖27.22 TFT控制模塊的建模圖。

圖27.22是TFT控制模塊的建模圖,它外表雖然簡單,內容卻很啰嗦 ... 具體情況讓我們來看代碼吧。

1.    module tft_ctrlmod
2.    (
3.         input CLOCK, RESET,
4.         input [2:0]iStart,
5.         output oDone,
6.         output [2:0]oStart,
7.         input iDone,
8.         output [7:0]oAddr,
9.         output [15:0]oData
10.    );

以上內容為相關的出入端聲明。

11.        reg [5:0]i,Go;
12.         reg [7:0]D1;   // Command(Register)
13.         reg [15:0]D2;  // Data
14.         reg [16:0]C1;
15.         reg [7:0]C2,C3;
16.         reg [2:0]isDo;
17.         reg isDone;
18.         
19.         always @ ( posedge CLOCK or negedge RESET )
20.             if( !RESET )
21.                  begin 
22.                        { i,Go } <= { 6'd0,6'd0 };
23.                         D1 <= 8'd0;
24.                         D2 <= 16'd0;
25.                         C1 <= 17'd0;
26.                         { C2,C3 } <= { 8'd0,8'd0 };
27.                         isDo <= 3'd0;
28.                         isDone <= 1'b0;
29.                    end

以上內容為相關的寄存器聲明還有復位操作。其中第16行的isDo作用類似isStart,D1暫存地址(命令),D2暫存讀寫數據,C1~C3則是用來控制循環。

30.              else if( iStart[2] )  // White blank page
31.                  case( i )
32.                    
33.                         0: // X0
34.                         if( iDone ) begin isDo[2] <= 1'b0; i <= i + 1'b1; end
35.                         else begin isDo[2] <= 1'b1; D1 <= 8'h4E; D2 <= 16'h0000; end
36.                         
37.                         1: // Y0
38.                         if( iDone ) begin isDo[2] <= 1'b0; i <= i + 1'b1; end
39.                         else begin isDo[2] <= 1'b1; D1 <= 8'h4F; D2 <= 16'h0000; end
40.                         
41.                         2: // Write data to ram 0x22
42.                         if( iDone ) begin isDo[1] <= 1'b0; i <= i + 1'b1; end
43.                         else begin isDo[1] <= 1'b1; D1 <= 8'h22; end
44.                         
45.                         /**********/
46.                         
47.                         3: // Write color
48.                         if( iDone ) begin isDo[0] <= 1'b0; i <= i + 1'b1; Go <= i; end
49.                         else begin isDo[0] <= 1'b1; D2 <= { C3[4:0],6'd0,5'd0}  ; end
50.                         
51.                         4: // Loop 1, rectangle width
52.                         if( C1 == 240 -1 ) begin C1 <= 17'd0; i <= i + 1'b1; end
53.                         else begin C1 <= C1 + 1'b1; i <= Go; end
54.                          
55.                         5: // Loop 2, rectangle high
56.                         if( C2 == 10 -1 ) begin C2 <= 8'd0; i <= i + 1'b1; end
57.                         else begin C2 <= C2 + 1'b1; i <= Go; end
58.                         
59.                         6: // Loop 3, color and times
60.                         if( C3 == 32 -1 ) begin C3 <= 8'd0; i <= i + 1'b1; end
61.                         else begin C3 <= C3 + 1'b1; i <= Go; end
62.                         
63.                         /**********/
64.                         
65.                         7:
66.                         begin isDone <= 1'b1; i <= i + 1'b1; end
67.                         
68.                         8:
69.                         begin isDone <= 1'b0; i <= 6'd0; end
70.                         
71.                    endcase

以上內容為繪圖功能。步驟0配置寫入地址X為0,步驟1配置寫入地址Y為0,步驟2配置寫數據的目標地址。步驟3則是寫數據,其中 { C3[4:0], 6’d0, 5’d0 },紅色信息取自 C3,綠色為0,藍色為0。步驟4,C1控制循環240次,主要是寫完1行240列。

步驟5,C2用來控制C1,主要是控制矩形的高度為10。步驟6,C3用來控制C2之余,它也用來控制矩形的數量還有顏色漸變。

for( C3 = 0; C3 < 32; C3++ )
    for( C2 = 0; C2 < 10; C2++ )
        for( C1 = 0; C1 <240; C1++ )
             Write_Data( C3,0,0 );  // <R><G><B>

代碼27.20

簡單來說,操作過程如代碼27.20所示。

72.            else if( iStart[1] )  // White blank page
73.                  case( i )
74.                    
75.                        0: // X0
76.                         if( iDone ) begin isDo[2] <= 1'b0; i <= i + 1'b1; end
77.                         else begin isDo[2] <= 1'b1; D1 <= 8'h4E; D2 <= 16'h0000; end
78.                         
79.                         1: // Y0
80.                         if( iDone ) begin isDo[2] <= 1'b0; i <= i + 1'b1; end
81.                         else begin isDo[2] <= 1'b1; D1 <= 8'h4F; D2 <= 16'h0000; end
82.                         
83.                         2: // Write data to ram 0x22
84.                         if( iDone ) begin isDo[1] <= 1'b0; i <= i + 1'b1; end
85.                         else begin isDo[1] <= 1'b1; D1 <= 8'h22; end
86.                         
87.                         /**********/
88.                         
89.                         3: // Write white color 0~768000
90.                         if( iDone ) begin isDo[0] <= 1'b0; i <= i + 1'b1; Go <= i; end
91.                         else begin isDo[0] <= 1'b1; D2 <= 16'hFFFF ; end
92.                         
93.                         4:
94.                         if( C1 == 76800 -1 ) begin C1 <= 17'd0; i <= i + 1'b1; end
95.                         else begin C1 <= C1 + 1'b1; i <= Go; end
96.                         
97.                         5:
98.                         begin isDone <= 1'b1; i <= i + 1'b1; end
99.                         
100.                         6:
101.                         begin isDone <= 1'b0; i <= 6'd0; end
102.                         
103.                    endcase

以上內容為清屏功能。步驟0配置寫入地址X,步驟1則是配置寫入地址Y,步驟2配置寫數據的目的地址。步驟3將白色的圖像信息寫入CGRAM,步驟4則是控制步驟3執行76800次(0~76799),因為 320 × 240 等價 76800,完后便能達成清屏功能。

104.              else if( iStart[0] )  // Initial TFT
105.                  case( i )
106.                    
107.                        0: // Oscillator, On
108.                         if( iDone ) begin isDo[2] <= 1'b0; i <= i + 1'b1; end
109.                         else begin isDo[2] <= 1'b1; D1 <= 8'h00; D2 <= 16'h0001; end
110.                         
111.                         1: // Power control 1
112.                         if( iDone ) begin isDo[2] <= 1'b0; i <= i + 1'b1; end
113.                         else begin isDo[2] <= 1'b1; D1 <= 8'h03; D2 <= 16'h6664; end
114.                         
115.                         2: // Power control 2
116.                         if( iDone ) begin isDo[2] <= 1'b0; i <= i + 1'b1; end
117.                         else begin isDo[2] <= 1'b1; D1 <= 8'h0C; D2 <= 16'h0000; end
118.                         
119.                         3: // Power control 3
120.                         if( iDone ) begin isDo[2] <= 1'b0; i <= i + 1'b1; end
121.                         else begin isDo[2] <= 1'b1; D1 <= 8'h0D; D2 <= 16'h080C; end
122.                         
123.                         4: // Power control 4
124.                         if( iDone ) begin isDo[2] <= 1'b0; i <= i + 1'b1; end
125.                         else begin isDo[2] <= 1'b1; D1 <= 8'h0E; D2 <= 16'h2B00; end
126.                         
127.                         5: // Power control 5
128.                         if( iDone ) begin isDo[2] <= 1'b0; i <= i + 1'b1; end
129.                         else begin isDo[2] <= 1'b1; D1 <= 8'h1E; D2 <= 16'h00B0; end
130.                         
131.                      6: // Driver output control, MUX = 319, <R><G><B>
132.                         if( iDone ) begin isDo[2] <= 1'b0; i <= i + 1'b1; end
133.                         else begin isDo[2] <= 1'b1; D1 <= 8'h01; D2 <= 16'h2B3F; end
134.                         
135.                         7: // LCD driving waveform control
136.                         if( iDone ) begin isDo[2] <= 1'b0; i <= i + 1'b1; end
137.                         else begin isDo[2] <= 1'b1; D1 <= 8'h02; D2 <= 16'h0600; end
138.                         
139.                         8: // Sleep mode, weak-up
140.                         if( iDone ) begin isDo[2] <= 1'b0; i <= i + 1'b1; end
141.                         else begin isDo[2] <= 1'b1; D1 <= 8'h10; D2 <= 16'h0000; end
142.                         
143.                         9: // Entry mode, 65k color, DM = 2'b00, AM = 0, ID = 2'b11
144.                         if( iDone ) begin isDo[2] <= 1'b0; i <= i + 1'b1; end
145.                         else begin isDo[2] <= 1'b1; D1 <= 8'h11; D2 <= 16'h6070; end
146.                         
147.                         10: // Compare register
148.                         if( iDone ) begin isDo[2] <= 1'b0; i <= i + 1'b1; end
149.                         else begin isDo[2] <= 1'b1; D1 <= 8'h05; D2 <= 16'h0000; end
150.                    
151.                        11: // Compare register
152.                        if( iDone ) begin isDo[2] <= 1'b0; i <= i + 1'b1; end
153.                         else begin isDo[2] <= 1'b1; D1 <= 8'h06; D2 <= 16'h0000; end
154.                         
155.                         12: // Horizontal porch, HBP = 30, XL = 240
156.                         if( iDone ) begin isDo[2] <= 1'b0; i <= i + 1'b1; end
157.                         else begin isDo[2] <= 1'b1; D1 <= 8'h16; D2 <= 16'hEF1C; end
158.                         
159.                         13: // Vertical porch, VBP = 1, VBP = 4
160.                        if( iDone ) begin isDo[2] <= 1'b0; i <= i + 1'b1; end
161.                         else begin isDo[2] <= 1'b1; D1 <= 8'h17; D2 <= 16'h0003; end
162.                         
163.                         14: // Display control, 8 color mode, display on, operation on
164.                         if( iDone ) begin isDo[2] <= 1'b0; i <= i + 1'b1; end
165.                         else begin isDo[2] <= 1'b1; D1 <= 8'h07; D2 <= 16'h0233; end
166.                         
167.                         15: // Frame cycle control
168.                        if( iDone ) begin isDo[2] <= 1'b0; i <= i + 1'b1; end
169.                         else begin isDo[2] <= 1'b1; D1 <= 8'h0B; D2 <= 16'h0000; end
170.                         
171.                         16: // Gate scan position, SCN = 0
172.                         if( iDone ) begin isDo[2] <= 1'b0; i <= i + 1'b1; end
173.                         else begin isDo[2] <= 1'b1; D1 <= 8'h0F; D2 <= 16'h0000; end
174.                         
175.                         17: // Vertical scroll control
176.                         if( iDone ) begin isDo[2] <= 1'b0; i <= i + 1'b1; end
177.                         else begin isDo[2] <= 1'b1; D1 <= 8'h41; D2 <= 16'h0000; end
178.                         
179.                         18: // Vertical scroll control
180.                         if( iDone ) begin isDo[2] <= 1'b0; i <= i + 1'b1; end
181.                         else begin isDo[2] <= 1'b1; D1 <= 8'h42; D2 <= 16'h0000; end
182.                         
183.                         19: // 1st screen driving position, G0~
184.                         if( iDone ) begin isDo[2] <= 1'b0; i <= i + 1'b1; end
185.                         else begin isDo[2] <= 1'b1; D1 <= 8'h48; D2 <= 16'h0000; end
186.                         
187.                         20: // 1st screen driving position, ~G319
188.                         if( iDone ) begin isDo[2] <= 1'b0; i <= i + 1'b1; end
189.                         else begin isDo[2] <= 1'b1; D1 <= 8'h49; D2 <= 16'h013F; end
190.                         
191.                         21: // 2nd screen driving position
192.                         if( iDone ) begin isDo[2] <= 1'b0; i <= i + 1'b1; end
193.                         else begin isDo[2] <= 1'b1; D1 <= 8'h4A; D2 <= 16'h0000; end
194.                         
195.                         22: // 2nd screen driving position
196.                         if( iDone ) begin isDo[2] <= 1'b0; i <= i + 1'b1; end
197.                         else begin isDo[2] <= 1'b1; D1 <= 8'h4B; D2 <= 16'h0000; end
198.                         
199.                         23: // Horizontal RAM address position, HSA = 0 HEA = 239
200.                         if( iDone ) begin isDo[2] <= 1'b0; i <= i + 1'b1; end
201.                         else begin isDo[2] <= 1'b1; D1 <= 8'h44; D2 <= 16'hEF00; end
202.                         
203.                         24: // Vertical RAM address position, VSA = 0 
204.                         if( iDone ) begin isDo[2] <= 1'b0; i <= i + 1'b1; end
205.                         else begin isDo[2] <= 1'b1; D1 <= 8'h45; D2 <= 16'h0000; end
206.                         
207.                         25: // Vertical RAM address position, VEA = 319 
208.                         if( iDone ) begin isDo[2] <= 1'b0; i <= i + 1'b1; end
209.                         else begin isDo[2] <= 1'b1; D1 <= 8'h46; D2 <= 16'h013F; end
210.                         
211.                         26: // Gamma control, PKP0~1
212.                         if( iDone ) begin isDo[2] <= 1'b0; i <= i + 1'b1; end
213.                         else begin isDo[2] <= 1'b1; D1 <= 8'h30; D2 <= 16'h0707; end
214.                         
215.                         27: // Gamma control, PKP2~3
216.                         if( iDone ) begin isDo[2] <= 1'b0; i <= i + 1'b1; end
217.                         else begin isDo[2] <= 1'b1; D1 <= 8'h31; D2 <= 16'h0204; end
218.                         
219.                         28: // Gamma control, PKP4~5
220.                         if( iDone ) begin isDo[2] <= 1'b0; i <= i + 1'b1; end
221.                         else begin isDo[2] <= 1'b1; D1 <= 8'h32; D2 <= 16'h0204; end
222.                         
223.                         29: // Gamma control, PRP0~1
224.                         if( iDone ) begin isDo[2] <= 1'b0; i <= i + 1'b1; end
225.                         else begin isDo[2] <= 1'b1; D1 <= 8'h33; D2 <= 16'h0502; end
226.                         
227.                         30: // Gamma control, PKN0~1
228.                         if( iDone ) begin isDo[2] <= 1'b0; i <= i + 1'b1; end
229.                         else begin isDo[2] <= 1'b1; D1 <= 8'h34; D2 <= 16'h0507; end
230.                         
231.                         31: // Gamma control, PKN2~3
232.                         if( iDone ) begin isDo[2] <= 1'b0; i <= i + 1'b1; end
233.                         else begin isDo[2] <= 1'b1; D1 <= 8'h35; D2 <= 16'h0204; end
234.                         
235.                         32: // Gamma control, PKN4~5
236.                         if( iDone ) begin isDo[2] <= 1'b0; i <= i + 1'b1; end
237.                         else begin isDo[2] <= 1'b1; D1 <= 8'h36; D2 <= 16'h0204; end
238.                         
239.                         33: // Gamma control, PRN0~1
240.                         if( iDone ) begin isDo[2] <= 1'b0; i <= i + 1'b1; end
241.                         else begin isDo[2] <= 1'b1; D1 <= 8'h37; D2 <= 16'h0502; end
242.                         
243.                         34: // Gamma control, VRP0~1
244.                         if( iDone ) begin isDo[2] <= 1'b0; i <= i + 1'b1; end
245.                         else begin isDo[2] <= 1'b1; D1 <= 8'h3A; D2 <= 16'h0302; end
246.                         
247.                         35: // Gamma control, VRN0~1
248.                         if( iDone ) begin isDo[2] <= 1'b0; i <= i + 1'b1; end
249.                         else begin isDo[2] <= 1'b1; D1 <= 8'h3B; D2 <= 16'h0302; end
250.                         
251.                         36: // RAM write data mask
252.                         if( iDone ) begin isDo[2] <= 1'b0; i <= i + 1'b1; end
253.                         else begin isDo[2] <= 1'b1; D1 <= 8'h23; D2 <= 16'h0000; end
254.                         
255.                         37: // RAM write data mask
256.                         if( iDone ) begin isDo[2] <= 1'b0; i <= i + 1'b1; end
257.                         else begin isDo[2] <= 1'b1; D1 <= 8'h24; D2 <= 16'h0000; end
258.                         
259.                         38: // Unknown
260.                         if( iDone ) begin isDo[2] <= 1'b0; i <= i + 1'b1; end
261.                         else begin isDo[2] <= 1'b1; D1 <= 8'h25; D2 <= 16'h8000; end
262.                         
263.                         39: // RAM address set, horizontal(X)
264.                         if( iDone ) begin isDo[2] <= 1'b0; i <= i + 1'b1; end
265.                         else begin isDo[2] <= 1'b1; D1 <= 8'h4E; D2 <= 16'h0000; end
266.                         
267.                         40: // RAM address set, vertical(Y)
268.                         if( iDone ) begin isDo[2] <= 1'b0; i <= i + 1'b1; end
269.                         else begin isDo[2] <= 1'b1; D1 <= 8'h4F; D2 <= 16'h0000; end
270.                         
271.                         41:
272.                         begin isDone <= 1'b1; i <= i + 1'b1; end
273.                         
274.                         42:
275.                         begin isDone <= 1'b0; i <= 6'd0; end
276.    
277.                    endcase
278.                    

以上內容為初始化過程。該控制模塊之所以變煩,原因都是這家伙在搞鬼。

279.            assign oStart = isDo;
280.            assign oDone = isDone;
281.            assign oAddr = D1;
282.            assign oData = D2;
283.                    
284.    endmodule

以上內容為相關的輸出驅動聲明。

tft_basemode.v

該組合模塊的連線部署請參考圖27.20。

1.    module tft_basemod
2.    (
3.         input CLOCK,RESET,
4.         output TFT_RST,
5.         output TFT_RS,     // 1 = Data, 0 = Command(Register)
6.         output TFT_CS_N,  
7.         output TFT_WR_N,
8.         output TFT_RD_N,
9.         output [15:0]TFT_DB,
10.         input [2:0]iStart,
11.         output oDone
12.    );
13.         wire [2:0]StartU1;
14.         wire [7:0]AddrU1;
15.         wire [15:0]DataU1;
16.    
17.        tft_ctrlmod U1
18.         (
19.              .CLOCK( CLOCK ),
20.              .RESET( RESET ),
21.              .iStart( iStart ),        // < top
22.              .oDone( oDone ),     // > top
23.              .oStart( StartU1 ),     // > U2
24.              .iDone( DoneU2 ),    // < U2
25.              .oAddr( AddrU1 ),    // > U2
26.              .oData( DataU1 )     // > U2
27.         );
28.         
29.         wire DoneU2;
30.         
31.        tft_funcmod U2
32.         (
33.              .CLOCK( CLOCK ),
34.              .RESET( RESET ),
35.              .TFT_RS( TFT_RS ),       // > top
36.              .TFT_CS_N( TFT_CS_N ),  // > top
37.              .TFT_WR_N( TFT_WR_N ),  // > top
38.              .TFT_RD_N( TFT_RD_N ),  // > top
39.              .TFT_DB( TFT_DB ),      // > top
40.              .iStart( StartU1 ),          // < U1
41.              .oDone( DoneU2 ),        // > U1
42.              .iAddr( AddrU1 ),         // < U1
43.              .iData( DataU1 )          // < U1
44.         );
45.       
46.        assign TFT_RST = 1'b1;
47.        
48.    endmodule

內容自己看着辦吧,不過還是注意一下第46行的輸出聲明,其中TFT_RST賦值為1。

tft_demov.

clip_image048

圖27.23 實驗二十七的建模圖。

圖27.23是實驗二十七的建模圖,其中TFT基礎模塊被核心操作調用,具體內容讓我們來看代碼吧。

1.    module tft_demo
2.    (
3.        input CLOCK, RESET,
4.         output TFT_RST,
5.         output TFT_RS,     
6.         output TFT_CS_N,  
7.         output TFT_WR_N,
8.         output TFT_RD_N,
9.         output [15:0]TFT_DB
10.    );

以上內容為相關的出入端聲明。

11.        wire DoneU1; 
12.         
13.        tft_basemod U1
14.         (
15.              .CLOCK( CLOCK ),
16.              .RESET( RESET ),
17.              .TFT_RST( TFT_RST ),
18.              .TFT_RS( TFT_RS ),
19.              .TFT_CS_N( TFT_CS_N ),
20.              .TFT_WR_N( TFT_WR_N ),
21.              .TFT_RD_N( TFT_RD_N ),
22.              .TFT_DB( TFT_DB ),
23.              .iStart( isDo ),
24.              .oDone( DoneU1 )
25.         );
26.         

以上內容為 TFT基礎模塊的實例化。

27.         reg [3:0]i;
28.         reg [2:0]isDo;
29.         
30.         always @ ( posedge CLOCK or negedge RESET )
31.             if( !RESET )
32.                   begin
33.                          i <= 4'd0;
34.                          isDo <= 3'd0;
35.                     end
36.              else 

以上內容為相關的寄存器聲明還有復位操作。

37.                  case( i )
38.                    
39.                         0: // Inital TFT
40.                         if( DoneU1 ) begin isDo[0] <= 1'b0; i <= i + 1'b1; end
41.                         else begin isDo[0] <= 1'b1; end
42.                         
43.                         1: // Clear Screen
44.                         if( DoneU1 ) begin isDo[1] <= 1'b0; i <= i + 1'b1; end
45.                         else begin isDo[1] <= 1'b1; end
46.                         
47.                         2: // Draw Function
48.                         if( DoneU1 ) begin isDo[2] <= 1'b0; i <= i + 1'b1; end
49.                         else begin isDo[2] <= 1'b1; end
50.                         
51.                         3:
52.                         i <= i;
53.                    
54.                    endcase
55.         
56.    endmodule

以上內容為核心操作。步驟0調用isDo[0]執行初始化,步驟1調用isDo[1]執行清屏,步驟2調用isDo[2]執行繪圖。

clip_image050

圖27.24 演示結果。

綜合完畢便下載程序,如果TFT LCD戶縣由上至下的紅色漸變,結果如圖27.24所示,表示實驗成功。事實上,圖27.24是由 32 個,高為10寬為240的矩形組成,第0個矩形接近黑色,第31個矩形則接近紅色。如此一來,便產生紅色漸變的效果。

細節一:完整的個體模塊

事實上,本實驗的TFT基礎模塊還不能稱為完整的個體,因為實驗二十七並沒有明確的設計目的,所以TFT基礎模塊也沒有具體封裝要求,這種情況好比VGA基礎模塊。TFT基礎模塊除了初始化功能還有清屏功能以外,繪圖功能則是為了演示才故意加上去而已。往后如果有需要,讀者再執行擴充吧。

細節二:提高速率

tft功能模塊曾經這樣聲明過,TCSL為50ns,量化結果為 3,還有TCSH 為 500ns,量化結果則是 25。根據手冊,寫周期 TCYCLE 最小可以去到 100ns,亦即TCSL還有TCSH皆為50ns。因此,速率也從原本的 1.818181 Mhz 飛升為 10Mhz。經過測試,筆者也沒發現什么問題。不過,膽小的筆者認為,如果沒有必要,TCSH還是設為500ns 比較保險,因為意外從總是突如其來。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM