VGA系列之一:VGA显示网络图片

一休哥是在读研究生的时候开始正式接触FPGA的,之所以这么说呢,是因为之前本科参加电赛的时候也学过一点FPGA的知识,可惜学习周期太短导致那次电赛惨败。可能世上就是有这么巧的事,刚上研究生的第一天,老板就给了我一块FPGA板,让我自己玩去,从此就踏上了这条不归路。

好了,闲话不多说,接下来我们来讲讲如何用FPGA实现VGA显示网络图片。这里我们先提出几个问题,通过解决这几个问题,从而实现工程效果。

1、  如何用FPGA实现VGA显示

2、  网络图片和VGA显示有何区别

3、  VGA如何显示图片

1 如何用FPGA实现VGA显示

首先,我们需要清楚一个概念,VGA是一个外设模块,VGA模块有两个接口,一个接口用于连接FPGA,另一个用于连接VGA线,VGA线的另一头我们常常会与显示器相连。连接VGA线的接口一般都是一个标准VGA接口的母口。而与FPGA相连的接口有很多种,在这里,我介绍其中的一种。

上图就是VGA模块的硬件电路,可以看到,在图片的左边就是与FPGA相连的接口信号,一共有29个信号,我们可以大致分成三类,

  • 恒定不变的信号(VGA_BLANK,VGA_SYN)
  • 时序控制信号(VGA_CLK,VGA_HS,VGA_VS)
  • 数据信号(VGA_R0-7,VGA_G0-7,VGA_B0-7)

在使用VGA显示时我们需要将VGA_BLANK默认置1,VGA_SYNC默认置0。VGA_CLK是VGA显示的主时钟,它的频率决定了VGA显示的分辨率。VGA_HS是VGA的水平同步信号,它决定了VGA显示的宽度。VGA_VS是VGA的垂直同步信号,它决定了VGA显示的高度。数据信号中R、G、B三种颜色的小大都是8位,所以这个VGA模块显示的颜色深度为24位,也就是说VGA显示的是24位真彩色,显示的质量非常高。

刚才提到过VGA显示的分辨率、宽度和高度。我们需要知道,VGA也是一个视频传输标准,所以VGA的分辨率也就是视频的分辨率。我们通过查看视频格式手册可以知道VGA的分辨率有哪些。在这里,我选择了一个最常见的分辨率640*[email protected],这里的640、480表示水平和垂直方向的像素点个数,也就表示VGA输出了一个60Hz的640*480的视频信号。

选择了VGA的分辨率之后,我们就可以开始着手编写程序了吗?其实不然,我们还不知道VGA显示的时序,这点我们也可以查看视频格式手册。

在上图中,我们可以看到VGA_HS(HSYNC)信号是一个周期信号,在一个周期内,VGA_HS的低电平时间为96个VGA_CLK信号周期,高电平时间为704个VGA_CLK信号周期。VGA的数据信号在VGA_HS高电平的第49个VGA_CLK信号周期开始有效,一直持续到VGA_HS高电平的第688个VGA_CLK信号周期。

VGA_VS(VSYNC)信号也是一个周期信号,在一个周期内,VGA_VS的低电平时间为2个VGA_HS信号周期,高电平时间为523个VGA_HS信号周期。VGA的数据信号在VGA_VS高电平的第34个VGA_HS信号周期开始有效,一直持续到高电平的第513个VGA_HS信号周期。为了更直观的表达这一时序,我们用下面这个图表示。

总的来说,其实VGA的显示时序就是一个逐行扫描的过程,每完成一个行扫描(即VGA_HS信号经过一个周期),则开始扫描下一行。只有当VGA_HS和VGA_VS同时有效,VGA数据信号才有效。

讲完VGA的显示时序后,我们还需要计算出VGA_CLK信号的频率。我们需要按照这个公式计算:HS_total×VS_total×60Hz。

