VGA显示图片-明德扬至简设计与应用FPGA

发布时间:2018年11月27日 11:11    发布者:luckyb1

1     项目背景 (至简设计交流群:544453837) 1.1   FPGA存储器

       目前大多数FPGA都有内嵌的块RAM(Block RAM),可以将其灵活地配置成单端口RAM(DPRAM,Single Port RAM)、双端口RAM(DPRAM,Double Ports RAM)、伪双端口RAM(PseudoDPRAM)、CAM(Content AddressableMemory)、FIFO等常用存储结构。FPGA中其实并没有专用的ROM硬件资源,实现ROM的思路是对RAM赋予初值,并保持该初值。

       Altera 的器件内部提供了各种存储器模块(RAM、ROM 或双口 RAM),可以在设计中使用 MegaWizardPlug-In Manager,执行【Tools】|【MegaWizard Plug-InManager】菜单命令来创建所需要 的存储器模块。也可以使用 Altera 提供的宏功能模块 LPM_ROM 来创建存储器模块。每个 ROM 模块有 CLOCK(时钟)、address(地址)这两个输入信号和一个 q(值)输出信号。 ROM 在每个时钟上升沿取出由地址信号所指定的存储单元中的值并输出。ROM 内的值通过加载 MIF (Memory Initialization File,存储器初始化文件)文件来实现。

      当在设计中使用了器件内部的存储器模块时,需要对存储器模块进行初始化。在Quartus 中, 可以使用两种格式的存储器初始化文件:Intel Hex 格式(.hex)或Altera 存储器初始化格式(.mif)的文件。 MIF 文件是 Altera 存储器类器件初始化的专用文件格式,文件内容为地址与值的对应表,规定了 存储器单元的初始值。

      如果将要存储于 ROM 中的内容比较少或者很有规律,可以执行【File】|【New…】菜单命令,创 建 MIF 文件并编辑其内容。 如果已经有 BMP 格式的图片,则可以使用我们提供的 BmpToMif 这个软件,从现有的 BMP 格式 图片生成 MIF 文件。其使用非常简单,注意要适当调整原图片的大小,这可以通过各种图形编辑软件修改,如 Windows 自带的画图程序、Photoshop 等。 BmpToMif 软件的功能有: 将 bmp 图片转为 mif 文件:将黑白图片转换为单色 mif 文件;将彩色图片转换为三色 mif 文件。

将二进制文件转为 mif 文件,如将中英文点阵字库转换为 mif 文件。

1.2 图片变成MIF文件的方法

      我们需要用到Img2Lcd软件,此软件可自行下载,无需安装即可使用。




      打开软件后,点击右上角“打开”按钮,选择需要转化的图片,在右侧输出数据类型下选择“c语言数组”,扫描模式为“水平扫描”,输出灰度“256色”,最大宽度和高度应大于图片的大小,由于我们示例的图片为120*60,最大宽度和高度我们选择240*240。其他选项可以不用管。




      之后点击“保存”,文件名为“1.c”,在点击下方保存。




      用gvim打开生成的文件,箭头处为数据个数,由于图片是120*60的,输出灰度为256色,所以就有7200个数据。




      第一行是不需要的,先删除掉,然后进入命令模式,输入“/,”,然后回车,就可以选中所有的逗号。




      将光标置于第一个字符,在命令模式下一次输入“q、a”,调用gvim的录制功能,然后输入“n”跳转到第一个逗号,进入编辑模式,将光标置于第一个逗号的右侧,然后回车,接着摁4下退格键删除多余的空格,在进入命令模式,输入“q”结束录制。




      在命令模式下输入“7198@a”,表示重复之前的录制7198次,然后回车。




      然后我们要删除多余的间隔,返回到第一个字符,在命令模式下输入“qa”进入录制,然后向下移动16格,刚好到没有数据的一行,输入“dd”进行删除,然后“q”退出录制。在命令模式下输入“954@a”,即可完成删除。




      删除掉中间的OX




      在命令模式下输入“:%s/0X/: /”回车将“0X”替换为冒号,输入“:%s/,/; /”回车将“,”替换为“;”。







       打开一个空白gvim,输入1,进入命令模式选中,输入“yy7199p”将1复制7199份,将光标至于第一个1处,进入命令模式,输入“:leti=1|g/1/s//\=i/|let i=i+1”让其递增。







       选中1~7199,然后Ctrl+Q,进行列操作,然后Ctrl+c复制,然后回到1.c文件中,命令模式下,将光标置于第二行最前,然后Ctrl+v粘贴。



      将第一个数据的地址标为0,然后在最上方和结尾添加如下图所示内容。






      13、然后将“1.c”文件保存为“1.mif”格式的。

