从拿到BASYS2到现在差不多有2个月的时间了。eeboard和le printemps给了我很多的时间。但是我一直都没好好把它用起来,挺对不住论坛的 。这就是我的最后一篇心得吧,完了我会把板子第一时间寄回给论坛,有需要的朋友可以找eeboard借来玩玩。今天借了显示器,倒腾了一下板子上最后一块大的外设VGA。还是先科普一下有关VGA显示的一些常识。 ~~~~~~~~~~~~~~~~~~~~~~~~~~~分割线~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ VGA显示 VGA(Video Graphics Array)作为一种标准的显示接口得到了广泛的应用。v(3A在任何时刻都必须工作在某一显示模式之下,其显示模式分为字符显示模式和图形显示模式。而在应用中,讨论的都是图形显示模式。VGA的图形模式分为三类:CGA、EGA兼容的图形模式;标准的VGA图形模式;VGA扩展图形模式。后两种图形模式统称为VGA图形模式。文中基于标准VGA模式来实现。工业标准的VGA显示模式为:640*480*16色*60Hz。常见的彩色显示器一般都是由CRT(阴极射线管)构成,每一个像素的色彩由R(红,Red)、G(绿,Green)、B(蓝,Blue)三基色构成。显示时采用的是逐行扫描的方式。由VGA显示模块产生的水平同步信号和垂直同步信号控制阴极射线管中的电子枪产生电子束,轰击涂有荧光粉的屏幕,产生RGB三基色,于显示屏上合成一个彩色像素点。图3表示的是VGA显示模块与CRT显示器的控制框图【3J。电子束扫描一幅屏幕图像上的各个点的过程称为屏幕扫描。现在显示器都是通过光栅扫描方式来进行 file:///C:/Users/zcat/AppData/Local/Temp/msohtmlclip1/01/clip_image002.jpg file:///C:/Users/zcat/AppData/Local/Temp/msohtmlclip1/01/clip_image004.jpg 通常情况下,对 VGA 显示器进行驱动只需要 5 个信号即可,包括:行、场帧同步信号,红、绿、兰三色信号。其中行、场同步信号为数字信号,红、绿、兰三色信号为模拟信号。 三色信号的输入电压范围是 0.0V~0.7V, 采用DAC来产生此信号时,应考虑到这个问题。 VGA时序分析 显示器采用光栅扫描方式,即轰击荧光屏的电子束在CRT(阴极射线管)屏上从左到右(受水平同步信号HSYNC控制)、从上到下(受垂直同步信号VSYNC控制)做有规律的移动。光栅扫描又分逐行扫描和隔行扫描。隔行扫描指电子束在扫描时每隔一行扫一线,完成一屏后再返回来扫描剩下的线。与电视机的原理一样。隔行扫描的显示器扫描闪烁的比较厉害,会让使用者的眼睛疲劳。目前微机所用显示器几乎都是逐行扫描。逐行扫描是指扫描从屏幕左上角一点开始,从左向右逐点进行扫描,每扫描完一行,电子束回到屏幕的左边下一行的起始位置,在这期间,CRT对电子束进行消隐,每行结束时,用行同步信号进行行同步;当扫描完所有行,形成一帧时,用场同步信号进行场同步,并使扫描回到屏幕的左上方,同时进行行场消隐,开始下一帧的扫描。完成一行扫描所需时间称为水平扫描时间,其倒数称为行频率;完成一帧(整屏)扫描所需的时问称为垂直扫描时间,其倒数为垂直扫描频率,又称刷新频率,即刷新一屏的频率。常见的有60Hz、75Hz等。VGA显示器要正确显示图像关键还是如何实现VGA时序。视频电子标准协会(VESA,VideoElectronics Standards Association)对显示器时序进行了规范。VGA的标准参考显示时序如图 file:///C:/Users/zcat/AppData/Local/Temp/msohtmlclip1/01/clip_image006.jpg 图3 VGA的行时序 file:///C:/Users/zcat/AppData/Local/Temp/msohtmlclip1/01/clip_image008.jpg 图4 VGA的场时序 VGA的行时序可知:没一行都有一个负极性行同步脉冲(Sync a),是数据行的结束标志,同时也是下一行的开始标志。在同步脉冲之后为显示后沿(Back porch b),在显示时序段(Display interval c)显示器为亮的过程,RGB数据驱动一行上的每一个像素点,从而显示一行。在一行的最后为显示前沿(Frontporch d)。在显示时间段(Display interval c)之外没有图像投射到屏幕是插入消隐信号。同步脉冲(Sync a)、显示后沿(Back porch b)和显示前沿(Front porch d)都是在行消隐间隔内(Horizontal BlankingInterval),当消隐有效时,RGB信号无效,屏幕不显示数据。VGA的场时序与行时序基本一样,每一帧的负极性脉冲(Sync a)是一帧的结束标志,同时也是下一帧的开始标志。而显示数据是一帧的所有行数据。 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~分割线~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 做好了VGA的时序,那么就成功了一半。而时序可以参照上面的时序图和官方例程就可以写出。另外可以看到是“从一点到一行到一页”的显示方式。于是乎,要实现如下图形的显示只需要把整个页面分为4个大行和4个大纵,然后分别在应该的时间操作RGB三个数据线的模拟电压即可。 file:///C:/Users/zcat/AppData/Local/Temp/msohtmlclip1/01/clip_image010.jpg 在看一看原理图 file:///C:/Users/zcat/AppData/Local/Temp/msohtmlclip1/01/clip_image012.jpg 在BASYS2的板子上,分别用了3个引脚控制红色,3个引脚控制绿色,2个引脚控制蓝色。所以引脚如下设置: entity DispCtrl is Port (ck: in std_logic; -- 50MHz -- Hcnt: in std_logic_vector(9 downto0); -- horizontal counter -- Vcnt: in std_logic_vector(9 downto0); -- verical counter HS: out std_logic; -- horizontalsynchro signal VS: out std_logic; -- verical synchrosignal outRed : out std_logic_vector(2 downto 0); -- final color outGreen: out std_logic_vector(2 downto0); -- outputs outBlue : out std_logic_vector(2 downto1) ); end DispCtrl; 然后定义常量和信号量: architecture Behavioral ofDispCtrl is -- constants for Synchro module constant PAL:integer:=640; --Pixels/ActiveLine (pixels)--行图像 constant LAF:integer:=480; --Lines/ActiveFrame (lines)--场图像 constant PLD: integer:=800; --Pixel/LineDivider--行周期 constant LFD: integer:=521; --Line/FrameDivider--场周期 constant HPW:integer:=96; --Horizontalsynchro Pulse Width (pixels)--行同步头 constant HFP:integer:=16; --Horizontalsynchro Front Porch (pixels)--显示前沿 -- constant HBP:integer:=48; --Horizontal synchro Back Porch(pixels) constant VPW:integer:=2; --Vericalsynchro Pulse Width (lines)--场同步头 constant VFP:integer:=10; --Vericalsynchro Front Porch (lines) -- constant VBP:integer:=29; --Verical synchro Back Porch (lines) -- signals for VGA Demo signal Hcnt: std_logic_vector(9 downto 0); -- horizontal counter signal Vcnt: std_logic_vector(9 downto 0); -- verical counter signal intHcnt: integer range 0 to 800-1; --PLD-1 - horizontal counter signal intVcnt: integer range 0 to 521-1; -- LFD-1 - verical counter signal ck25MHz: std_logic; --ck 25MHz 做一次分频,得到需要的25MHZ,可以看出demo的分频方式非常的巧妙 begin -- divide 50MHz clock to 25MHz div2: process(ck) begin if ck'event andck = '1' then ck25MHz <= not ck25MHz; end if; end process; 接下来是操作VGA的核心—时序了!! syncro: process (ck25MHz) begin if ck25MHz'event and ck25MHz='1' then if intHcnt=PLD-1 then intHcnt<=0; if intVcnt=LFD-1 then intVcnt<=0; else intVcnt<=intVcnt+1; end if; else intHcnt<=intHcnt+1; end if; --Generates HS - active low ifintHcnt=PAL-1+HFP then HS<='0'; elsifintHcnt=PAL-1+HFP+HPW then HS<='1'; endif; --Generates VS - active low ifintVcnt=LAF-1+VFP then VS<='0'; elsifintVcnt=LAF-1+VFP+VPW then VS<='1'; endif; end if; end process; -- mapping itnernal integers tostd_logic_vector ports Hcnt <= conv_std_logic_vector(intHcnt,10); Vcnt <= conv_std_logic_vector(intVcnt,10); 最后是显示如上图所示的格子的代码: mixer: process(ck25MHz,intHcnt,intVcnt) begin if intHcnt < PAL and intVcnt < LAFthen -- in the active screen -- ifVcnt(8 downto 7) = "00" then outGreen <= "000"; outBlue <= "00"; ifHcnt(8 downto 7) = "00" then outRed <= "100"; elsifHcnt(8 downto 7) = "11" then outRed <= "110"; else outRed <= "000"; end if; elsif Vcnt(8 downto 7) = "01"then ifHcnt(8 downto 7) = "01" then outRed <= "000"; outGreen <= Vcnt(5 downto 3); outBlue <= "00"; elsifHcnt(8 downto 7) = "10" then outRed<= "000"; outGreen <= "000"; outBlue <= "01"; else outGreen<= "000"; outBlue <= "00"; outRed<= "000"; endif; elsif Vcnt(8 downto 7) = "10"then if Hcnt(8 downto 7) = "01"then outRed <= Vcnt(5 downto 3); outGreen <= Vcnt(5 downto 3); outBlue <= Vcnt(5 downto 4); elsifHcnt(8 downto 7) = "10" then outRed<= "000"; outGreen <= "000"; outBlue <= "11"; else outGreen<= "000"; outBlue <= "00"; outRed<= "000"; endif; else outGreen <= "000"; outBlue <= "00"; ifHcnt(8 downto 7) = "00" then outRed <= "100"; elsif Hcnt(8 downto 7) = "11" then outRed <= "110"; else outRed <= "000"; end if; end if; else outRed <= (others => '0'); outGreen <= (others => '0'); outBlue <= (others => '0'); end if; end process; 有几点心得可以分享一下, 1、 时序必须严格,否则会显示不正常 2、 由于是640*480,所以长宽比例不是1:1在显示器上显示的格子会多显示其他部分,这个需要做修正。 3、 当药静态显示某个画面的时候,每一个点的在变换的时候必须在每页刷新的的频率的倍数,否则会跳动。 4、 本来想做字符显示的,但是貌似需要外扩RAM,不知道说得对不对。
|