zoukankan      html  css  js  c++  java
  • 【黑金原创教程】【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 比较保险,因为意外从总是突如其来。

  • 相关阅读:
    Java中对话框的弹出
    数据格式化和ModelAttribute注解的介绍
    SpringMVC的环境搭建
    Js基础之数组
    Js基础之函数
    POJ-3208 Apocalypse Someday (数位DP)
    HDU-6148 Valley Number (数位DP)
    [SCOI2009] [BZOJ1026] windy数
    POJ-2411 Mondriann's Dream (状压DP)
    「SCOI2005」互不侵犯 (状压DP)
  • 原文地址:https://www.cnblogs.com/alinx/p/4567323.html
Copyright © 2011-2022 走看看