其中,HS_total为VGA_HS信号的一个周期内包含的VGA_CLK信号周期个数,VS_total为VGA_VS信号的一个周期内包含的VGA_HS信号周期个数,分辨率640*[email protected]时,HS_total为800,VS_total为525。

因此VGA_CLK信号的频率为800×525×60Hz=25.2MHz。

接下来,我们开始编写VGA的显示时序代码,我们用两个计数器hsync_cnt和vsync_cnt来实现。部分代码如下:

 1 /* 时序逻辑,用来给hsync_cnt寄存器赋值 */
 2 always @ (posedge CLK_VGA or negedge RST_N)
 3 begin
 4     if(!RST_N)
 5         hsync_cnt <= 16‘b0;
 6     else
 7         hsync_cnt <= hsync_cnt_n;
 8 end
 9
10 /* 组合逻辑,水平扫描计数器,在启动标志拉高后再计数,每个时钟循环递增 */
11 always @ (*)
12 begin
13     if(hsync_cnt == `HSYNC_D - 16‘h1)
14         hsync_cnt_n = 16‘b0;
15     else
16         hsync_cnt_n = hsync_cnt + 1‘b1;
17 end
18
19 /* 时序逻辑,用来给vsync_cnt寄存器赋值 */
20 always @ (posedge CLK_VGA or negedge RST_N)
21 begin
22     if(!RST_N)
23         vsync_cnt <= 16‘b0;
24     else
25         vsync_cnt <= vsync_cnt_n;
26 end
27
28 /* 组合逻辑,垂直扫描计数器,每次水平扫描计数器计满时循环递增 */
29 always @ (*)
30 begin
31     if((vsync_cnt == `VSYNC_R - 16‘h1) && (hsync_cnt == `HSYNC_D - 16‘h1))
32         vsync_cnt_n = 16‘b0;
33     else if(hsync_cnt == `HSYNC_D - 16‘h1)
34         vsync_cnt_n = vsync_cnt + 1‘b1;
35     else
36         vsync_cnt_n = vsync_cnt;
37 end
38
39 /* 时序逻辑,用来给VGA_HSYNC寄存器赋值 */
40 always @ (posedge CLK_VGA or negedge RST_N)
41 begin
42     if(!RST_N)
43         VGA_HSYNC <= 1‘b0;
44     else
45         VGA_HSYNC <= VGA_HSYNC_N;
46 end
47
48 /* 组合逻辑,在B C D区间拉高水平扫描信号 */
49 always @ (*)
50 begin
51     if(hsync_cnt == `HSYNC_A - 16‘h1)
52         VGA_HSYNC_N = 1‘b1;
53     else if(hsync_cnt == `HSYNC_D - 16‘h1)
54         VGA_HSYNC_N = 1‘b0;
55     else
56         VGA_HSYNC_N = VGA_HSYNC;
57 end
58
59 /* 时序逻辑,用来给VGA_VSYNC寄存器赋值 */
60 always @ (posedge CLK_VGA or negedge RST_N)
61 begin
62     if(!RST_N)
63         VGA_VSYNC <= 1‘b0;
64     else
65         VGA_VSYNC <= VGA_VSYNC_N;
66 end
67
68 /* 组合逻辑,在P Q R区间拉高垂直扫描信号 */
69 always @ (*)
70 begin
71     if(vsync_cnt == `VSYNC_O - 16‘h1 && hsync_cnt == `HSYNC_D - 16‘h1)
72         VGA_VSYNC_N = 1‘b1;
73     else if((vsync_cnt == `VSYNC_R - 16‘h1) && (hsync_cnt == `HSYNC_D - 16‘h1))
74         VGA_VSYNC_N = 1‘b0;
75     else
76         VGA_VSYNC_N = VGA_VSYNC;
77 end

2 网络图片和VGA显示有何区别

在第一个问题中,我们知道了VGA显示分辨率为640*480,颜色深度为24位真彩色。但是,网络图片一般不会完全符合这两个参数,因此,我们需要借助一个软件工具转换一下。

