明德扬论坛

 找回密码
 立即注册

QQ登录

只需一步,快速开始

微信扫一扫,快捷登录!

查看: 1499|回复: 2

【FPGA至简设计原理与应用】书籍连载17 第三篇 FPGA至简设计项目 第八章 VGA显示颜色

[复制链接]
发表于 2020-6-2 15:29:52 | 显示全部楼层 |阅读模式

马上注册,看完整文章,学更多FPGA知识。

您需要 登录 才可以下载或查看,没有帐号?立即注册

x

大家好,近期我们会连载《FPGA至简设计原理与应用》一书,有兴趣的同学可以学习,也希望大家可以对我们的书提出宝贵的意见和建议。

《FPGA
至简设计原理与应用》书籍连载索引目录

http://www.fpgabbs.cn/forum.php?mod=viewthread&tid=989


读过的朋友可积极在贴后留言,书籍正式出版时,我们会从留言者中挑选20位幸运读者,幸运读者可获潘老师亲笔签名书籍一本。

注:手机浏览可能格式会乱,建议用电脑端进行浏览。





FPGA至简设计项目实践   


      第八章 VGA显示颜色

第1节 项目背景

1.1 VGA介绍

Video Graphics ArrayVGA)视频图形阵列是IBM公司在1987年随着PS/2一起推出的使用模拟信号的一种视频传输标准。其在当时具有分辨率高、显示速率快、颜色丰富等优点,因而在彩色显示器领域得到了广泛的应用。虽然对于现今的个人电脑市场来说该标准已经十分过时,但VGA仍然是众多制造商所共同支持的一个标准。在加载自己的独特驱动程序之前,个人电脑都必须支持VGA的标准。例如,微软Windows系列产品的开机画面仍然使用VGA显示模式,这也说明该标准在显示标准中的重要性和兼容性。

目前,VGA技术主要应用于基于VGA显示卡的计算机、笔记本电脑等设备,而少用于要求显示彩色高分辨率图像又没有必要使用计算机的设备中。部分嵌入式的VGA显示系统可以在不使用VGA显示卡和计算机的情况下实现VGA图像的显示和控制,该系统具有成本低、结构简单、应用灵活的优点,可广泛应用于超市、车站、飞机场等公共场所的广告宣传和提示信息显示,也可应用于工厂车间生产过程中的操作信息显示,还能以多媒体形式应用于日常的生活中。

1.2 VGA管脚
VGA接口是一种D型接口,采用非对称分布的15pin 连接方式,如下图所示,共有15针,分成3排,每排5个孔。这是显卡中应用最为广泛的接口类型,绝大多数显卡都带有此种接口,可以传输红、绿、蓝模拟信号以及同步信号(水平和垂直信号)
                              
1.png
3.8-1VGA插针图

2.png
3.8-2VGA插座图

