基于FPGA的VGA可移植模块终极设计
一、VGA的诱惑
首先,VGA的驱动,这事,一般的单片机是办不到的;由于FPGA的速度,以及并行的优势,加上可现场配置的优势,VGA的配置,只有俺们FPGA可以胜任,也只有FPGA可以随心所欲地配置(当然ARM也可以,应用比较高吧)。
初学者就是喜欢看炫的效果,往往会忍不住想玩。尤其玩FPGA的,没玩VGA就感到跟单片机没啥提升,因此VGA的驱动也不得不讲。Bingo当年也是如此。挡不住VGA的诱惑,初学者问Bingo VGA问题的人也是灰常的多,也许一般教科书理论太强,实际应用不是很身后,在此Bingo用浅显易懂的语言来讲述VGA的驱动原理,以及通过设计一个可移植模块的应用来讲述。
二、VGA驱动原理
此处Bingo不参考任何资料,用当年已学的知识,用浅显易懂的语言讲述。
1、VGA接口
最主要的几根线:
更详细的资料,请看 http://baike.baidu.com/view/10346.htm
2、VGA时序
VGA其实就是相当于一块芯片,跟单片机驱动IC一样,满足一定的时序,便能驱动起来。
(1)扫描轨迹
VGA的扫描其实很简单,大致轨迹如下所示:
没扫描完一行,从新开始下一行;每扫完一场,重新开始下一场。相信你应该看的懂。
(2)行场扫描
以下是行扫描,场扫描HS,VS时序图
如上如所示:VGA一直在扫描,没一场的扫描包括了若干行扫描,如此循环。
(3)VS时序深入分析
VS时序如下所示:
可见时序的循环,可被划分为a,b,c,d4个时期。这四个时期定义如下:
A~B:场消隐期 即同步,相当于还原扫描坐标吧
B~C:场消隐后肩 相当于准备开始扫描吧
C~D:场显示期 扫描中,数据有效区域
D~E:场消隐前肩 完成扫描,相当于准备同步
(4)HS时序深入分析
可见时序的循环,可被划分为a,b,c,d4个时期。这四个时期定义如下:
A~B:行消隐期 即同步,相当于还原扫描坐标吧
B~C:行消隐后肩 相当于准备开始扫描吧
C~D:行显示期 扫描中,数据有效区域
D~E:行消隐前肩 完成扫描,相当于准备同步
综上描述,我们只要知道每个时期的时间,便可以表示出VGA的时序。而FPGA的工作是由固定频率的时钟触发的,因此某固定时间可以用n次触发来表示。因此我们很容易就想到了FPGA常用的计数方法:比如说行扫描,我们计数0~H_total-1。用另一个进程将其划分为4个时期,安标注分配。其实这相当于状态机。
以下是固定分辨率1024*768 60fps下HS,VS的标准:
用代码表示4个时期,如下:
// VGA_1024_768_60fps_65MHz
// Horizontal Parameter( Pixel )
parameter H_DISP = 11'd1024,
parameter H_FRONT = 11'd24,
parameter H_SYNC = 11'd136,
parameter H_BACK = 11'd160,
parameter H_TOTAL = 11'd1344,
// Virtical Parameter( Line )
parameter V_DISP = 10'd768,
parameter V_FRONT = 10'd3,
parameter V_SYNC = 10'd6,
parameter V_BACK = 10'd29,
parameter V_TOTAL = 10'd806
3、VGA电路
(1)三原色
VGA接口:R,G,B三通道,直接赋给数字信号,RGB,最多产生8种色彩。这是最基本的。电路如下所示:
(2)真彩显示
a) 电阻网络
考虑到成本意识实现的简易方案,用R-2R电阻网络分流模拟DAC替换ADV7123视频转换芯片。见以下几个方案:
DE1 VGA模拟电路
小马哥电路图
具体设计参考Bingo当年总结:
http://www.cnblogs.com/crazybingo/archive/2010/07/31/1789323.html
或者参考小马哥设计:
b) 专用视频转换芯片
利用专用视频转换芯片,ADV7120等,将数字信号转换为VGA RGB的模拟信号。ADV7120为高速D/A芯片,将数字信号转换为模拟信号输给VGA,电路如下:
三、可移植VGA模块设计
Bingo玩VGA也算是比较早了,当年也是视觉的诱惑,以及唯FPGA独尊的优势。于是之后一发而不可收拾。本章Bingo将自己这些年最终优化的VGA驱动模块,发布至此。本模块所有代码均Bingo独家创造,请尊重版权哈。
本设计已经封装成模块,只要修改时序参数、扫描时钟参数以及在vga_display.v中添加显示电路,即可。方便移植,希望对大家有用。
1、模块划分
(1)vga_design.v
工程顶层文件,例化各个模块。
(2)sys_ctrl.v
PLL时钟分配电路。
(3)vga_display.v
显示电路,根据时序,用于描述VGA的显示电路。
(4)vga_driver.v
VGA驱动电路,对时序,状态的约束。
RTL图如下所示:
2、代码设计
Bingo例程以16bit RGB VGA驱动为例,不同位数的显示只要改一下vga_data即可。
前文以及代码讲述了那么多,此处不再贴完整代码,而是对代码中部分结构进行解析。
代码下载地址:http://blog.chinaaet.com/detail/21606.html
(1)vga_driver.v代码分析
a) 参数例化列表
#(
// VGA_1024_768_60fps_65MHz
// Horizontal Parameter ( Pixel )
parameter H_DISP = 11'd1024,
parameter H_FRONT = 11'd24,
parameter H_SYNC = 11'd136,
parameter H_BACK = 11'd160,
parameter H_TOTAL = 11'd1344,
// Virtical Parameter ( Line )
parameter V_DISP = 10'd768,
parameter V_FRONT = 10'd3,
parameter V_SYNC = 10'd6,
parameter V_BACK = 10'd29,
parameter V_TOTAL = 10'd806
)
这样写的目的是为了软件封装性,能够在例化的时候修改法分辨率,同时电路结构保持不变。
DISP,FRONT ,SYNC,BACK,TOTAL分别为显示期,消隐前肩,消音期,消隐后肩,总时间,各自对应各自的行场信号。
b) 行同步信号设计
//------------------------------------------
// 行同步信号发生器
reg [10:0] hcnt;
always @ (posedge clk_vga or negedge rst_n)
begin
if (!rst_n)
hcnt <= 0;
else
begin
if (hcnt < H_TOTAL-1'b1)
hcnt <= hcnt + 1'b1;
else
hcnt <= 0;
end
end
//------------------------------------------
always@(posedge clk_vga or negedge rst_n)
begin
if(!rst_n)
vga_hs <= 1;
else
begin
if( (hcnt >= H_DISP+H_FRONT-1'b1) && (hcnt < H_DISP+H_FRONT+H_SYNC-1'b1) )
vga_hs <= 0;
else
vga_hs <= 1;
end
end
如上所示,分析代码可以知道,行同步信号的计数状态机按照时序的划分,是以下过程:H_DISP,H_FRONT,H_SYNC,H_BACK,这似乎和上述分析的VGA时序不是完全吻合。但是VGA时序是一个循环,顺推H_BACK个时终域便可以得到以上时期划分,但是这样更方便后续坐标计数,因为Bingo此处这样设计,当然实际证明是完全可行的。
注意:(hcnt >= H_DISP+H_FRONT-1'b1) && (hcnt < H_DISP+H_FRONT+H_SYNC-1'b1) 只是因为后续坐标计算,就把时序提前了1个时钟已达到同步。
c) 场同步信号设计
同上。
d) 数据显示坐标以及输出设计
//------------------------------------------
assign vga_xpos = (hcnt < H_DISP) ? hcnt[9:0]+1'b1 : 10'd0;
assign vga_ypos = (vcnt < V_DISP) ? vcnt[9:0]+1'b1 : 10'd0;
assign vga_rgb = ((hcnt < H_DISP) && (vcnt < V_DISP)) ? vga_data : 16'd0;
这部分很容易理解,在显示期坐标根据显示的扫描而改变,在非显示期,坐标置零。
(2)Vga_display.v代码分析
a) 标准色彩定义
//define colors RGB--5|6|5
localparam RED = 16'hF800;
localparam GREEN = 16'h07E0;
localparam BLUE = 16'h001F;
localparam WHITE = 16'hFFFF;
localparam BLACK = 16'h0000;
localparam YELLOW = 16'hFFE0;
localparam CYAN = 16'hF81F;
localparam ROYAL = 16'h07FF;
定义当地的参数,目的是为了后续标准色彩调用的方便,没有其他作用。
b) 根据固定区域输出数据
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
vga_data <= 16'h0;
else
begin
if (vga_xpos >= 0 && vga_xpos < (H_DISP/3))
vga_data <= RED;
else if(vga_xpos >= (H_DISP>>3)*1 && vga_xpos < (H_DISP/3)*2)
vga_data <= GREEN;
else
vga_data <= BLUE;
end
end
如上代码,根据xpos坐标,来输出固定色彩。由于vga_driver模块已经完全设计好接口,因此vga_display.v模块就是简单的根据区域,输出设计的颜色。
(3) Vga_ctrl.v代码分析
a) 同步化解析
略。
b) 可控锁相环PLL设计
本来可空锁相环PLL设计,Bingo想单独成章来讲述的,但觉得可能别的地方实际应用价值不是很大,最后便在此处阐明这样设计的原因。
由于VGA不同分辨率的扫描时钟不同,因此分辨率变化的时候,PLL的输出时钟是必要跟随着变化。但是Bingo觉得好麻烦,于是想了偷懒的一招:PLL IP定义,无非是用Quartus II GUI对PLL参数的设定,最后生成pll.v这个文件。而这个文件相当于是锁相环的驱动电路,故势必包含着参数的定义。实际如下:
altpll_component.clk0_divide_by = 1,
altpll_component.clk0_duty_cycle = 50,
altpll_component.clk0_multiply_by = 1,
如上图所示,每次修改参数,这三个变量会相应的发生变化。因此,何不把这三个参数作为例化的参数,可以再顶层直接修改代码来实现固定频率的输出?这样岂不是很方便?
因此设计参数例化接口如下:
#(
parameter DUTY_CYCLE = 50,
parameter DIVIDE_DATA = 1,
parameter MULTIPLY_DATA = 1
)
理论上是完全行得通的,这样设计的另一个好处就是电路的封装性更好Bingo实践证明,完全可行,因此在此处说明,要学会正确的偷懒呵呵。
(4)Vga_design.v顶层文件代码解析
a) Vga接口定义
//vga interface
output vga_adv_clk,
output vga_blank_n,
output vga_sync_n,
output vga_hs,
output vga_vs,
output [15:0] vga_rgb
如上所示,上面三个信号线是在使用adv712x视频转换芯片的时候才会出现,一般的电阻模拟电路,或者直接RGB3线驱动,可以直接删除相关信号以及电路。
b) 进一步偷懒法则
根据常用的几种分辨率,Bingo进行了总结,以下三种应用较多,因此进一步偷懒法则,围绕他们三者来(不在这三种以内的话,自己模仿着再写一个):
VGA_640_480_60FPS_25MHz
VGA_800_600_72FPS_50MHz
VGA_1024_768_60FPS_65MHz
Verilog语法也有define的用法,是否还记得C语言中,大工程的调试经常对相关变量的注释,注销来调整整个工程变量的应用,因此此处Bingo套用这种思维模式,定义以上三种模式的变量,通过修改注释define便可以轻松修改全局。具体如下所示:
//----------------------------------------
//vga parameter define
//`define VGA_640_480_60FPS_25MHz
//`define VGA_800_600_72FPS_50MHz
`define VGA_1024_768_60FPS_65MHz
`ifdef VGA_640_480_60FPS_25MHz
parameter DUTY_CYCLE = 50;
parameter DIVIDE_DATA = 2;
parameter MULTIPLY_DATA = 1;
parameter H_DISP = 11'd640;
parameter H_FRONT = 11'd16;
parameter H_SYNC = 11'd96;
parameter H_BACK = 11'd48;
parameter H_TOTAL = 11'd800;
parameter V_DISP = 10'd480;
parameter V_FRONT = 10'd10;
parameter V_SYNC = 10'd2;
parameter V_BACK = 10'd33;
parameter V_TOTAL = 10'd525;
`endif
`ifdef VGA_800_600_72FPS_50MHz
parameter DUTY_CYCLE = 50;
parameter DIVIDE_DATA = 1;
parameter MULTIPLY_DATA = 1;
parameter H_DISP = 11'd800;
parameter H_FRONT = 11'd56;
parameter H_SYNC = 11'd120;
parameter H_BACK = 11'd64;
parameter H_TOTAL = 11'd1040;
parameter V_DISP = 10'd600;
parameter V_FRONT = 10'd37;
parameter V_SYNC = 10'd6;
parameter V_BACK = 10'd23;
parameter V_TOTAL = 10'd666;
`endif
`ifdef VGA_1024_768_60FPS_65MHz
parameter DUTY_CYCLE = 50;
parameter DIVIDE_DATA = 10;
parameter MULTIPLY_DATA = 13;
parameter H_DISP = 11'd1024;
parameter H_FRONT = 11'd24;
parameter H_SYNC = 11'd136;
parameter H_BACK = 11'd160;
parameter H_TOTAL = 11'd1344;
parameter V_DISP = 10'd768;
parameter V_FRONT = 10'd3;
parameter V_SYNC = 10'd6;
parameter V_BACK = 10'd29;
parameter V_TOTAL = 10'd806;
`endif
四、Display方案以及效果
1、彩条
(1)代码
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
vga_data <= 16'h0;
else
begin
if (vga_xpos >= 0 && vga_xpos < (H_DISP>>3))
vga_data <= RED;
else if(vga_xpos >= (H_DISP>>3)*1 && vga_xpos < (H_DISP>>3)*2)
vga_data <= GREEN;
else if(vga_xpos >= (H_DISP>>3)*2 && vga_xpos < (H_DISP>>3)*3)
vga_data <= BLUE;
else if(vga_xpos >= (H_DISP>>3)*3 && vga_xpos < (H_DISP>>3)*4)
vga_data <= WHITE;
else if(vga_xpos >= (H_DISP>>3)*4 && vga_xpos < (H_DISP>>3)*5)
vga_data <= BLACK;
else if(vga_xpos >= (H_DISP>>3)*5 && vga_xpos < (H_DISP>>3)*6)
vga_data <= YELLOW;
else if(vga_xpos >= (H_DISP>>3)*6 && vga_xpos < (H_DISP>>3)*7)
vga_data <= CYAN;
else// if(vga_xpos >= (H_DISP<<3)*7 && vga_xpos < (H_DISP<<3)*8)
vga_data <= ROYAL;
end
end
通过简单的对X坐标地址的分割,来得到彩条。这是应该是VGA初学者一开始最兴奋的几个界面吧。
(2)效果图
2、花型矩阵
(1)代码
wire [19:0] vga_result = vga_xpos * vga_ypos;
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
vga_data <= 16'h0;
else
vga_data = vga_result[15:0];
end
通过x坐标地址和y坐标地址的乘积的值,取低16位,得到的数据有一定的规律。Bingo当年也是不小心发现的,仅此献给初学的孩子们,这个比彩条更帅气。
(2)效果图
S
基于FPGA的VGA可移植模块终极设计的更多相关文章
- 基于FPGA的VGA可移植模块终极设计【转】
本文转载自:http://www.cnblogs.com/lueguo/p/3373643.html 略过天涯 基于FPGA的VGA可移植模块终极设计 一.VGA的诱惑 首先,VGA的驱动,这事, ...
- [置顶]
基于FPGA的VGA简易显存设计&NIOS ii软核接入
项目简介 本项目基于Altera公司的Cyclone IV型芯片,利用NIOS II软核,2-port RAM与时序控制模块,实现64*48分辨率的显存(再大的显存板载资源m9k不够用) 实现效果如下 ...
- 基于FPGA的VGA显示设计(二)
上一篇:基于FPGA的VGA显示设计(一) 参照 CrazyBingo 的 基于FPGA的VGA可移植模块终极设计代码 的工程代码风格,模块化处理了上一篇的代码,并增加了一点其它图形. 顶层 ...
- 基于FPGA的VGA显示实验设计
基于FPGA的VGA显示实验设计 成果展示(优酷视频): 视频: 基于FPGA的VGA显示技术(手机控制) http://v.youku.com/v_show/id_XNjk4ODE3ODUy.htm ...
- 基于FPGA的VGA显示设计(一)
前言 FPGA主要运用于芯片验证.通信.图像处理.显示VGA接口的显示器是最基本的要求了. 原理 首先需要了解 : (1)VGA接口协议:VGA端子_维基百科 .VGA视频传输标准_百度 引脚1 RE ...
- 基于FPGA的VGA显示静态图片
终于熬到暑假了,记过三四周的突击带考试,终于为我的大二画上了一个完整的句号,接下来终于可以静心去做自己想做的事情了,前一阵子报了一个线上培训班,学学Sobel边缘检测,之前一直在学习图像处理,但是因为 ...
- 基于FPGA的DDS任意波形发生器设计
一.简介 DDS技术最初是作为频率合成技术提出的,由于其易于控制,相位连续,输出频率稳定度高,分辨率高, 频率转换速度快等优点,现在被广泛应用于任意波形发生器(AWG).基于DDS技术的任 ...
- 基于FPGA的通信信号源的设计
通信信号源设计原理 通过设计一个DDS信号源,然后将该信号作为载波信号,再对基带信号进行2ASK.2FSK.2PSK.2DPSK调制,进而产生多种通信信号. 设计框图如下: 将PN序列进行2ASK.2 ...
- 基于fpga的vga学习(3)
本次学习如何通过vga发送数字.文字.字母, 首先利用建模软件,将想要发送的数据通过数学建模转换,这里我用的软件是PCtoLCD,具体效果如下 这里可以看出,建模将数据装换成0和1,一个字母用16x8 ...
随机推荐
- 23. Merge k Sorted Lists[H]合并k个排序链表
题目 Merge k sorted linked lists and return it as one sorted list. Analyze and describe its complexity ...
- 【读书笔记】UEFI原理与编程(1)概述及开发环境的搭建
一.概述: 0.为什么会有这篇文章 说实在的,在2016初的时候,我就萌生了写一个操作系统的念头,但是这对于我一个菜鸟来说,犹如登天. 既然想了就去写,即使最后做不完,也不后悔. 抱着这样的念头,我开 ...
- Hadoop MapReduce编程 API入门系列之分区和合并(十四)
不多说,直接上代码. 代码 package zhouls.bigdata.myMapReduce.Star; import java.io.IOException; import org.apache ...
- 八叉树(Octree)Typescript 实现
Demo GitHub export class Octree { // 父&子树 private parent_node: any; private children_nodes: Octr ...
- 当接口上配了 FeignClient 和 RequestMapping 两个注解,结果错误提示 重复mapping处理方法
再接手老文档的时候,发现有这么一个问题 错误显示为: 原文档写法: 解决方法: 这是一个编译时写法的问题,将上方的RequestMapping去掉,然后把路径放在下面的PostMapping 便可以正 ...
- 【Oracle】RAC集群中的命令
数据库名称:racdb 节点名称:rac3.rac4 注:以下命令均在grid用户中执行 1.查看集群节点的状态: [grid@rac3 ~]$ crsctl check cluster [grid@ ...
- SqlServer动态变换库名
declare @tname varchar(20),@num intset @tname='Players_Log_L10001'declare @sql Nvarchar(1000)=N'sele ...
- WP - 控件基础-按钮控件
Button:HyperlinkButton:RepeatButton:ToggleButton 1.Button: <button content="Button" ...
- layui 时间控件 单击 年直接赋值
在 //选中 lay(ul).find('li').on('click', function(){ //结尾处添加 if (options.done === "year") { ...
- Centos7 执行firewall-cmd –permanent –add-service=mysql报错“ModuleNotFoundError: No module named 'gi'”
因为目前环境Python3.x与Python2.x版本并存,所以导致以上问题. 解决方法: 第一步,vim /usr/bin/firewall-cmd, 将#!/usr/bin/python -Es ...