1、  首先,我们在网上随意找到一副图片。

2、 可以看到这个图片的参数为分辨率为700*718,颜色深度为8位。我们用软件Image2Lcd打开该图片并设置相应参数,可以发现我们得到一张分辨率为174*179,颜色深度为8位的bmp图片。由于我使用的FPGA芯片的片内存储器资源较少,而为了将生成的mif文件顺利导入Rom IP核中,我们需要压缩图片。这里为什么要转换成bmp图片呢,因为bmp格式是非压缩的,数据格式比较简单容易处理,方便我们将这个图片存取FPGA中。

3、我们知道FPGA不能直接读取图片,我们要将图片转换成mif文件,存入FPGA的rom中。因此,这里我们将制作一个包含图片全部有效数据的mif文件。这里,我们要使用另一个常用的工具MATLAB,用m语言来实现。代码如下:

 1 clear;
 2 clc;
 3 n=31146;%174*179
 4 mat = imread(‘tu1.bmp‘);%读取.bmp文件
 5 mat = double(mat);
 6 fid=fopen(‘bmp_data.mif‘,‘w‘);%打开待写入的.mif文件
 7 fprintf(fid,‘WIDTH=8;\n‘);%写入存储位宽8位
 8 fprintf(fid,‘DEPTH=31146;\n‘);%写入存储深度31146
 9 fprintf(fid,‘ADDRESS_RADIX=UNS;\n‘);%写入地址类型为无符号整型
10 fprintf(fid,‘DATA_RADIX=HEX;‘);%写入数据类型为无符号整型
11 fprintf(fid,‘CONTENT BEGIN\n‘);%起始内容
12 for i=0:n-1
13     x = mod(i,174)+1;  %174为bmp图片的水平分辨率
14     y = fix(i/174)+1;
15     k = mat(y,x);
16 fprintf(fid,‘\t%d:%x;\n‘,i,k);
17 end
18 fprintf(fid,‘END;\n‘);
19 fclose(fid);%关闭文件 

3 VGA如何显示图片

解决了上述两个问题之后,终于可以显示图片了。

首先,我们调用一个Rom IP核,由于我们显示的bmp图片分辨率为174*179,颜色深度为8位,所以我们设置Rom IP核如下图所示。

如图中所示,我们设置了一个32768*8bit大小的Rom,并且使输出q受时钟控制,加入mif文件。

VGA显示图片的代码也十分简单,我们以VGA显示有效区设置了vga_x和vga_y坐标变量,定义了两个信号用来控制产生Rom表的地址信号,与VGA显示的数据信号。由于bmp图的深度为8bit,所以我们按照332来分配红绿蓝三色数据,并将末尾置1。具体代码如下:

 1 assign vga_x = hsync_cnt - `HSYNC_B;
 2 assign vga_y = vsync_cnt - `VSYNC_P;
 3
 4 Rom                     Rom_init
 5 (
 6     .clock          (CLK_25M            ),
 7     .address            (bmp_rom_add    ),
 8     .q                  (bmp_rom_data   )
 9 );