VGA接头上一般会156101115等标明每个接口的编号。如果没有这一编号则按照下图所示进行编号。VGA接口共有15根针,其对应接口定义如下:
1、红基色 red
2、绿基色 green
3、蓝基色 blue
4、地址码 ID Bit(也有部分是RES,或者为ID2显示器标示位2
5、自测试 ( 各家定义不同 )(一般为GND
6、红地
7、绿地
8、蓝地
9、保留 ( 各家定义不同 )
10、数字地
11、地址码(ID0显示器标示位0
12、地址码(ID1显示器标示位1
13、行同步
14、场同步
15、地址码 ( ID3或显示器标示位3 )
3.png
3.8-3 VGA接口管脚定义

在进行FPGA逻辑设计时主要关注的信号是红基色、绿基色、蓝基色、行同步和场同步信号,其他信号在进行原理图和PCB设计时才需要关注。FPGA通过控制红基色、绿基色、蓝基色、行同步和场同步信号这5个接口,就能让显示器显示丰富的色彩和各种视频图像。

1.3 VGA色彩原理
在中学的物理课中同学们可能做过棱镜的试验,通过棱镜后白光被分解成多种颜色逐渐过渡的色谱,依次为红、橙、黄、绿、青、蓝、紫,这就是可见光谱。而人的眼睛就像一个三色接收器的体系,在可见光谱中人眼对红、绿、蓝最为敏感,在视觉接收时大多数的颜色可以通过红、绿、蓝三色按照不同的比例合成产生。同样地,绝大多数的单色光也可以分解成红绿蓝三种色光,这就是色度学最基本的原理,即三基色原理。三种基色相互独立,任何一种基色都不能由其它两种基色合成。红绿蓝就是三基色,这三种颜色合成的颜色范围最为广泛,而按照不同的比例的红绿蓝三基色相加合成的混色被称为相加混色。

三基色的颜色编码如下所示:

3.8-1三基色颜色编码
颜色
绿
  
R
  
  
0
  
  
0
  
  
1
  
  
1
  
  
0
  
  
0
  
  
1
  
  
1
  
  
G
  
  
0
  
  
0
  
  
0
  
  
0
  
  
1
  
  
1
  
  
1
  
  
1
  
  
B
  
  
0
  
  
1
  
  
0
  
  
1
  
  
0
  
  
1
  
  
0
  
  
1
  

从上表可以看出RBG一共有8组合,即可以产生8种颜色。然而显示器显示的色彩非常丰富,要远远多于8种颜色,这又是如何做到的呢?

对于显示器来说,RGB三个信号其实是模拟信号,其电压的高低可以表示出颜色的深浅。利用这一原理,就可以产生丰富的色彩。例如,如果R=3.3VG=0VB=0V,则显示器会显示非常鲜艳的红色。如果GB仍然是0V,而R改为1.8V,则显示器会显示比较浅的红色。RGB的电压范围从0~3.3V,将其任意组合就可以表示出丰富的颜色了。

1.4 显示器扫描方式
通过控制红绿蓝三基色可以确定一个像素的颜色,但众所周知一幅图像是由非常多的像素组成的。例如640*480分辨率的图像则是由480行、每行640个像素组合起来显示的图像。如果显示器想要显示这样一幅图像,就需要控制显示器的扫描枪将每一个像素对应的颜色显示出来,且需要控制像素快速变化,使人眼认为所有像素同时显示,从而达到显示器显示图像的效果。

CRT 显示器的控制框图如下图所示:
4.png
3.8-4显示器扫描原理

显示器采用光栅扫描方式,即轰击荧光屏的电子束在CRT屏幕上从左到右(受水平同步信号HSYNC控制)、从上到下(受垂直同步信号VSYNC控制)做有规律的移动。电子束采用光栅扫描方式,从屏幕左上角一点开始,向右逐点进行扫描,形成一条水平线;在到达最右端后,回到下一条水平线的最左端重复上面的过程;当电子束完成右下角一点的扫描后,完成一帧图像扫描。随后,电子束再次回到左上方起点开始下一帧的扫描。这种方法即为常说的逐行扫描显示,通俗来讲就是显示器是从左上角的第一个像素模块开始,从左到右,从上到下,一块一块的快速显示,从而实现了一幅静止画面,这幅静止画面在视频中称之为一帧,连续的帧就会产成动画的效果。

1.5 VGA时序
行同步信号的时序如下图所示。可以看出行同步信号周期性地产生高低电平,可将其分为4个阶段:同步脉冲a、显示后沿b、显示区域c和显示前沿d。同步脉冲a代表新一行的扫描开始,同时也是上一行扫描的结束。显示时序c是真正图像的显示区域,在此阶段,像素逐个显示出来,即在这一阶段控制红、绿、蓝三基色信号输出对应像素的RGB值。显示后沿b和显示前沿d这两个阶段是消隐时刻,此时要求红、绿、蓝三基色信号都为0

5.png
3.8-5 VGA行同步信号时序

通俗来讲即行同步信号先产生一段低电平的同步脉冲表示“这一行要开始了”。此处可能存在疑虑:为什么要有这一段同步脉冲呢?从图3.8- 4的显示器扫描原理图中可以看出,电子束的行进路径是从左上角开始,由左到右一个点一个点的移动。当一行扫描完毕后,电子束则需要从最右边回到最左边开始下一行的扫描,这一移动的时间即为同步脉冲a

在同步脉冲a结束后,信号由01迎来一个上升沿,进入显示信号。这段显示信号包含显示后沿b、显示时序c和显示前沿d三部分,其中只有显示时序c是真正可以显示像素的,即在这一段需要输出有效的RGB的值,从而保证显示效果。
场同步信号的时序与行同步信号相似,如下图所示。

6.png
3.8-6VGA场同步信号时序

场同步信号也是周期性地产生高低电平,同样可以分成4个阶段:同步脉冲a、显示后沿b、显示区域c和显示前沿d。但需要注意的是,场同步信号的变化单位是“一行”,即一个“场同步脉冲”时间内包含多个“行脉冲周期”。

为了避免读者错误理解,这里再次强调一下VGA的时序。
1、场同步的变化是以“一行”为单位的。例如,假定一行时间是800个时钟,场同步脉冲a的值为2,则场同步脉冲的时间是2*800=1600个时钟。
2、真正的显示区域是场同步信号处于显示区域且行同步信号也处于显示区域的部分,其他区域中红、绿、蓝基色都要给低电平。即场同步信号和行同步信号均处于显示区域的时候,才是真正的显示区域。

在进行时序观察时,同学们不仅要关注其变化点,还要关注持续的时间。那么行、场同步四个阶段中的持续时间分别是多少呢?下面是常见分辨率的相应参数。

3.8-2常见分辨率参数
  
分辨率
  
行/列
同步脉冲
显示后沿
显示区域
显示前沿
帧长
单位
640*480
  
/60Hz
96
48
640
16
800
基准时钟
2
33
480
10
525
800*600
  
/72Hz
120
64
800
56
1040
基准时钟
6
23
600
37
666
800*600
  
/60Hz
128
88
800
40
1056
基准时钟
4
23
600
1
628
1024*768
  
/60Hz
136
160
1024
24
1344
基准时钟
6
29
768
3
806

640*480/60Hz为例,这一指标代表刷新频率为60Hz,分辨率为640X480。标准VGA显示驱动中刷新频率60 Hz即为1秒显示60幅图像。

此外,从上表中可以看出,在该分辨率下行同步信号的同步脉冲是96个基准时钟,显示后沿是48个基准时钟,显示区域是640个基准时钟,显示前沿是16个基准时钟,通过相加可以得出一行一共有800个基准时钟。该分辨率下的场同步信号的同步脉冲是2行(2*800个基准时钟),显示后沿是33行(33*800个基准时钟),显示区域为480行(480*800个基准时钟),显示前沿为10行(10*800个基准时钟),共计525行(525*800个基准时钟)。

那么基准时钟又是多少呢?由于1秒显示60幅图像,所以一幅图像显示的时间是1/60秒。由于一幅图像占用了525*800个基准时钟,则基准时钟周期 = 1/60/525*800)秒,约为39.6825ns。因此,基准时钟频率约为25.175 MHz,在本设计中取25M

有些读者可能对于行同步信号与场同步信号还是不太理解,在这里将两个信号融合在一起解释一下。每一行的扫描需要行同步信号,且每一行中都具有同步脉冲a,显示后沿b,显示时序c和显示前沿d四个阶段,完成一行扫描后再进入到下一行再次重复同样的四个阶段,这一过程为行同步。将范围扩大,点动成线即为行,线动成面即为场。场同步中和行同步一样依旧分为四个阶段,同步脉冲a,显示后沿b,显示时序c和显示前沿d。因此就有了如图3.8- 6所示的时序图,场同步的单位就为行同步。

这也是最终选取的显示区域需要行同步和场同步都处于显示时序c中的原因。试想一下,如果行同步处于显示脉冲c的位置,但是场同步还处于同步脉冲a的位置,在这一状态下无法完成图像的显示。同理,如果场同步处于显示脉冲c的位置,但是行同步处于同步脉冲a的位置,也同样无法显示图像。这是一个包含与被包含的关系,在场同步脉冲c位置的时候,包含了很多个时序四个脉冲的行同步,而只有在行同步中也处于同步脉冲c的位置时才可以真正显示图像。

1.6 至简设计法VGA原理图
FPGA是数字芯片,其管脚输出的都是01的数字信号,只有高电平和低电平。因此如果要控制RGB电压的高低从而实现显示器彩色画面的显示,则必须用到数转换DA芯片,利用数字信号控制数模转换芯片的输入端,从而让其输出不同幅度的电压值。

如下图所示,FPGA产生RGB三种信号,此时的RGB都是多位数字信号,将RGB信号传送给DA芯片,DA会根据这一数字信号产生不同电压的模拟信号rgb。此时只要将模拟信号rgb连到显示器上即可显示丰富的颜色。

在这里,读者只需了解FPGA通过数字信号控制DA芯片,DA芯片就可以产生不同电平即可。关有DA芯片的介绍可以参考至简设计法中DA转换的相关内容。

7.png
3.8-7VGA数模转换原理

至简设计法教学板中VGA接口的原理图如下图所示。
8.png
3.8-8教学板VGA接口原理图

其中,行同步管脚连接信号VGA_HSYNC,场同步信号连接信号VGA_VSYNC,红基管脚连接VGA_RED,蓝基管脚连接信号VGA_BLUE,绿基管脚连接信号VGA_GREEN

VGA_HSYNCVGA_VSYNC信号的另一端连接到FPGAC20D20管脚上。
9.png
3.8-9VGA信号与FPGA连接图

综上所述,FPGA控制管脚C20D20的输出,从而控制VGA接口的行、场同步。

再来看VGA_REDVGA_BLUEVGA_GREEN信号,其原理图如下图所示。
10.png
3.8-10电阻实现数模转换网络

从图中可以看出,VGA_REDVGA_R0~VGA_R4与电阻并联产生的,VGA_GREENVGA_G0~VGA_G5与电阻并联产生,VGA_BLUEVGA_B0~VGA_B4与电阻并联产生。而VGA_R0~VGA_R4VGA_G0~VGA_G5VGA_B0~VGA_B4是连接到FPGA管脚的数字信号,每个信号都只有0V3.3V两种可能。那么FPGA可以通过控制这些信号来控制VGA红基、绿基和蓝基管脚的电压。

以利用VGA_R0~VGA_R4产生VGA_RED的电压值为例。
VGA_RED电压 = (VGA_R0/2 + VGA_R1/4 + VGA_R2/8 + VGA_R3/16 + VGA_R4/32)*3.3V
VGA_GREEN电压 = (VGA_G0/2 + VGA_G1/4 + VGA_G2/8 + VGA_G3/16 +VGA_G4/32+VGA_G5/64)*3.3V
VGA_BLUE电压 = (VGA_B0/2 + VGA_B1/4 + VGA_B2/8 + VGA_B3/16 + VGA_B4/32)*3.3V

至简设计法教学板利用电阻网络代替DA芯片来产生不同幅度电压。其信号与管脚的对应关系及本设计所用颜色的对应信号值如表3.8- 4所示。
11.png
3.8-11VGA信号与FPGA连接

3.8-3信号与FPGA管脚对应关系
  
电阻网络转换后
  
信号线
  
信号线
FPGA管脚
VGA_RED
VGA_R4
E11
VGA_R3
C10
VGA_R2
D10
VGA_R1
E9
VGA_R0
E10
VGA_GREEN
VGA_G5
D15
VGA_G4
C17
VGA_G3
C19
VGA_G2
E12
VGA_G1
C13
VGA_G0
E15
VGA_BLUE
VGA_B4
D13
VGA_B3
E13
VGA_B2
D17
VGA_B1
E16
VGA_B0
C15
VGA_HSYNC
VGA_HSYNC
C20
VGA_VSYNC
VGA_VSYNC
D20

3.8-4本设计使用颜色的相应信号值
  
本设计使用的颜色
  
VGA_R4~R0
VGA_G5~G0
VGA_B4~B0
白色
5’b11111
6’b111111
5’b11111
黑色
5’b0
6’b0
5’b0
蓝色
5’b0
6’b0
5’b11111
绿色
5’b0
6’b111111
5’b0
红色
5’b11111
6’b0
5’b0
第2节 设计目标
了解了VGA的显示原理后来进行本次设计:通过VGA进行不同颜色显示。按照至简设计法的思路,在进行设计之前首先应明确设计目标。明确了设计目标后,后续的每一步操作都是围绕设计目标进行展开。如果没有牢记设计目标就开始动手进行实践操作,最终的作品也是东拼西凑的产物,一旦在设计过程中出现了问题就需要花费大量的精力进行寻找修复。建议初学者在开始学习时就养成良好的设计习惯,才能在后续的职业生涯中受益。

本设计需要通过VGA连接线将显示器和开发板进行连接,FPGA在连接成功后产生640*480分辨率,刷新频率为60HzVGA时序,使显示器产生显示一幅完整的红色图像。显示器一般都具有分辨率自适应功能,无须设置就能识别不同分辨率的图像。本设计的相应参数参见表3.8-5中的第一行,该表中常用分辨率的VGA对应时序参数并不是随意选定的,而是国际通用标准,每个关于VGA的设计工程都需要遵守这一标准。这里需要注意的是:行的单位为“基准时钟”,即频率为25MHz、周期为40ns的时钟,列的单位则为“行”,请读者朋友们一定要准确区分。

3.8-5常见分辨率参数
  
分辨率
  
行/列
同步脉冲
显示后沿
显示区域
显示前沿
帧长
单位
640*480
  
/60Hz
96
48
640
16
800
基准时钟
2
33
480
10
525
800*600
  
/72Hz
120
64
800
56
1040
基准时钟
6
23
600
37
666
800*600
  
/60Hz
128
88
800
40
1056
基准时钟
4
23
600
1
628
1024*768
  
/60Hz
136
160
1024
24
1344
基准时钟
6
29
768
3
806

设计完成后,使用VGA线将开发板与显示器相连,连接示意图如下所示。
12.png
3.8-12教学板连接示意图

上板后显示器展示效果图如下图所示,不同的显示器会有一定的色差,需要以实际显示情况为主。想要观看连接后演示视频效果的读者朋友,可以登陆至简设计法官网观看学习:www.mdy-edu.com/xxxx
13.png
3.8-13VGA显示颜色效果图

第3节 设计实现
确定了设计目标后,本书会逐步分析讲解工程的制作步骤。建议初学者认真学习每一步,因为这里分享给同学们的不仅仅是案例,还有在操作过程中的一些设计理念及原理。当然本书也会分享一些至简设计法的设计技巧,希望每一位读者都可以具备独立设计工程的能力。当然已经拥有扎实的功底、只是想要根据步骤完成项目的读者朋友们可以跳过此部分,直接进入第五节中的简略版操作步骤分享。

3.1 顶层信号
新建目录:D:\mdy_book\color_exec1,并该目录中,新建一个名为color_exec1.v的文件。用GVIM打开后开始编写代码。这里建议初学者一定要按照本书提供的文件路径以及文件名进行设置,避免后面出现未知错误。
第一步应确定顶层信号。分析设计目标可知:本设计中FPGA会产生VGA时序,即控制VGA_R4~R0VGA_G5~G0VGA_B4~B0VGA_HSYNCVGA_VSYNC,从而使显示器显示红色。其中,FPGA可根据时序产生高低电平从而控制VGA_HSYNCVGA_VSYNC。由于本设计需要的显示的颜色数据是固定的红色,因此通过FPGA自身产生而不需要外部输入图像的数据。在FPGA的设计中可以定义输出信号hys表示行同步,定义输出信号vys表示场同步,定义一个16位的信号lcd_rgb来进行RGB输出,其中lcd_rgb[15:11]表示VGA_R4~0lcd_rgb[10:5]表示VGA_G5~0lcd_rgb[4:0]表示VGA_B4~0。当然,本设计中还需要时钟信号clk和复位信号rst_n来进行工程控制。

综上所述,此设计共需要五个信号:时钟信号clk、复位信号rst_n、场同步信号vys、行同步信号hys16位的RGB输出信号lcd_rgb。信号和硬件的对应关系如下表所示。

3.8-6信号和管脚关系
  
器件
  
电阻网络转换后
  
信号线
信号线
FPGA管脚
FPGA工程信号
CN1
VGA_RED
VGA_R4
E11
lcd_rgb[15]
VGA_R3
C10
lcd_rgb[14]
VGA_R2
D10
lcd_rgb[13]
VGA_R1
E9
lcd_rgb[12]
VGA_R0
E10
lcd_rgb[11]
VGA_GREEN
VGA_G5
D15
lcd_rgb[10]
VGA_G4
C17
lcd_rgb[9]
VGA_G3
C19
lcd_rgb[8]
VGA_G2
E12
lcd_rgb[7]
VGA_G1
C13
lcd_rgb[6]
VGA_G0
E15
lcd_rgb[5]
VGA_BLUE
VGA_B4
D13
lcd_rgb[4]
VGA_B3
E13
lcd_rgb[3]
VGA_B2
D17
lcd_rgb[2]
VGA_B1
E16
lcd_rgb[1]
VGA_B0
C15
lcd_rgb[0]
VGA_HSYNC
VGA_HSYNC
C20
hys
VGA_VSYNC
VGA_VSYNC
D20
vys
X1
SYS_CLK
G1
clk
K1
SYS_RST
AB12
rst_n

通过以上分析可以写出顶层信号代码:将module的名称定义为color_exec1,已知该模块有五个信号:clkrst_nlcd_hslcd_vslcd_rgb,将与外部相连接的输入/输出信号列出,从而实现信号与管脚的连接。具体顶层代码如下:
1
  
2
  
3
  
4
  
5
  
6
  
7
module color_exec1(
  
clk   ,
  
rst_n   ,
  
lcd_hs   ,
  
lcd_vs   ,
  
lcd_rgb
  
     );

随后声明信号的输入输出属性,模块中需要声明这一信号对于FPGA来说,属于输入信号还是输出信号。信号若为输入的话则声明其为input,若为输出则声明其为ouput。在本设计中,由于clk是外部的晶振输入给FPGA的,因此在FPGAclk是输入信号input;同样地,rst_n是外部按键给FPGA的,在FPGA中同样为输入信号inputlcd_hslcd_vslcd_rgbFPGA输出给显示器的,因此是输出信号output。其中clkrst_nlcd_hslcd_vs的值都是0或者1,用一根线表示即可,即位宽为1lcd_rgb信号的位宽为为16位。根据以上分析补充输入输出端口定义,其具体代码如下:
1
  
2
  
3
  
4
  
5
input                   clk  ;
  
input                   rst_n  ;
  
output                  lcd_hs  ;
  
output                  lcd_vs  ;
  
output   [15:0]          lcd_rgb       ;
3.2 信号设计
分析设计目标可知,首先需要设计行同步信号hys,其时序图表示如下:
14.png
3.8-14VGA行同步时序

根据时序图可以看出hys是一个周期性进行高低变化的脉冲。根据设计目标得知图像分辨率选定为640*480,因此使用下表中的640*480分辨率的相应参数。即同步脉冲a的时间是96个基准时钟,显示后沿b的时间是48个基准时钟周期,显示时序c的时间是640个基准时钟,显示前沿的时间是16个基准时钟,共计800个基准时钟(800=96+48+640+16)。

3.8-5常见分辨率参数
  
分辨率
  
行/列
同步脉冲
显示后沿
显示区域
显示前沿
帧长
单位
640*480
  
/60Hz
96
48
640
16
800
基准时钟
2
33
480
10
525
800*600
  
/72Hz
120
64
800
56
1040
基准时钟
6
23
600
37
666
800*600
  
/60Hz
128
88
800
40
1056
基准时钟
4
23
600
1
628
1024*768
  
/60Hz
136
160
1024
24
1344
基准时钟
6
29
768
3
806

这里需要注意,一个基准时钟是40ns,而至简设计法开发板的时钟周期是20ns,因此基于至简设计法开发板的VGA工程设计中,采用2个时钟代表一个基准时钟。在图中补充对应的时间信号,带有时间信号的时序图如下图所示。
15.png
3.8-15带时间信息的VGA行同步时序

根据至简设计法的理论,分析波形图和设计目标后可以得到本设计的计数器架构:本设计需要使用2个计数器,一个计数器cnt0用来计数基准时间,另一个计数器cnt1用来计数hys的行长度。

先来讨论用于计数基准时间的计数器cnt0。至简设计法的计数器只考虑两个因素:加1条件和计数数量,只要确定相应逻辑,就能完成计数器代码设计。

首先确定计数器cnt0的加1条件:由于该计数器在不停地计数,是永远不停止的,因此可以认为其加1条件始终有效,可写成:assign add_cnt0==1

此处可能有同学存在疑惑加1条件的概念是什么?这里以停车位来进行比喻,一般情况下对每个停车位置会进行对应编号,但是如果某个位置上放置了一块石头无法作为停车位时,该位置就不能获得对应的编号。反之则可以认为停车位编号的加1条件就是:对应位置上没有石头,其可以继续的进行编号,即assign add_cnt0 = “没有石头。因此如果在设计中计数器一直没有阻碍地进行计数工作,就可以认为加1条件是一直有效的。

接下来确定计数器cnt0的计数数量,前文分析中可知2个时钟周期等于1个基准时钟,所以计数器cnt0的计数数量为2

确定好了加1条件和计数数量后开始进行代码编写。相信各位往常都是一行行输入代码,但是至简设计法有一个小技巧,可以节省编写代码的时间,并且一定程度上降低了代码的出错率。至简设计法将日常代码中常用到的固定部分做成了模板,进行代码编程时可以调用相应模板后根据逻辑输入对应设计的变量将代码补充完整。这里就可以用模板编写计数器代码,感受一下这个炫酷的功能。

打开GVIM,在命令模式下输入“:Mdyjsq”,点击回车,就调出了对应模板,如下图所示。之后再将本案例中的变量填到模板里面,就可以得到完整正确的计数器代码。

16.png
3.8-16至简设计法调用计数器代码模板

补充完整后得到计数基准时间的计数器cnt0代码如下。
1
  
2
  
3
  
4
  
5
  
6
  
7
  
8
  
9
  
10
  
11
  
12
  
13
always  @(posedge clk or negedge rst_n)begin
  
    if(!rst_n)begin
  
        cnt0 <= 0;
  
    end
  
    else if(add_cnt0)begin
  
        if(end_cnt0)
  
            cnt0 <= 0;
  
        else
  
            cnt0 <= cnt0 + 1;
  
    end
  
end
  
  
assign  add_cnt0 = 1;
  
assign  end_cnt0 = add_cnt0 && cnt0== 2 -1;

接着讨论用于计数hys长度的计数器cnt1。根据设计目标可知一行需要800个基准时钟,因此其计数数量为800。前文设计中已经确定一个基准时钟可以用end_cnt0表示。因此计数器cnt1的加1条件为“end_cnt0”,可写成:assign add_cnt1 = end_cnt0。继续调用至简设计法模板,在命令模式下输入“:Mdyjsq”,点击回车,就调出了对应模板,将“add_cnt1”和“end_cnt1”补充完整,得到该计数器的代码如下:
1
  
2
  
3
  
4
  
5
  
6
  
7
  
8
  
9
  
10
  
11
  
12
  
13
always  @(posedge clk or negedge rst_n)begin
  
    if(!rst_n)begin
  
        cnt1 <= 0;
  
    end
  
    else if(add_cnt1)begin
  
        if(end_cnt1)
  
            cnt1 <= 0;
  
        else
  
            cnt1 <= cnt1 + 1;
  
    end
  
end
  
  
assign  add_cnt1 = end_cnt0;
  
assign  end_cnt1 = add_cnt1 && cnt1==800-1 ;

确定了计数器cnt0cnt1hys信号的设计则有了对齐的对象。从时序图可以发现,hys有两个变化点,一个是cnt1数到96个基准时钟时,同步脉冲a结束,信号由01出现一个上升沿;另一个是当cnt1数到800个基准时钟时,信号由10出现下降沿。下面将其翻译成代码,在编辑模式下输入“Shixu2”,调用至简设计法模板,将模板补充完整得到场同步信号的代码如下:
1
  
2
  
3
  
4
  
5
  
6
  
7
  
8
  
9
  
10
  
11
always  @(posedge clk or negedge rst_n)begin
  
    if(rst_n==1'b0)begin
  
hys<=  0;
  
    end
  
    else if(add_cnt1 &&  cnt1==96-1)begin
  
hys<= 1;
  
    end
  
else if(end_cnt1)begin
  
hys<=  0;
  
    end
  
end

接下来讨论vys信号的设计,根据设计目标可以得到VGA场同步信号的时序图如下所示:
17.png
3.8-17VGA场同步时序

可以看出vys也是一个周期性地进行高低变化的脉冲,本设计中图像分辨率选定为640*480,因此使用表3.8- 5中的640*480分辨率的相应参数。同步脉冲a的时间是2行,显示后沿b的时间是33行,显示时序c的时间是480行,显示前沿的时间是10行,共计525行。这里需要注意,行的单位为“基准时钟”,前面的设计中使用计数器cnt0表示一个基准时钟,cnt1表示一行。由于场同步信号是的单位是“行”,因此可以使用cnt1来辅助完成场同步信号的表示;cnt1计数结束则代表一“行”结束。
在场同步信号中补充时间信号,得到带有时间信号的时序图如下所示。
18.png
3.8-18带时间信息的VGA场同步时序

分析时序图发现,若要产生这一时序还需要1个计数器,将产生这一时序的计数器命名为cnt2。前文强调过vys的单位是行,因此该计数器是用来计数行的数量。因此cnt2的加1条件是一行结束,前文定义了cnt1代表一行结束,因此计数器cnt2的加1条件即为“end_cnt1”,可写成:assign add_cnt2 = end_cnt1。分析时序图可以知道,该计数器的周期为525。将cnt2的设计翻译为代码表示,继续调用至简设计法模板,在命令模式下输入“:Mdyjsq”,点击回车,调出了对应模板,将“add_cnt1”和“end_cnt1”补充完整,得到该计数器的代码如下:
1
  
2
  
3
  
4
  
5
  
6
  
7
  
8
  
9
  
10
  
11
  
12
  
13
always  @(posedge clk or negedge rst_n)begin
  
    if(!rst_n)begin
  
        cnt2 <= 0;
  
    end
  
    else if(add_cnt2)begin
  
        if(end_cnt2)
  
            cnt2 <= 0;
  
        else
  
            cnt2 <= cnt2 + 1;
  
    end
  
end
  
  
assign  add_cnt2 = end_cnt1;
  
assign  end_cnt2 = add_cnt2 && cnt2==525-1 ;

确定了计数器cnt2,则vys信号的设计就有了对齐的对象。从时序图可以看出vys有两个变化点,一个是cnt2数到2个时,信号值由01;另一个是当cnt2数到525个时,信号值由10。下面将vys信号翻译成代码,在编辑模式下输入“Shixu2”,调用至简设计法模板后将其补充完整,得到场同步信号的代码如下:
1
  
2
  
3
  
4
  
5
  
6
  
7
  
8
  
9
  
10
  
11
always  @(posedge clk or negedge rst_n)begin
  
    if(!rst_n)begin
  
vys<=  1'b0;
  
    end
  
    else if(add_cnt2 &&cnt2 == 2 -  1)begin
  
vys<=  1'b1;
  
    end
  
    else if(end_cnt2)begin
  
vys<=  1'b0;
  
    end
  
end

最后还需要设计lcd_rgb信号。设计目标中可知显示器需要显示的颜色为红色,即lcd_rgb输出的值为“16’b11111_000000_00000”。这里一定要注意,设计目标需要在显示区域才能将该值赋给lcd_rgb,在其他区域要将lcd_rgb的值设定为0

前文确定VGA背景的时候有说明过显示区域如何确定,场同步信号处于显示区域且行同步信号也处于显示区域时才是真正的显示区域,而其他区域中红、绿、蓝基色都应赋值为低电平时,从而实现VGA颜色显示。

因此本设计中的显示区域即为场同步信号vys和行同步信号hys都处于“显示区域c”阶段。结合时序图可知,当cnt1大于(96+48)并且小于(96+48+640),cnt2大于(2+33)并且小于(2+33+480)时,两个信号都处于“显示区域”。

综上所述,lcd_rgb的信号输出为,当在显示区域时输出“16’b11111_000000_00000”,否则输出0,将其翻译成代码如下所示:
1
  
2
  
3
  
4
  
5
  
6
  
7
  
8
  
9
  
10
  
11
always  @(posedge clk or negedge rst_n)begin
  
    if(rst_n==1'b0)begin
  
lcd_rgb<=  16'h0;
  
    end
  
    else if((cnt1>=(96+48)  &&cnt1<(96+48+640))&& (cnt2>=(2+33)  &&cnt2<(2+33+480)))begin
  
lcd_rgb<= 16'b11111_000000_00000;
  
    end
  
    else begin
  
lcd_rgb<=  0;
  
    end
  
end

至此,主体程序已经完成,接下来带领同学们将module补充完整。如果充分理解了VGA的工作原理,就会发现利用FPGA完成VGA图像显示还是非常容易的。截止到这里如果有读者还存在不能完全理解的部分,建议可以认真学习本设计的项目背景章节,更加透彻的学习一下VGA的原理。将原理理解透彻后,在进行后续几个案例的设计时也会觉得更加轻松。

3.3 信号定义
接下来将module补充完整,首先来定义信号类型。再次强调,在进行regwire的判断的时候,总容易存在多余的联想,比如认为reg就是寄存器,wire是线;或者认为reg的会综合成寄存器,wire不会综合成寄存器。但是这些其实和reg型还是wire型都是并无关系,在信号类型的判断时不需要做任何的联想,只要记住一个规则“用always实现的是reg型,其他都是wire型”就可以了。

cnt0是用always产生的信号,因此类型为regcnt0计数的最大值为1,需要用1根线表示,即位宽是1位。
add_cnt0end_cnt0都是用assign方式设计的,因此类型为wire。其值是0或者1,用1根线表示即可。
编辑模式下输入“Reg1”“Wire1”可调用至简设计法模板,补充完整后得到代码如下:
1
reg    [0:0]           cnt0  ;
  
wire                   add_cnt0;
  
wire                   end_cnt0;

cnt1是用always产生的信号,因此类型为regcnt1计数的最大值为800,那如何确定该值对应的位宽是多少呢?至简设计法在这里分享一个非常实用的技巧,打开计算器,点击“查看”,选择“程序员”模式,在“十进制”下将信号值输入进去,就会获得对应的信号位宽。利用这一方法将cnt1的最大计数器800输入到计算器中,如下图所示,可以看出其位宽为10。本设计的数位比较小,这种方法在后续遇到比较大的数字时会方便很多,也不容易出错。

19.png
3.8-19通过计算器获取信号位宽

add_cnt1end_cnt1都是用assign方式设计的,因此类型为wire,其值是0或者11个线表示即可。编辑模式下输入“Wire1”调用至简设计法模板,补充完整后得到代码如下:
1
reg    [9:0]           cnt1  ;
  
wire                   add_cnt1;
  
wire                   end_cnt1;

cnt2是用always产生的信号,因此类型为regcnt2计数的最大值为525,需要用10根线表示,即位宽是10位。

add_cnt2end_cnt2都是用assign方式设计的,因此类型为wire,并且其值是0或者1,用1根线表示即可。编辑模式下输入“Wire1”调用至简设计法模板,补充完整后得到代码如下:
  
reg    [9:0]            cnt2  ;
  
wire                   add_cnt2;
  
wire                   end_cnt2;
  

lcd_rgb是用always方式设计的,因此类型为reg,其位宽是16位,16根线表示即可。编辑模式下输入“Reg16”调用至简设计法模板,补充完整后得到代码如下:
1
reg    [15:0]          lcd_rgb;

hysvys是用always方式设计的,因此类型为reg,其值是01,需要1根线表示。编辑模式下输入“Reg1”调用至简设计法模板,补充完整后得到代码如下:
1
  
2
reg                    hys  ;
  
reg                    vys  ;

至此,整个代码的设计工作已经完成。回顾一下本工程的整个代码设计,可以发现一共需要3个计数器,这里同样分享一个至简设计法代码模板,在“GVIM”中使用快捷命令“Jsq3”可以调出3个计数器的模板,调出的“Jsq3”模板如下图所示,补充完整后可以得到完整的计数器代码。
20.png
3.8-20至简设计法调用3个计数器模板

最终得到整个工程的代码如下:
1
  
2
  
module color_exec1(
  
clk   ,
  
rst_n   ,
  
lcd_hs   ,
  
lcd_vs   ,
  
lcd_rgb
  
     );
  
  
input                   clk  ;
  
input                   rst_n  ;
  
output                  lcd_hs  ;
  
output                  lcd_vs  ;
  
output   [15:0]          lcd_rgb       ;
  
  
reg     [0:0]           cnt0  ;
  
wire                   add_cnt0;
  
wire                   end_cnt0;
  
  
reg     [9:0]           cnt1  ;
  
wire                   add_cnt1;
  
wire                   end_cnt1;
  
reg     [9:0]            cnt2  ;
  
wire                   add_cnt2;
  
wire                   end_cnt2;
  
reg     [15:0]     lcd_rgb;
  
  
reg                    hys  ;
  
reg                    vys  ;
  
  
always @(posedge clk or negedge  rst_n)begin
  
     if(!rst_n)begin
  
         cnt0 <= 0;
  
     end
  
     else if(add_cnt0)begin
  
         if(end_cnt0)
  
            cnt0 <= 0;
  
         else
  
            cnt0 <= cnt0 + 1;
  
     end
  
end
  
  
assign add_cnt0 = 1;
  
assign end_cnt0 = add_cnt0 &&  cnt0== 2 -1;
  
  
always @(posedge clk or negedge  rst_n)begin
  
     if(!rst_n)begin
  
         cnt1 <= 0;
  
     end
  
     else if(add_cnt1)begin
  
         if(end_cnt1)
  
            cnt1 <= 0;
  
         else
  
            cnt1 <= cnt1 + 1;
  
     end
  
end
  
  
assign add_cnt1 = end_cnt0;
  
assign end_cnt1 = add_cnt1 &&  cnt1==800-1 ;
  
  
always @(posedge clk or negedge  rst_n)begin
  
     if(!rst_n)begin
  
         cnt2 <= 0;
  
     end
  
     else if(add_cnt2)begin
  
         if(end_cnt2)
  
            cnt2 <= 0;
  
         else
  
            cnt2 <= cnt2 + 1;
  
     end
  
end
  
  
assign add_cnt2 = end_cnt1;
  
assign end_cnt2 = add_cnt2 &&  cnt2==525-1 ;
  
  
always   @(posedge clk or negedge rst_n)begin
  
    if(rst_n==1'b0)begin
  
hys<= 0;
  
     end
  
     else if(add_cnt1 && cnt1==96-1)begin
  
hys<= 1;
  
     end
  
     else if(end_cnt1)begin
  
hys<= 0;
  
     end
  
end
  
  
always   @(posedge clk or negedge rst_n)begin
  
     if(!rst_n)begin
  
vys<= 1'b0;
  
     end
  
     else if(add_cnt2 && cnt2 == 2 - 1)begin
  
vys<= 1'b1;
  
     end
  
     else if(end_cnt2)begin
  
vys<= 1'b0;
  
     end
  
end
  
  
always   @(posedge clk or negedge rst_n)begin
  
     if(rst_n==1'b0)begin
  
lcd_rgb<= 16'h0;
  
     end
  
     else if((cnt1>=(96+48) &&cnt1<(96+48+640))&&  (cnt2>=(2+33) &&cnt2<(2+33+480)))begin
  
lcd_rgb<= 16'b11111_111111_00000;
  
     end
  
     else begin
  
lcd_rgb<= 0;
  
     end
  
end
  
  
endmodule
下一步是新建工程和上板查看现象。

第4节 综合与上板


4.1 新建工程
打开软件Quartus Ⅱ,点击“File”下拉列表中的New ProjectWzard...新建工程选项,如下图所示。
21.png
3.8-21Quartus新建工程
随后会出现Quartus新建工程介绍,如下图所示,直接点击“Next”。
22.png
3.8-22Quartus新建工程介绍

此时会出现工程文件夹、工程名、顶层模块名设置界面,如图3.8- 23所示。设置目录为:D:/mdy_book/color_exec1,工程名和顶层名为color_exec1。再次强调,为了避免初学者在后续操作中发生程序跳出未知错误的问题,强烈建议设置的文件目录和工程名称与本书保持一致。设置完成后点击“Next”。
23.png
3.8-23QUARTUS新建工程设置名称

新建工程类型设置如下图所示,选择“Emptyproject”,然后点击“Next”。
24.png
3.8-24QUARTUS新建工程类型

接下来进行文件添加,其界面如下图所示。点击右侧的“Add”按钮,选择之前写好的“color_exec1.v”文件,可以看到界面下方会显示出文件,之后点击“Next”。
25.png
3.8-25QUARTUS添加文件

3.8- 26为芯片选择页面,选择“Cyclone E”,在芯片型号选择处选择“EP4CE15F23C8”,之后点击“Next”。
26.png
3.8-26QUARTUS选择芯片型号

3.8- 27QUARTUS设置工具界面,,不必做任何修改,直接点击“Next”。
27.png
3.8-27QUARTUS设置工具界面
下图为QUARTUS新建工程汇总界面,可以看到新建工程的汇总情况,点击“Finish”完成新建工程。
28.png
3.8-28QUARTUS新建工程汇总界面
4.2 综合
新建工程步骤完成后,就会出现如下图所示的 QUARTUS新建工程后界面
29.png
3.8-29QUARTUS新建工程后界面
点击编译按钮,可以对整个工程进行编译。编译成功的界面如下图所示。
30.png
3.8-30QUARTUS编译后界面
4.3 配置管脚
下面需要对相应管脚进行配置。如下图所示,在菜单栏中选中“Assignments”,然后选择“Pin Planner”,随后会弹出配置管脚的窗口。
31.png
3.8-31QUARTUS配置管脚选项

在配置窗口最下方中的“location”一列,参考信号和管脚关系,按照表3.8- 6中最右两列配置好FPGA管脚,配置管理来源参见管脚配置环节,最终配置的结果如图3.8-32。配置完成后,关闭Pin Planner,软件自动会保存管脚配置信息。
3.8 - 6信号和管脚关系
  
器件
  
信号线
信号线
FPGA管脚
内部信号
U6,U7
SEG_E
SEG0
Y6
seg_ment[2]
SEG_DP
SEG1
W6
未用到
SEG_G
SEG2
Y7
seg_ment[0]
SEG_F
SEG3
W7
seg_ment[1]
SEG_D
SEG4
P3
seg_ment[3]
SEG_C
SEG5
P4
seg_ment[4]
SEG_B
SEG6
R5
seg_ment[5]
SEG_A
SEG7
T3
seg_ment[6]
DIG1
DIG_EN1
T4
seg_sel[0]
DIG2
DIG_EN2
V4
seg_sel[1]
DIG3
DIG_EN3
V3
seg_sel[2]
DIG4
DIG_EN4
Y3
seg_sel[3]
DIG5
DIG_EN5
Y8
seg_sel[4]
DIG6
DIG_EN6
W8
seg_sel[5]
DIG7
DIG_EN7
W10
seg_sel[6]
DIG8
DIG_EN8
Y10
seg_sel[7]
X1
SYS_CLK
G1
clk
K1
SYS_RST
AB12
rst_n

32.png
3.8-32 QUARTUS配置管脚
4.4 再次综合
再次打开“QUARTUS”软件,在菜单栏中选中“Processing”,然后选择“StartCompilation”,再次对整个工程进行编译和综合,如图3.8- 33所示。
33.png
3.8-33QUARTUS编译选项

当出现如下图所示的QUARTUS编译成功标志,则说明编译综合成功。
34.png
3.8-34QUARTUS编译成功标志

4.5 连接开发板
完成编译后开始进行上板调试操作,按照下图的方式将下载器接入电脑USB接口,接上开发板电源,将开发板的VGA口连接到一台显示器上,然后按下下方蓝色开关,硬件连接完毕。
35.png
3.8-35开发板连接图

4.6 上板
打开QUARTUS界面,单击界面中的“ 35,.png ”则会弹出配置界面。在界面中点击“add file”添加“.sof”文件后点击“Start”,会在“Progress”出现显示进度。
36.png
3.8-36QUARTUS界面
QUARTUS下载程序界面如下图所示,当进度条到100%提示成功后,即可在显示器上观察到相应的现象。
37.png
3.8-37QUARTUS下载程序界面

进度条提示成功后,如果操作无误此时可以在显示器上看到640*480像素的红色显示区域。如果没有显示成功,就需要返回检查一下连接是否到位,代码是否编写正确。如果无法自己完成错误排查的话,可以重新按照步骤操作一遍,相信一定会达到想要的效果。

第5节 简化版步骤分享
这里依旧会分享简化版的步骤,方便掌握基础原理后进行反复操作复习。

5.1 设计实现


5.1.1顶层信号
新建目录:D:\mdy_book\color_exec1。在该目录中,新建一个名为color_exec1.v的文件,用GVIM打开后开始编写代码。
确定顶层信号,信号和管脚的对应关系见表3.8- 6
3.8 - 6信号和管脚关系
  
器件
  
电阻网络转换后
  
信号线
信号线
FPGA管脚
FPGA工程信号
CN1
VGA_RED
VGA_R4
E11
lcd_rgb[15]
VGA_R3
C10
lcd_rgb[14]
VGA_R2
D10
lcd_rgb[13]
VGA_R1
E9
lcd_rgb[12]
VGA_R0
E10
lcd_rgb[11]
VGA_GREEN
VGA_G5
D15
lcd_rgb[10]
VGA_G4
C17
lcd_rgb[9]
VGA_G3
C19
lcd_rgb[8]
VGA_G2
E12
lcd_rgb[7]
VGA_G1
C13
lcd_rgb[6]
VGA_G0
E15
lcd_rgb[5]
VGA_BLUE
VGA_B4
D13
lcd_rgb[4]
VGA_B3
E13
lcd_rgb[3]
VGA_B2
D17
lcd_rgb[2]
VGA_B1
E16
lcd_rgb[1]
VGA_B0
C15
lcd_rgb[0]
VGA_HSYNC
VGA_HSYNC
C20
hys
VGA_VSYNC
VGA_VSYNC
D20
vys
X1
SYS_CLK
G1
clk
K1
SYS_RST
AB12
rst_n
写出顶层信号代码:
1
  
2
  
3
  
4
  
5
  
6
  
7
module color_exec1(
  
clk   ,
  
rst_n   ,
  
lcd_hs   ,
  
lcd_vs   ,
  
lcd_rgb
  
     );
声明输入输出属性:
1
  
2
  
3
  
4
  
5
input                   clk  ;
  
input                   rst_n  ;
  
output                  lcd_hs  ;
  
output                  lcd_vs  ;
  
output   [15:0]          lcd_rgb       ;
5.1.2信号设计
首先进行架构设计。设计目标中确定显示器中需要显示640*480分辨率的图像,因此使用下表中的第一种分辨率:

3.8-5常见分辨率参数
  
分辨率
  
行/列
同步脉冲
显示后沿
显示区域
显示前沿
帧长
单位
640*480
  
/60Hz
96
48
640
16
800
基准时钟
2
33
480
10
525
800*600
  
/72Hz
120
64
800
56
1040
基准时钟
6
23
600
37
666
800*600
  
/60Hz
128
88
800
40
1056
基准时钟
4
23
600
1
628
1024*768
  
/60Hz
136
160
1024
24
1344
基准时钟
6
29
768
3
806

根据设计目标得到VGA行同步时序图如下所示:
45.png
3.8-15带时间信息的VGA行同步时序

设计计数器架构,表示计数基准时间的计数器cnt0代码如下:
1
  
2
  
3
  
4
  
5
  
6
  
7
  
8
  
9
  
10
  
11
  
12
  
13
always @(posedge clk or negedge  rst_n)begin
  
     if(!rst_n)begin
  
         cnt0 <= 0;
  
     end
  
     else if(add_cnt0)begin
  
         if(end_cnt0)
  
            cnt0 <= 0;
  
         else
  
            cnt0 <= cnt0 + 1;
  
     end
  
end
  
  
assign add_cnt0 = 1;
  
assign end_cnt0 = add_cnt0 &&  cnt0== 2 -1;

表示计数hys长度的计数器cnt1代码如下:
1
  
2
  
3
  
4
  
5
  
6
  
7
  
8
  
9
  
10
  
11
  
12
  
13
always @(posedge clk or negedge  rst_n)begin
  
     if(!rst_n)begin
  
         cnt1 <= 0;
  
     end
  
     else if(add_cnt1)begin
  
         if(end_cnt1)
  
            cnt1 <= 0;
  
         else
  
            cnt1 <= cnt1 + 1;
  
     end
  
end
  
  
assign add_cnt1 = end_cnt0;
  
assign end_cnt1 = add_cnt1 &&  cnt1==800-1 ;

设计行同步信号的代码如下:
1
  
2
  
3
  
4
  
5
  
6
  
7
  
8
  
9
  
10
  
11
always   @(posedge clk or negedge rst_n)begin
  
     if(rst_n==1'b0)begin
  
hys<= 0;
  
     end
  
     else if(add_cnt1 && cnt1==96-1)begin
  
hys<= 1;
  
     end
  
               else if(end_cnt1)begin
  
hys<= 0;
  
     end
  
end

设计VGA场同步时序计数器cnt2代码如下:
1
  
2
  
3
  
4
  
5
  
6
  
7
  
8
  
9
  
10
  
11
  
12
  
13
always @(posedge clk or negedge  rst_n)begin
  
     if(!rst_n)begin
  
         cnt2 <= 0;
  
     end
  
     else if(add_cnt2)begin
  
         if(end_cnt2)
  
            cnt2 <= 0;
  
         else
  
            cnt2 <= cnt2 + 1;
  
     end
  
end
  
  
assign add_cnt2 = end_cnt1;
  
assign end_cnt2 = add_cnt2 &&  cnt2==525-1 ;

场同步信号的代码如下:
1
  
2
  
3
  
4
  
5
  
6
  
7
  
8
  
9
  
10
  
11
always   @(posedge clk or negedge rst_n)begin
  
     if(!rst_n)begin
  
vys<= 1'b0;
  
     end
  
     else if(add_cnt2 &&cnt2 == 2 - 1)begin
  
vys<= 1'b1;
  
     end
  
     else if(end_cnt2)begin
  
vys<= 1'b0;
  
     end
  
end

设计lcd_rgb信号代码如下:
1
  
2
  
3
  
4
  
5
  
6
  
7
  
8
  
9
  
10
  
11
always   @(posedge clk or negedge rst_n)begin
  
     if(rst_n==1'b0)begin
  
lcd_rgb<= 16'h0;
  
     end
  
     else if((cnt1>=(96+48) &&cnt1<(96+48+640))&&  (cnt2>=(2+33) &&cnt2<(2+33+480)))begin
  
lcd_rgb<= 16'b11111_000000_00000;
  
     end
  
     else begin
  
lcd_rgb<= 0;
  
     end
  
end

至此,主体程序已经完成,接下来将module补充完整。

5.1.3信号定义
首先定义信号类型,cnt0add_cnt0 end_cnt0的信号定义如下:
1
reg     [0:0]           cnt0  ;
  
wire                   add_cnt0;
  
wire                   end_cnt0;

cnt1add_cnt1end_cnt1的信号定义如下:
1
reg     [9:0]           cnt1  ;
  
wire                   add_cnt1;
  
wire                   end_cnt1;
cnt2add_cnt2end_cnt2的信号定义如下:
  
reg     [9:0]            cnt2  ;
  
wire                   add_cnt2;
  
wire                   end_cnt2;
  
lcd_rgb的信号定义如下:
1
reg     [15:0]          lcd_rgb;
hysvys的信号定义如下:
1
  
2
reg                    hys  ;
  
reg                    vys  ;
至此,整个代码的设计工作已经完成。最终得到完整的设计代码如下:
1
  
2
  
module color_exec1(
  
clk   ,
  
rst_n   ,
  
lcd_hs   ,
  
lcd_vs   ,
  
lcd_rgb
  
     );
  
  
input                   clk  ;
  
input                   rst_n  ;
  
output                  lcd_hs  ;
  
output                  lcd_vs  ;
  
output   [15:0]          lcd_rgb       ;
  
  
reg     [0:0]           cnt0  ;
  
wire                   add_cnt0;
  
wire                   end_cnt0;
  
  
reg     [9:0]           cnt1  ;
  
wire                   add_cnt1;
  
wire                   end_cnt1;
  
reg     [9:0]            cnt2  ;
  
wire                   add_cnt2;
  
wire                   end_cnt2;
  
reg     [15:0]     lcd_rgb;
  
  
reg                    hys  ;
  
reg                    vys  ;
  
  
always @(posedge clk or negedge  rst_n)begin
  
     if(!rst_n)begin
  
         cnt0 <= 0;
  
     end
  
     else if(add_cnt0)begin
  
         if(end_cnt0)
  
            cnt0 <= 0;
  
         else
  
            cnt0 <= cnt0 + 1;
  
     end
  
end
  
  
assign add_cnt0 = 1;
  
assign end_cnt0 = add_cnt0 &&  cnt0== 2 -1;
  
  
always @(posedge clk or negedge  rst_n)begin
  
     if(!rst_n)begin
  
         cnt1 <= 0;
  
     end
  
     else if(add_cnt1)begin
  
         if(end_cnt1)
  
            cnt1 <= 0;
  
         else
  
            cnt1 <= cnt1 + 1;
  
     end
  
end
  
  
assign add_cnt1 = end_cnt0;
  
assign end_cnt1 = add_cnt1 &&  cnt1==800-1 ;
  
  
always @(posedge clk or negedge  rst_n)begin
  
     if(!rst_n)begin
  
         cnt2 <= 0;
  
     end
  
     else if(add_cnt2)begin
  
         if(end_cnt2)
  
            cnt2 <= 0;
  
         else
  
            cnt2 <= cnt2 + 1;
  
     end
  
end
  
  
assign add_cnt2 = end_cnt1;
  
assign end_cnt2 = add_cnt2 &&  cnt2==525-1 ;
  
  
always   @(posedge clk or negedge rst_n)begin
  
     if(rst_n==1'b0)begin
  
hys<= 0;
  
     end
  
     else if(add_cnt1 && cnt1==96-1)begin
  
hys<= 1;
  
     end
  
     else if(end_cnt1)begin
  
hys<= 0;
  
     end
  
end
  
  
always   @(posedge clk or negedge rst_n)begin
  
     if(!rst_n)begin
  
vys<= 1'b0;
  
     end
  
     else if(add_cnt2 && cnt2 == 2 - 1)begin
  
vys<= 1'b1;
  
     end
  
     else if(end_cnt2)begin
  
vys<= 1'b0;
  
     end
  
end
  
  
always   @(posedge clk or negedge rst_n)begin
  
     if(rst_n==1'b0)begin
  
lcd_rgb<= 16'h0;
  
     end
  
     else if((cnt1>=(96+48) &&cnt1<(96+48+640))&&  (cnt2>=(2+33) &&cnt2<(2+33+480)))begin
  
lcd_rgb<= 16'b11111_111111_00000;
  
     end
  
     else begin
  
lcd_rgb<= 0;
  
     end
  
end
  
  
endmodule
下一步是新建工程和上板查看现象。

5.2 综合与上板


5.2.1新建工程
打开软件Quartus Ⅱ,点击“File”下拉列表中的New ProjectWzard...新建工程选项。
51.png
3.8-21Quartus新建工程
直接点击“Next”。
52.png
3.8-22Quartus新建工程介绍
此时出现的是工程文件夹、工程名、顶层模块名设置界面(目录为:D:/mdy_book/color_exec1,工程名和顶层名为color_exec1),完成设置后点击“Next”。
53.png
3.8-23QUARTUS新建工程设置名称
选择“Empty project”后点击“Next”。
54.png
3.8-24QUARTUS新建工程类型
点击右侧的“Add”按钮,选择“color_exec1.v”文件后点击“Next”,完成文件添加。
55.png
3.8-25QUARTUS添加文件
对芯片型号进行选择,在“Device family”选项中选择“Cyclone E”,“Available devices”选项中选择“EP4CE15F23C8”,随后点击“Next”。
56.png
3.8-26QUARTUS选择芯片型号
直接点击“Next”。
57.png
3.8-27QUARTUS设置工具界面
点击“Finish”,完成新建工程。
58.png
3.8-28QUARTUS新建工程汇总界面
5.2.2综合
新建工程后界面如下图所示,点击“编译”。
59.png
3.8-29QUARTUS新建工程后界面
编译成功如下图所示。
60.png
3.8-30QUARTUS编译后界面
5.3 配置管脚
进行管脚配置,在菜单栏中点击“Assignments”后点击“Pin Planner”,此时会弹出配置管脚的窗口。
61.png
3.8-31 QUARTUS配置管脚选项
在配置窗口“location”根据信号和管脚关系配置管脚,配置完成关闭“Pin Planner”即可自动保存配置信息。
62.png
3.8-32QUARTUS配置管脚
5.3.1再次综合
在此打开“QUARTUS”软件,在菜单栏中选择“Processing”,随后点击“StartCompilation”再次进行综合。
63.png
3.8-33QUARTUS编译选项
出现 QUARTUS 编译成功标志时表示此次编译成功。
64.png
3.8-34QUARTUS编译成功标志
5.3.2连接开发板
下载器接入电脑 USB 接口,将开发板接上电源,开发板的VGA口连接到一台显示器上后按下蓝色开关。
65.png
3.8-35开发板连接图
5.3.3上板
打开 QUARTUS 界面后单击“ 65,.png ”图标:
66.png
3.8-36QUARTUS界面

点击“add file”,添加.sof文件完成添加后点击“Start”,在“Progress”中会显示进度,当进度条显示“100%”为成功,可观察显示器现象。
67.png
3.8-37QUARTUS下载程序界面

第6节 扩展练习
至此,VGA显示颜色设计已经完成,相信同学们已经可以完全掌握这一设计。那么在掌握这项工程后可以多做一些思考,尝试在工程原理不变的基础上进行一定的数据调整,试着改变图像显示区域或者改变显示颜色等参数,挑战一下独立完成多个设计。也欢迎有更多思路和想法的同学前往至简设计法论坛进行交流讨论。






上一篇:【FPGA至简设计原理与应用】书籍连载16 第三篇 FPGA至简设计项目 第七章 数字时钟设计
下一篇:【FPGA至简设计原理与应用】书籍连载18 第三篇 FPGA至简设计项目 第九章 VGA显示矩阵
MP801开发板 网络培训班 就业培训班 FPGA学习资料
吴老师18022857217

0

主题

2

帖子

69

积分

注册会员

Rank: 2

积分
69
QQ
发表于 2020-6-2 16:06:56 来自手机 | 显示全部楼层
Nice,终于等着VGA这一章了,一直没太搞懂vga这一部分,期待看完明德扬vga的收获哈哈哈

0

主题

6

帖子

90

积分

注册会员

Rank: 2

积分
90
发表于 2021-2-2 14:52:42 | 显示全部楼层
非常好的课程,总是将设计目标解释的很清楚,拆分成很明确的步骤,最重要是学到了很多硬件方面的知识!
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则


QQ|手机版|小黑屋|MDYBBS ( 粤ICP备16061416号-1

GMT+8, 2021-3-2 06:45 , Processed in 0.632638 second(s), 17 queries , File On.

Powered by Discuz! X3.4

本论坛由广州健飞通信有限公司所有

© 2001-2019 Comsenz Inc.

快速回复 返回顶部 返回列表