基于FPGA的VGA显示控制的设计.docx
《基于FPGA的VGA显示控制的设计.docx》由会员分享,可在线阅读,更多相关《基于FPGA的VGA显示控制的设计.docx(22页珍藏版)》请在冰豆网上搜索。
![基于FPGA的VGA显示控制的设计.docx](https://file1.bdocx.com/fileroot1/2023-5/22/d384392a-7e9d-4b86-9c91-eba1f76614cd/d384392a-7e9d-4b86-9c91-eba1f76614cd1.gif)
基于FPGA的VGA显示控制的设计
基于FPGA的VGA图像显示设计及应用
摘要:
VGA(视频图形阵列)作为一种标准的显示接口得到广泛的应用。
本文依据VGA显示原理,介绍了利用FPGA实现对VGA图形控制器VHDL设计方法。
详细描述了各硬件模块的工作原理及实现途径,并给出了软件设计思路及部分代码。
关键词:
VGA;FPGA;VHDL;仿真
一、引言
随着可编程逻辑器件的不断发展及其价格的不断下降,EDA开发软件的不断完善,可编程逻辑设计的应用优势逐渐显示出来,特别是大规模可编程器件。
而FPGA具有功能强大,开发过程投资小、周期短等特点,成为当今硬件设计的首选方式之一。
VGA(视频图形阵列)作为一种标准的显示接口得到广泛地应用。
利用FPGA芯片和EDA设计方法,可以灵活地根据用户需求,设计出针对性强的VGA显示控制器,不仅降低了生产成本也可以快速地对产品进行升级换代。
本文设计采用QuartusII9.0软件工具,并以Altera公司的Cyclone系列FPGA的器件EP1C12F324C8为主实现硬件平台的设计。
二、方案设计及工作原理
2.1方案设计
采用模块化设计方法,我们对VGA图形控制器按功能进行层次划分。
本设计的VGA控制器主要由以下模块组成:
VGA时序控制模块、ROM内存地址控制模块、RO内存模块、按键扫描控制模块。
如图1所示。
图1.方案模块框图
2.2工作原理
根据VGA的显示协议,可以选择不同的显示分辨率。
本设计选择的是800*600的分辨率。
根据相应协议,在“VGA时序控制模块”对行扫描信号HS和场扫描信号VS进行相应的时序控制(具体控制详细信息见“VGA驱动原理”资料文档)。
“ROM内存地址控制模块”根据“按键扫描控制模块”获得的图片显示首坐标xx、yy信息和“VGA时序控制模块”传送过来的hcnt(列计数器值)、vcnt(行计数器值)及相关控制信号计算出正确的内存地址,从rom中取出所需要的像素点的三基色数据。
本设计的功能实现包括:
图像旋转、放大、单步步进移动和屏保移动功能。
其中图像的旋转运用了矩阵的转置原理,通过对图像的内存存储地址矩阵进行转置运算获得图像90°旋转的效果。
图像的放大是通过将包括原像素点在内,相邻的4个点填写相同的颜色来实现的。
三、各功能模块设计和仿真
3.1VGA时序控制模块
VGA时序控制模块是整个显示控制器的关键部分,其实质就是完成VGA显示卡的功能。
主要作用就是在一定的工作频率下,产生准确的时序关系(VS-垂直同步信号,HS-水平同步信号,消隐信号之间的关系)。
及其在准确的时序下对ROM存储器数据进行读取。
其中产生准确的时序关系为此模块重点,在VGA显示过程中,完成一行扫描所需要的时间称为水平扫描时间,完成一帧(一屏)扫描所需要的时间称为垂直扫描时间。
每扫描完一行用行同步信号进行同步;扫描完所有行后用场同步信号进行同步。
本文设计采用的是800×600×75Hz模式。
依据时序标准,每显示行包含1056点,其中800点为有效显示区,256点为消隐区,每行的行同步脉冲低电平宽度为80个像素点;同理每场有625行,有效行为600行,其中场同步脉冲低电平宽度为3行。
其行、场时序如表1,时序图见图2。
表1行扫描、场扫描时序
Format
Pixel
Clock
MHz
Horizontal(inPixels)
Vertical(inlines)
Active
Video
Front
Porch
Sync
Pulse
Back
Porch
Active
Video
Front
Porch
Sync
Pulse
Back
Porch
800,600,
75Hz
49.50
800
16
80
160
600
1
3
21
依照这个标准,正好与开发板EP1C12外部晶振频率50MHz相近,可以直接引用外部晶振时钟作为其驱动时钟(只要所用时钟和协议要求的时钟相差不大,不会影响显示效果,最多就是显示的刷新频率不是真正等于75Hz而已)。
图2行HS、场VS时序图
3.2ROM内存地址控制模块
整个显示思路是在800*600分辨率的显示器上开辟一256*64的显示区域来显示图片,因为显示的图片的大小为256*64。
在此区域以外显示指定颜色,例如黑色、蓝色等,作为一个背景色显示。
在本模块中,通过在对ROM内存地址的控制,实现了图片的旋转和一倍放大功能。
以下通过一段关键代码详细讲解其实现原理。
(1)首先分析无旋转状态下的romaddr_control的计算原理:
romaddr_control<=(vcnt(5downto0)-count_tempv(5downto0))
&(hcnt(7downto0)-count_temph(7downto0));
注:
romaddr_control为从rom中取数据时所需要的地址,它对应着图片的没一个像素点的三基色数据;vcnt(5downto0)和hcnt(7downto0)分别表示取vcnt(9downto0)、hcnt(10downto0)的后6、后8个二进制位来做运算。
count_tempv(5downto0)和count_temph(7downto0)同理可以理解其含义。
Vcnt、hcnt实际上可以分别理解为屏幕显示的行和列计数器,count_tempv和count_temph可以分别理解为图片在屏幕上显示的起始行和列坐标。
抛开以上等式,按照正常思路,我们可以得到图片控制地址:
addr_control=(vcnt-count_tempv)*256+(hcnt–count_temph)
通过分析整个mid.vhdl文件,会发现其实上面代码是该等式的高效等效实现。
(2)从矩阵的角度分析90°旋转的实现
旋转的控制,将图片的显示分为4种状态,“00”表示0°旋转,“01”表示90°旋转,“10”表示180°旋转,“11”表示270°旋转。
首先对比0°旋转和90°旋转图片在屏幕上显示所对应的内存地址矩阵图:
0度显示对应内存地址矩阵:
0*256+0
0*256+1
…
…
…
0*256+254
0*256+255
1*256+0
1*256+1
…
…
…
1*256+254
1*256+255
.
.
.
.
.
.
.
62*256+0
62*256+1
…
…
…
62*256+254
62*256+255
63*256+0
63*256+1
…
…
…
63*256+254
63*256+255
90度显示对应内存地址矩阵:
63*256+0
62*256+0
…
1*256+0
0*256+0
63*256+1
62*256+1
…
1*256+1
0*256+1
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
63*256+254
62*256+254
…
1*256+254
0*256+254
63*256+255
62*256+255
…
1*256+255
0*256+255
观察前后变化规律,可以看成是进行了矩阵的转置运算。
根据矩阵的转制原理,我们可以根据0°状态下romaddr_control的算法获得90°状态下romaddr_control的算法。
romaddr_control<=(64-(hcnt(5downto0)-count_temph(5downto0)))
&(vcnt(7downto0)-count_tempv(7downto0));
同理可以计算出“10”、“11”状态下的地址控制表达式,即旋转到180°和270°状态的地址控制表达式。
(3)实现一倍放大
实现一倍放大的基本思路为将原来的像素点相邻的另外三个点填上同样的颜色,也就是一个地址对应屏幕上的四个像素位,从而实现放大的效果。
这个比较容易实现,也比较容易想到,将原来的算法改为:
romaddr_control<=(hcnt(6downto1)-count_temph(6downto1))
&(vcnt(8downto1)-count_tempv(8downto1));
3.3按键扫描控制模块
按键扫描控制采用的电平触发机制,5Hz左右的扫描频率,适合于人们的使用习惯。
其中按键的功能里包括了:
a.单步上下、左右的移动;b.放大和不放大两种模式的选择;c.顺时针90°旋转按钮;d.屏保模式自由移动和停止选择按钮。
3.4ROM内存模块
ROM内存中存储的是图片的依次行扫描三基色数据,作为显示时送给VGA显示器的RGB数据。
本设计存储的是一幅256*64的图片,所以定义了一256*64=16384byte的rom内存。
四、结果分析和调试
在调试过程中,我们遇到图片在靠近行和列的零边界线时,会出现整个图片突然消失的问题,而不是想象中的逐渐步入,逐渐消失。
经过查阅资料,发现VHDL中没有能表示负数的数据类型,而在程序的运算过程中,有会出现负数的可能性,即在对图片初始坐标的减运算过程中,可能会将图片的起始坐标减成负数,使图片初始坐标变量进入未知状态,致使图片在屏幕上立即消失的。
经过对程序的修改,排除了大部分的漏洞,让图片显示基本能按设想显示,不排除还存在部分Bug的可能性。
5、体会和感受
利用可编程逻辑器件(FPGA)可以很方便地实现数字系统设计,而在Altera的QuartusII软件平台下,FPGA设计的各个阶段都得到了很好的支持,两者有效结合使得数字系统的设计更加方便快捷。
通过硬件平台的验证,基于FPGA的VGA图形显示器已达到设计要求,可以稳定地实现界面的显示,并可以在界面上根据需要进行数字的动态改变显示,对于一些工业监控场合的应用,尤其数据量不是很大的场合尤为适合,不仅可以及时显示当前状态,还可以保存关机时状态。
经过本次课程设计,学到了很多VHDL的知识,比纯粹的理论教学课堂上学到的知识更多,更加深刻。
实践教学方式对于我们工程运用专业是一个非常适合的教学方式,不仅锻炼了个人的动手能力,而且调动了学习的积极性,改变了我们的学习状态,是一种非常值得重视和推广的教学方式。
六、参考文献
【1】王诚,吴继华,范丽珍.[AlteraFPGA/CPLD设计(基础篇)][M].北京人民邮电出版社(2005)
【2】王诚,吴继华,范丽珍.[AlteraFPGA/CPLD设计(高级篇)][M].北京:
人民邮电出版社(2005)
【3】夏宇闻.[Verilog数字系统设计教程][M].北京:
北京航空航天大学出版社(2003)
【4】潘松、王国栋.[VHDL实用教程][M].电子科技大学出版社(2000)
【5】李广军孟宪元.[可编程ASIC设计及应用][M].电子科技大学出版社(2000)
【6】曹允.[基于FPGA的时序彩条信号实现方法及应用][M].电子技术应用,第七期(2006)
【7】朱耀东,经亚枝,张焕春.[基于FPGA的LCD&VGA控制器设计][M].电子技术应用,第十一期(2006)
【8】陈志生,陈景贤.[基于FPGA的多分辨率VGA图像控制器设计][J].现代电子技术(2008)
【9】王恒心.熊庆国.基于FPGA/CPLD的嵌入式VGA显示系统[J].微计算机信息(2008)
【10】潘松,黄继业.[EDA技术与VHDL][M].2版.北京:
清华大学出版社(2007)
1.附录(程序和元件清单)
(1)800*600VGA时序控制模块代码:
----------------------------------
libraryIEEE;
useIEEE.std_logic_1164.all;
useIEEE.STD_LOGIC_UNSIGNED.ALL;
entityvga800600is
port(
clk:
inSTD_LOGIC;
hs:
outSTD_LOGIc;
vs:
outSTD_LOGIc;
r:
outSTD_LOGIC_VECTOR(2downto0);
g:
outSTD_LOGIC_VECTOR(2downto0);
b:
outSTD_LOGIC_VECTOR(1downto0);
rgbin:
instd_logic_vector(7downto0);
hcntout:
outstd_logic_vector(10downto0);
vcntout:
outstd_logic_vector(9downto0));
endvga800600;
architectureONEofvga800600is
signalhcnt:
std_logic_vector(10downto0);
signalvcnt:
std_logic_vector(9downto0);
begin
hcntout<=hcnt;
vcntout<=vcnt;
process(clk)begin
if(rising_edge(clk))then
if(hcnt<1056)then
hcnt<=hcnt+1;
else
hcnt<=(others=>'0');
endif;
endif;
endprocess;
--thisisVerticalcounter
process(clk)begin
if(rising_edge(clk))then
if(hcnt=800+8)then
if(vcnt<625)then
vcnt<=vcnt+1;
else
vcnt<=(others=>'0');
endif;
endif;
endif;
endprocess;
--thisishspulse
process(clk)begin
if(rising_edge(clk))then
if((hcnt>=800+8+8)and(hcnt<800+8+8+80))then
hs<='0';
else
hs<='1';
endif;
endif;
endprocess;
--thisisvspulse
process(vcnt)begin
if((vcnt>=600+0+1)and(vcnt<600+0+1+3))then
vs<='0';
else
vs<='1';
endif;
endprocess;
process(clk)begin
if(rising_edge(clk))then
if(hcnt<800andvcnt<600)then
r(2downto0)<=rgbin(7downto5);
g(2downto0)<=rgbin(4downto2);
b(1downto0)<=rgbin(1downto0);
else
r<="000";
g<="000";
b<="00";
endif;
endif;
endprocess;
endONE;
(2)rom内存地址控制模块代码:
--------------------------------------
libraryieee;
useieee.std_logic_1164.all;
useieee.std_logic_unsigned.all;
entitymidis
port(
clk:
instd_logic;
fangda_temp:
instd_logic;
mode:
instd_logic_vector(1downto0);--key按键输入旋转90度信号,下降沿有效
qin:
instd_logic_vector(7downto0);
xx:
instd_logic_vector(9downto0);
yy:
instd_logic_vector(9downto0);
hcntin:
instd_logic_vector(10downto0);
vcntin:
instd_logic_vector(9downto0);
qout:
outstd_logic_vector(7downto0);
romaddr_control:
outstd_logic_vector(13downto0)
);
endmid;
architectureoneofmidis
signalxuanzhuanjiaodu:
std_logic_vector(1downto0);
signalhcnt:
std_logic_vector(10downto0);
signalvcnt:
std_logic_vector(9downto0);
signalqout_temp:
std_logic_vector(7downto0);
signalcount_temph:
std_logic_vector(9downto0);
signalcount_tempv:
std_logic_vector(9downto0);
signalwide:
integerrange0to1024;
signallong:
integerrange0to1024;
begin
--Assignpin
hcnt<=hcntin;
vcnt<=vcntin;
qout<=qout_temp;
xuanzhuanjiaodu<=mode;
process(fangda_temp)
begin
if(fangda_temp='0')then
wide<=256;
long<=64;
else
wide<=512;
long<=128;
endif;
endprocess;
process(xuanzhuanjiaodu)
BEGIN
casexuanzhuanjiaoduIS
WHEN"00"=>
if(fangda_temp='0')then
romaddr_control<=(vcnt(5downto0)-count_tempv(5downto0))--0du
&(hcnt(7downto0)-count_temph(7downto0));
else
romaddr_control<=(vcnt(6downto1)-count_tempv(6downto1))--0du
&(hcnt(8downto1)-count_temph(8downto1));
endif;
WHEN"01"=>
if(fangda_temp='0')then
romaddr_control<=(64-(hcnt(5downto0)-count_temph(5downto0)))--90du
&(vcnt(7downto0)-count_tempv(7downto0));
else
romaddr_control<=(64-(hcnt(6downto1)-count_temph(6downto1)))--90du
&(vcnt(8downto1)-count_tempv(8downto1));
endif;
WHEN"10"=>
if(fangda_temp='0')then
romaddr_control<=(64-(vcnt(5downto0)-count_tempv(5downto0)))--180du
&(256-(hcnt(7downto0)-count_temph(7downto0)));
else
romaddr_control<=(64-(vcnt(6downto1)-count_tempv(6downto1)))--180du
&(256-(hcnt(8downto1)-count_temph(8downto1)));
endif;
WHENOTHERS=>
if(fangda_temp='0')then
romaddr_control<=((hcnt(5downto0)-count_temph(5downto0)))--270du
&(256-(vcnt(7downto0)-count_tempv(7downto0)));
else
romaddr_control<=((hcnt(6downto1)-count_temph(6downto1)))--270du
&(256-(vcnt(8downto1)-count_tempv(8downto1)));
endif;
endcase;
endprocess;
process(xx,yy)begin
if((vcnt=yy)and(hcnt=xx))then
count_temph<=xx;
count_tempv<=yy;
endif;
if((xuanzhuanjiaodu=1)or(xuanzhuanjiaodu=3))then
if((vcntyy+wide))thenqout_temp<="00000111";--cnt(31downto24);
elsif((hcnt>xx)and(hcntqout_temp<=qin;---------inputlogo.hex
else
qout_temp<="00000111";--cnt(31downto24);
endif;
else
if((vcntyy+long))thenqout_temp<="00000111";--cnt(31downto24);
e