10
11 //组合电路,用于生成图片位置信号
12 assign  bmp_add = (vga_x >= `BMP1_X - 8‘h3) && (vga_x < `BMP1_X + `BMP1_W - 8‘h3) && (vga_y >= `BMP1_Y) && (vga_y < `BMP1_Y + `BMP1_H);
13 //组合电路,用于生成图片使能信号
14 assign  bmp_en = (vga_x >= `BMP1_X) && (vga_x < `BMP1_X + `BMP1_W) && (vga_y >= `BMP1_Y) && (vga_y < `BMP1_Y + `BMP1_H);
15
16 //时序电路,用来给bmp_rom_add寄存器赋值
17 always @ (posedge CLK_25M or negedge RST_N)
18 begin
19     if(!RST_N)
20         bmp_rom_add <= 1‘h0;
21     else
22         bmp_rom_add <= bmp_rom_add_n;
23 end
24
25 //组合电路,用于生成bmp_rom_add
26 always @ (*)
27 begin
28     if((vga_x == `BMP1_X - 8‘h3) && (vga_y == `BMP1_Y) && bmp_add)
29         bmp_rom_add_n = 1‘h0;
30     else if(bmp_add)
31         bmp_rom_add_n = bmp_rom_add + 1‘b1;
32     else
33         bmp_rom_add_n = bmp_rom_add;
34 end
35
36
37 /* 时序电路,用来给VGA_DATA寄存器赋值 */
38 always @ (posedge CLK_25M or negedge RST_N)
39 begin
40     if(!RST_N)
41         VGA_DATA <= 1‘b0;
42     else
43         VGA_DATA <= VGA_DATA_N;
44 end
45
46 /* 组合电路,用来生成VGA_DATA */
47 always @ (*)
48 begin
49     if(bmp_en)
50         VGA_DATA_N = {bmp_rom_data[7:5],5‘b11111,bmp_rom_data[4:2],5‘b11111,bmp_rom_data[1:0],6‘bb111111};
51     else if(hsync_cnt > `HSYNC_B && hsync_cnt <= `HSYNC_B + 16‘d128 && vsync_cnt > `VSYNC_P && vsync_cnt < `VSYNC_Q)
52         VGA_DATA_N = 24‘hFF0000;
53     else if(hsync_cnt > `HSYNC_B + 16‘d128 && hsync_cnt <= `HSYNC_B + 16‘d256 && vsync_cnt > `VSYNC_P && vsync_cnt < `VSYNC_Q)
54         VGA_DATA_N = 24‘hFFFF00;
55     else if(hsync_cnt > `HSYNC_B + 16‘d256 && hsync_cnt <= `HSYNC_B + 16‘d384 && vsync_cnt > `VSYNC_P && vsync_cnt < `VSYNC_Q)
56         VGA_DATA_N = 24‘h00FF00;
57     else if(hsync_cnt > `HSYNC_B + 16‘d384 && hsync_cnt <= `HSYNC_B + 16‘d512 && vsync_cnt > `VSYNC_P && vsync_cnt < `VSYNC_Q)
58         VGA_DATA_N = 24‘h00FFFF;
59     else if(hsync_cnt > `HSYNC_B + 16‘d512 && hsync_cnt <= `HSYNC_B + 16‘d640 && vsync_cnt > `VSYNC_P && vsync_cnt < `VSYNC_Q)
60         VGA_DATA_N = 24‘h0000FF;
61     else
62         VGA_DATA_N = 24‘d0;
63 end

大家可能会比较疑惑,为什么用来控制产生Rom表的地址信号bmp_add会比控制VGA显示图片的信号bmp_en提前三个时钟。那是因为我们在读取Rom表数据时存在延时,经过我们signaltap采集后发现,原本作为地址70的输出数据FF比地址70慢两个时钟,即bmp_rom_data的输出会比bmp_rom_add延迟两个时钟,而VGA_DATA又比bmp_rom_data延迟1个时钟,因此bmp_add信号需要比bmp_en提前三个时钟。

最后,奉上一张效果图。

本文所涉及的相关资料链接 :http://pan.baidu.com/s/1dFb4J65 密码:zp2r

时间: 2024-12-20 00:41:28

VGA系列之一:VGA显示网络图片的相关文章

学习Android之SimpleAdapter显示网络图片

效果图: 此程序主要的知识点是:SimpleAdapter本身是不支持网络图片的, 如果在Map.put(a,b)中 b为一个Bitmap,程序不会报红色字体,而是在控制台输出绿色的字体,如下 05-10 15:46:45.474: I/System.out(846): resolveUri failed on bad bitmap uri: [email protected] 要想实现显示网络图片其实很简单,使用SimpleAdapter中的方法simpleAdapter.setViewBin