2     设计目标

      通过VGA连接线,将显示器和教学板的VGA接口相连。连接示意图如下。





      然后FPGA产生640*480分辨率(使用上表中的第一种分辨率),让显示器产生显示一幅图像。提示:显示器一般都会自适应功能,无须设置就能识别不同分辨率的图像。

      图像的内容是:在屏幕的中央显示一个明德扬的LOGO。明德扬LOGO的大小是120*60像素。除了图片之外的显示区域,则显示白色。上板效果图如下图所示(显示器不同,显示效果也会有差别,请注意)。



      rom1是一个宽度为16位,深度为8192的ROM,该ROM保存了明德扬的LOGO图片,其排列方式如下。



       该ROM按从左往右,由上往下的顺序保存了LOGO图像每个像素的值。图中的(y,x)表示的是第y行第x列的像素值。例如,地址0保存的是第1行第1列的像素值,地址1保存的是第1行第2列的像素值,地址119保存的是第1行第120列的像素值。接下来,地址120保存的是第2行第1列的像素值,以此类推,地址6599保存的是第55行120列的像素值。而大于6599的地址,保存的值是0,没有意义的数字。

      ROM的每一个像素,按RGB565的方式保存,也就是[15:11]表示红基色,[10:5]表示绿基色,[4:0]表示蓝基色。

3      设计实现3.1     顶层接口

      新建目录:D:\mdy_book\picture_new_borad。在该目录中,新建一个名为picture_new_borad.v的文件,并用GVIM打开,开始编写代码。

      我们要实现的功能,概括起来就是FPGA产生VGA时序,即控制VGA_R4~R0、VGA_G5~G0、VGA_B4~B0、VGA_HSYNC和VGA_VSYNC,让显示器显示红色。其中,VGA_HSYNC和VGA_VSYNC,FPGA可根据时序产生高低电平。而颜色数据,由于是固定的红色,FPGA也能自己产生,不需要外部输入图像的数据。那么我们的FPGA工程,可以定义输出信号hys表示行同步,用输出信号vys表示场同步,定义一个16位的信号lcd_rgb,其中lcd_rgb[15:11]表示VGA_R4~0,、lcd_rgb[10:5]表示VGA_G5~0,、lcd_rgb[4:0]表示VGA_B4~0。

      我们还需要时钟信号和复位信号来进行工程控制。

      综上所述,我们这个工程需要五个信号,时钟clk,复位rst_n,场同步信号vys、行同步信号hys和RGB输出信号lcd_rgb。



      将module的名称定义为picture_new_borad。并且我们已经知道该模块有五个信号:clk、rst_n、lcd_hs、lcd_vs和lcd_rgb。为此,代码如下:



       其中clk、rst_n是输入信号,lcd_hs、lcd_vs和lcd_rgb是输出信号,其中clk、rst_n、lcd_hs、lcd_vs的值是0或者1,一根线即可,lcd_rgb为16位位宽的,根据这些信息,我们补充输入输出端口定义。代码如下:



3.2  架构设计

      需要注意的是,输入进来的时钟clk是50MHz,而从分辨率参数表可知道,行单位的基准时钟是25MHz。为此我们需要根据50MHz来产生一个25 MHz的时钟,然后再用于产生VGA时序。

      为了得到这个25M时钟,我们需要一个PLL。PLL可以认为是FPGA内的一个硬核,它的功能是根据输入的时钟,产生一个或多个倍频和分频后的输出时钟,同时可以调整这些输出时钟的相位、占空比等。

      例如,输入进来是50M时钟,如果我需要一个100M时钟,那么从逻辑上、代码上是不可能产生的,我们就必须用到PLL来产生了。

      整个工程的结构图如下。



      PLL的生成方式过程,请看本案例的综合工程和上板一节的内容。