Android 本地加载网页与显示网络图片

有时候需要在应用程序里展示一些网页,但是需求里又明确指出,不允许打开系统浏览器,显然也不可能去编写一个浏览器出来,这时就需要使用 WebView控件,借助它我们就可以在自己的应用程序里嵌入一个浏览器,从而非常轻松地展示各种各样的网页. 由于程序用到了网络功能,而访问网络是需要声明权限的,因此首先得修改 AndroidManifest.xml 文件,并加入声明权限: <uses-permission android:name="android.permission.INTERNET"

winform下 PictureBox 显示网络图片

Image pic = new Image.FromStream(WebRequest.Create("http://x.com/x.jpg").GetResponse().GetResponseStream()); picturebox1.Image = pic 对读取错误的处理没有加 winform下 PictureBox 显示网络图片,布布扣,bubuko.com

Android 使用URL显示网络图片

作者:卿笃军 原文地址:http://blog.csdn.net/qingdujun/article/details/39271479 URL对象中前而几个方法都非常容易理解,而该对象提供的openStream()可以读取该 URL资源的InputStream,通过该方法可以非常方便地读取远程资源. 下面的程序示范如何通过URL类读取远程资源: 1)只显示网络图片 2)显示并下载网络图片 1)只显示网络图片 a) activity_main.xml <RelativeLayout xmlns:a

图片轮播_支持显示网络图片及下载图片至SD后再显示

现在的移动应用, 很常见的一个交互效果就是在首页顶部添加图片轮播的控件, 焦点图可以放入广告, 也可以放入文章的内容图片, 它们不断自动切换, 点击焦点图即跳至对应的界面. 交互效果很棒. 做图片轮播的效果, 方法并不少. 本文使用了常见的viewpager 去实现. 支持显示网络图片下载在缓存显示, 如果有SD卡则默认将图片下载至SD卡中再显示本地的图片. 其实网上这类代码已经很多,  应该也有很多写得比我好. 今天有点时间, 就做了个来练手, 顺便写下自己第一篇原创文章. 敲代码之前也参考了

Android 显示网络图片

本文内容 环境 演示显示网络图片 本文演示 Android 如何显示网络图片.学习一门新的语言,最好办法就先了解该语言的语法和库,以及设计思想,再着手现实一些常用功能,毕竟以后用该语言是要写程序的,而程序说白了,就是一个个功能点. 环境 Windows 2008 R2 64 位 Eclipse ADT V22.6.2,Android 4.4.3 三星 SM-G3508   演示显示网络图片 利用一个新线程加载并显示网络图片,并使用 handler 传递消息,若无异常,则用 Toast 现实"加载

SharePoint 2013技巧分享系列 - 同步Exchange显示高清用户照片

在"SharePoint 2013技巧分享系列 - Active Directory同步显示用户照片"文中介绍了如何同步Active Directory显示用户照片,但是同步完成后,用户照片尺寸和清晰度都不是非常理想.本文将介绍如何同步Exchange Server显示高清用户照片. 原理 与SharePoint Server 2010相似, 在SharePoint Server 2013中存在一个用户照片的目录.当SharePoint启用Exchange照片同步时, SharePoi

Android视频录制从不入门到入门系列教程(二)————显示视频图像

1.创建一个空的工程,注意声明下列权限: <uses-permission android:name="android.permission.CAMERA"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> 2.布局文件 <?xml version="1.0" encoding="utf-8"

pygame系列_游戏窗口显示策略

在这篇blog中,我将给出一个demo演示: 当我们按下键盘的‘f’键的时候,演示的窗口会切换到全屏显示和默认显示两种显示模式 并且在后台我们可以看到相关的信息输出: 上面给出了一个简单的例子,当然在pygame的官方文档中有对显示策略的更权威的说明: http://www.pygame.org/docs/ref/display.html#pygame.display.set_mode ''' pygame.FULLSCREEN create a fullscreen display pygam