3.3VGA驱动模块设计3.3.1接口信号

      在目录:D:\mdy_book\ picture_new_borad中,建立一个rectangle.v文件,并用GVIM打开,开始编写代码。

     我们新建一个GVIM文件,并且将文件保存为vga_driver.v。

      我们先分析功能。要控制显示器,让其产生红色,也就是让FPGA控制VGA_R0~4、VGA_G0~5、VGA_B0~4、VGA_VSYNC和VGA_HSYNC信号。那么VGA驱动模块,可以定义输出信号hys表示行同步,用输出信号vys表示场同步,定义一个16位的信号lcd_rgb,其中lcd_rgb[15:11]表示VGA_R4~0,、lcd_rgb[10:5]表示VGA_G5~0,、lcd_rgb[4:0]表示VGA_B4~0。

      同时该模块的工作时钟为25M,同时需要一个复位信号。

     综上所述,我们这个模块需要五个信号,25M时钟clk,复位rst_n,场同步信号vys、行同步信号hys和RGB输出信号lcd_rgb。

     将module的名称定义为vga_driver。并且我们已经知道该模块有五个信号:clk、rst_n、hys、vys和lcd_rgb。为此,代码如下:



       其中clk、rst_n是输入信号,hys、vys和lcd_rgb是输出信号,其中clk、rst_n、hys、vys的值是0或者1,一根线即可,lcd_rgb为16位位宽的,根据这些信息,我们补充输入输出端口定义。代码如下:



3.3.2    信号设计

     我们先设计场同步信号hys,VGA时序中的场同步信号,其时序图如下:



       hys就是一个周期性地高低变化的脉冲。我们使用的是下表中的第一种分辨率,也就是同步脉冲a的时间是96个时钟周期,而显示后沿b是48个时钟周期,显示时序c是640个时钟周期,显示前沿是16个时钟周期,一共是800个时钟周期。



​      将时间信号填入图中,更新后的时序图如下:



      很显然,我们需要1个计数器来产生这个时序,我们将该计数器命名为h_cnt。由于hys是不停地产生的,那么h_cnt就是不停地计数,每个时钟都要计数器,所以认为该计数器的加1条件为“1”,可写成:assign add_h_cnt = 1。从上图可知,该计数器的周期是800。综上所述,该计数器的代码如下:​



​     有了计数器h_cnt,那么hys信号就有了对齐的对象。从时序图可以发现, hys有两个变化点,一个是h_cnt数到96个时,由0变1;另一个是当h_cnt数到800个时,由1变0。所以,场同步信号的代码如下:



     接下来设计vys信号。该信号的时序图如下所示。



    vys就是一个周期性地高低变化的脉冲。我们使用的是表中的第一种分辨率,查询表可知,同步脉冲a的时间是2行的时间,而显示后沿b是33行,显示时序c是480行,显示前沿是10行,一共是525行。其中,一“行”结束,也就是h_cnt数完了。

     将时间信号填入图中,更新后的时序图如下:




      很显然,我们还需要1个计数器来产生这个时序,我们将该计数器命名为v_cnt。该计数器是用来数有多少行的,所以加1条件就是一行结束,即end_h_cnt,可写成:assignadd_v_cnt = end_h_cnt。从上图可知,该计数器的周期是525。综上所述,该计数器的代码如下:



      有了计数器v_cnt,那么vys信号就有了对齐的对象。从时序图可以发现, vys有两个变化点,一个是v_cnt数到2个时,由0变1;另一个是当h_cnt数到525个时,由1变0。所以,场同步信号的代码如下:



​      最后我们还有一个信号需要设计,那就是lcd_rgb信号。



​      我们在显示器中一共要分成两种方式显示。如上图中,以屏幕中点为中心,左右60列、上27个行、下28行显示的是图像数据;而在其他区域直接显示白色,也就是lcd_rgb等于16’b11111_111111_11111。还要注意的是,在非显示区域,lcd_rgb的值要为0,才能正确显示。我们现在要仔细区分,在什么时候分别输出上面的值。




欢迎分享本文,转载请保留出处:http://www.eechina.com/thread-550594-1-1.html     【打印本页】
您需要登录后才可以发表评论 登录 | 立即注册

厂商推荐

关于我们  -  服务条款  -  使用指南  -  站点地图  -  友情链接  -  联系我们
电子工程网 © 版权所有   京ICP备16069177号 | 京公网安备11010502021702
回顶部