2013.09.14
跟着版主视频学,完成第五份课后作业,代码如下。
注:版主视频中提到的 7段数码管代码查询工具,版主在群里共享了一份,我把它上传到了服务器上,链接为
http://share.eepw.com.cn/share/download/id/90001
`timescale 1ns / 1ps
`define ONE_SECOND 26'd49999999 //50MHz时钟, 因此计数器要计数 50M次=50*1000*1000 就为1秒
//////////////////////////////////////////////////////////////////////////////////
//学号: 136.finema
//
//创建时间: 21:49:14 09/14/2013
//名称:dig_display_dip
//功能:数码管静态循环显示,用DIP控制哪个数码管亮
//算法:
//////////////////////////////////////////////////////////////////////////////////
module dig_display_dip(sys_clk,
sys_rst,
dip,
dig_seg,
dig_bit);
input sys_clk; //输入时钟信号
input sys_rst; //输入复位信号
input [7:0] dip; //输入DIP
output [7:0] dig_seg; //输出数码管段码
output [7:0] dig_bit; //输出数码管位码
//变量定义
reg [7:0] dig_seg;
wire [7:0] dig_bit;
reg [25:0] timeCounter; //时间计数器.
reg [7:0] charIndex; //字符索引.
//计数器的逻辑处理
always @(posedge sys_clk,negedge sys_rst) //只对时钟上升沿信号和复位下降沿信号敏感
begin
if (!sys_rst)
timeCounter <=26'd0;
else
begin
if (timeCounter==`ONE_SECOND)
timeCounter<=26'd0;
else
timeCounter<=timeCounter+1'b1;
end
end
//led的逻辑处理
always @(posedge sys_clk,negedge sys_rst)
begin
if (!sys_rst)
begin
charIndex <=8'd0;
end
else
begin
if (timeCounter==`ONE_SECOND)
begin
charIndex <=charIndex+1'b1;
end
else
begin
charIndex <= charIndex;
if (charIndex>8'd16)
charIndex <=8'd0;
end
end
end
//数码管显示
always @(charIndex)
begin
case (charIndex)
8'd0: dig_seg<=8'hC0; //0
8'd1: dig_seg<=8'hF9; //1
8'd2: dig_seg<=8'hA4; //2
8'd3: dig_seg<=8'hB0; //3
8'd4: dig_seg<=8'h99; //4
8'd5: dig_seg<=8'h92; //5
8'd6: dig_seg<=8'h82; //6
8'd7: dig_seg<=8'hF8; //7
8'd8: dig_seg<=8'h80; //8
8'd9: dig_seg<=8'h90; //9
8'd10: dig_seg<=8'h88; //A
8'd11: dig_seg<=8'h83; //B
8'd12: dig_seg<=8'hC6; //C
8'd13: dig_seg<=8'hA1; //D
8'd14: dig_seg<=8'h86; //E
8'd15: dig_seg<=8'h8E; //F
8'd16: dig_seg<=8'h7F; //.
default: dig_seg<=8'hC0;
endcase
end
//连续赋值,8个数码管根据DIP导通
assign dig_bit = dip;
endmodule
我用到的tcl文件,内容如下:
关于 TCL 可以看网友284-xiacf88推荐的
http://www.cnblogs.com/yuphone/archive/2010/01/18/1650612.html
我用到的tcl文件,内容如下:
#CPLD 时钟脚
set_location_assignment PIN_12 -to sys_clk
#CPLD 复位脚
set_location_assignment PIN_14 -to sys_rst
#8个数码管
set_location_assignment PIN_82 -to dig_seg[0]
set_location_assignment PIN_86 -to dig_seg[1]
set_location_assignment PIN_76 -to dig_seg[2]
set_location_assignment PIN_84 -to dig_seg[3]
set_location_assignment PIN_91 -to dig_seg[4]
set_location_assignment PIN_90 -to dig_seg[5]
set_location_assignment PIN_88 -to dig_seg[6]
set_location_assignment PIN_81 -to dig_seg[7]
set_location_assignment PIN_77 -to dig_bit[0]
set_location_assignment PIN_78 -to dig_bit[1]
set_location_assignment PIN_83 -to dig_bit[2]
set_location_assignment PIN_85 -to dig_bit[3]
set_location_assignment PIN_87 -to dig_bit[4]
set_location_assignment PIN_89 -to dig_bit[5]
set_location_assignment PIN_92 -to dig_bit[6]
set_location_assignment PIN_95 -to dig_bit[7]
#DIP
set_location_assignment PIN_29 -to dip[0]
set_location_assignment PIN_30 -to dip[1]
set_location_assignment PIN_33 -to dip[2]
set_location_assignment PIN_34 -to dip[3]
set_location_assignment PIN_35 -to dip[4]
set_location_assignment PIN_36 -to dip[5]
set_location_assignment PIN_37 -to dip[6]
set_location_assignment PIN_38 -to dip[7]
2013.09.15
跟着版主视频学习,完成第六份课后作业——数码管动态显示。
代码如下:
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
//学号: 136.finema
//
//创建时间: 17:35:14 09/15/2013
//名称:dig_scan
//功能:8个数码码管动态显示方式显示0~7,刷新率1ms
//算法:
//////////////////////////////////////////////////////////////////////////////////
module dig_scan(sys_clk,
sys_rst,
dig_seg,
dig_bit);
input sys_clk; //输入时钟信号
input sys_rst; //输入复位信号
output [7:0] dig_seg; //输出数码管段码
output [7:0] dig_bit; //输出数码管位码
parameter TIME_INIT_VALUE = 26'd0; //常量: 时间计数器初始值
parameter ONE_SECOND = 26'd4999_9999; //50MHz时钟, 因此计数器要计数 50M次=50*1000*1000 就为1秒
parameter ONE_MILLI_SECOND = 16'd49999; //50MHz时钟, 1毫秒
//变量定义
reg [7:0] dig_seg;
reg [7:0] dig_bit;
reg [25:0] timeCounter; //时间计数器.
reg [2:0] charIndex; //字符索引. 8个字符
//计数器的逻辑处理
always @(posedge sys_clk,negedge sys_rst) //只对时钟上升沿信号和复位下降沿信号敏感
begin
if (!sys_rst)
timeCounter <= TIME_INIT_VALUE;
else
begin
if (timeCounter==ONE_MILLI_SECOND)
timeCounter<=TIME_INIT_VALUE;
else
timeCounter<=timeCounter+1'b1;
end
end
//led的逻辑处理
always @(posedge sys_clk,negedge sys_rst)
begin
if (!sys_rst)
begin
charIndex <=3'd0;
dig_bit = 8'b1111_1110;
end
else
begin
if (timeCounter==ONE_MILLI_SECOND)
begin
charIndex <=charIndex+1'b1;
dig_bit = {dig_bit[6:0],dig_bit[7]}; //位选左移
end
end
end
//数码管译码
always @(charIndex)
begin
case (charIndex)
3'd0: dig_seg<=8'hC0; //0
3'd1: dig_seg<=8'hF9; //1
3'd2: dig_seg<=8'hA4; //2
3'd3: dig_seg<=8'hB0; //3
3'd4: dig_seg<=8'h99; //4
3'd5: dig_seg<=8'h92; //5
3'd6: dig_seg<=8'h82; //6
3'd7: dig_seg<=8'hF8; //7
default: dig_seg<=8'hC0; //0
endcase
end
endmodule
理解VGA时序
2014/4/12
(学号:136-finema)
1. 前言最近一段时间,看见EEPW-CPLD群里的很多网友和我一样在弄CPLD 的VGA实验,和很多人一样,我一开始也是搞不掂,后来经过咨询51FPGA版主、查看网上的资料、多次仿真才弄清楚了VGA是怎么回事。撰写本文,主要是为了自己理清所学内容,希望也能对大家有帮助。
在此先感谢51FPGA和EEPW,谢谢他们把我这菜鸟带进了 CPLD-FPGA这多姿多彩的世界。
2. 背景知识本文以学习生成 640*480@60Hz VGA信号为目标,要继续阅读,首先你要对老式VGA显示器是如何显示东西的,以及对VGA的场、行周期、刷新率等概念搞清楚,不清楚去查Baidu或Google。
也可看:http://www.cnblogs.com/spartan/archive/2011/08/16/2140546.html
(我的理解就是:1 场= 1帧; 而1帧 有 480行,每1行有640列;完成一行扫描需要的时间就是行周期;刷新率就是 1秒中刷新了多少帧。)
3. 时序总览
相信你看了资料,也会理解不透彻,特别是时序部分,关键是动起手来。
废话少说了,先看一下ModelSim仿真的时序图(见图1),比你看资料中的手工画的时序图直观多了。
图1
图1中红线部分就是“场信号(vsync)”, 蓝线部分就是“行信号(hsync)”也叫“水平信号”。
图中我们可以数出有3段“场信号(vsync)”即3帧。
4. 场信号时序(vsync)那么场信号时序包含了什么呢,我们看一下VGA定的标准。
见http://tinyvga.com/vga-timing/640x480@60Hz
整理得到640*480@60Hz的场信号定义,见表1。
同步脉冲 |
后沿 |
显示脉冲 (即可见部分) |
前沿 |
合计 |
单位 |
2 |
33 |
480 |
10 |
525 |
行周期 |
表1
从表1我们得知“场信号”的同步脉冲有2个行周期长(注意:这里指的是行周期,而不是时钟周期),好了我们放大一下图1,可以清楚的验证这一点。见图2。
图2
也就是说2个行周期后,1帧的场同步信号就结束了,再经过 33+480+10=523个行周期后,下1帧的场同步信号才会来。
5. 行信号时序(hsync)那么1行的周期到底有多长呢,行信号的时序又包含那些东西呢?我们还是看VGA的标准。见http://tinyvga.com/vga-timing/640x480@60Hz
整理得到640*480@60Hz的行信号定义,见表2。
同步脉冲 |
后沿 |
显示脉冲 (即可见部分) |
前沿 |
合计 |
单位 |
96 |
48 |
640 |
16 |
800 |
时钟周期 |
表2
我们把图1的“行信号(hsync)”波形放大,见图3,可看到“hcount=96”,即“行信号”的同步脉冲有96个时钟周期,与表2的描述相符。
图3
6. 可见部分可见部分(即显示脉冲)是什么,我们把表1和表2的数据合并得到表3。
|
同步脉冲 |
后沿 |
显示脉冲 (即可见部分) |
前沿 |
合计 |
单位 |
场信号 |
2 |
33 |
480 |
10 |
525 |
行周期 |
行信号 |
96 |
48 |
640 |
16 |
800 |
时钟周期 |
表3
所谓可见部分,就是每1帧中,场信号的计数“hcount”在35到515之间(区间长度为480)并且行信号的计数“vcount”在144到784之间(区间长度为640),RGB的值才有效,其它范围VGA显示器不会显示RGB的值。
那么不在范围内的计数器的值有什么作用呢,老式VGA显示器就是靠他们来进行消隐的,对于液晶显示器,我想没什么可用之地。
7. 时钟那么我们要生成 640*480@60Hz 的VGA信号,需要多快的时钟才能起作用呢,我们来算一下:
525*800*60Hz=25200000Hz=25.2MHz≈25MHz
更精确点的计算:
525*800*60Hz *1000/1001=25174825.174825174825174825174825Hz≈25MHz
也就是说至少需要25MHz的时钟,我们才能正确生成640*480@60Hz 的VGA信号。(我们会看到一些接了50MHz 晶振的CPLD,其HDL代码就有分频处理。)
那么显示1帧需要多长时间呢?精确点的计算就是:
525*800/25.174≈16683微妙
8. 源代码 8.1. Verilog代码
代码源于网络,自身修改了一下,以便于仿真,参照前面所说,自己琢磨代码。
//VGA 640*480@60Hz彩色信号显示
module VGA2(
sys_rst,
clock,
switch,
disp_RGB,
hsync,
vsync
);
input sys_rst; //复位信号
input clock; //系统输入时钟 50MHz
input [1:0]switch;
output [2:0]disp_RGB; //VGA数据输出
output hsync; //VGA行同步信号
output vsync; //VGA场同步信号
reg [9:0] hcount; //VGA行扫描计数器
reg [9:0] vcount; //VGA场扫描计数器
reg [2:0] data;
reg [2:0] h_dat;
reg [2:0] v_dat;
//reg [9:0] timer;
//reg flag;
wire hcount_ov;
wire vcount_ov;
wire dat_act;
wire hsync;
wire vsync;
reg vga_clk; //VGA时钟
//VGA行、场扫描时序参数表
parameter hsync_end = 10'd95,
hdat_begin = 10'd143,
hdat_end = 10'd783,
hpixel_end = 10'd799,
vsync_end = 10'd1,
vdat_begin = 10'd34,
vdat_end = 10'd514,
vline_end = 10'd524;
//always @(posedge clock)
//begin
// vga_clk = ~vga_clk;
// //vga_clk <= ~vga_clk;
//end
always @(posedge clock,negedge sys_rst)
begin
if (!sys_rst)
begin
vga_clk = 0;
end
else
begin
vga_clk = ~vga_clk; //50MHz 分频成 25MHz
//vga_clk <= ~vga_clk;
end
end
//************************VGA驱动部分*******************************
//行扫描
//always @(posedge clock,negedge sys_rst)
//begin
// if (!sys_rst)
// begin
// hcount <= 10'd0;
// end
// else
// begin
// if (hcount_ov)
// hcount <= 10'd0;
// else
// hcount <= hcount + 10'd1;
// end
//end
always @(posedge vga_clk,negedge sys_rst)
begin
if (!sys_rst)
begin
hcount <= 10'd0;
end
else
begin
if (hcount_ov)
hcount <= 10'd0;
else
hcount <= hcount + 10'd1;
end
end
assign hcount_ov = (hcount == hpixel_end);
//场扫描
//always @(posedge clock,negedge sys_rst)
//begin
// if (!sys_rst)
// begin
// vcount <= 10'd0;
// end
// else
// begin
// if (hcount_ov)
// begin
// if (vcount_ov)
// vcount <= 10'd0;
// else
// vcount <= vcount + 10'd1;
// end
// end
//end
always @(posedge vga_clk,negedge sys_rst)
begin
if (!sys_rst)
begin
vcount <= 10'd0;
end
else
begin
if (hcount_ov)
begin
if (vcount_ov)
vcount <= 10'd0;
else
vcount <= vcount + 10'd1;
end
end
end
assign vcount_ov = (vcount == vline_end);
//数据、同步信号输
assign dat_act = ((hcount >= hdat_begin) && (hcount < hdat_end))
&& ((vcount >= vdat_begin) && (vcount < vdat_end));
assign hsync = (hcount > hsync_end);
assign vsync = (vcount > vsync_end);
assign disp_RGB = (dat_act) ? data : 3'h00;
//************************显示数据处理部分*******************************
//图片显示延时计数器
/*always @(posedge vga_clk)
begin
flag <= vcount_ov;
if(vcount_ov && ~flag)
timer <= timer + 1'b1;
end
*/
always @(posedge vga_clk)
begin
case(switch[1:0])
2'd0: data <= h_dat; //选择横彩条
2'd1: data <= v_dat; //选择竖彩条
2'd2: data <= (v_dat ^ h_dat); //产生棋盘格
2'd3: data <= (v_dat ~^ h_dat); //产生棋盘格
endcase
end
always @(posedge vga_clk) //产生竖彩条
begin
if(hcount < 223)
v_dat <= 3'h7; //白
else if(hcount < 303)
v_dat <= 3'h6; //黄
else if(hcount < 383)
v_dat <= 3'h5; //青
else if(hcount < 463)
v_dat <= 3'h4; //绿
else if(hcount < 543)
v_dat <= 3'h3; //紫
else if(hcount < 623)
v_dat <= 3'h2; //红
else if(hcount < 703)
v_dat <= 3'h1; //蓝
else
v_dat <= 3'h0; //黑
end
always @(posedge vga_clk) //产生横彩条
begin
if(vcount < 94)
h_dat <= 3'h7; //白
else if(vcount < 154)
h_dat <= 3'h6; //黄
else if(vcount < 214)
h_dat <= 3'h5; //青
else if(vcount < 274)
h_dat <= 3'h4; //绿
else if(vcount < 334)
h_dat <= 3'h3; //紫
else if(vcount < 394)
h_dat <= 3'h2; //红
else if(vcount < 454)
h_dat <= 3'h1; //蓝
else
h_dat <= 3'h0; //黑
end
endmodule
8.2. ModelSim测试激励代码
`timescale 1 ns/ 1 ps
module VGA2_test();
// constants
// general purpose registers
// test vector input registers
reg clock;
reg sys_rst;
reg [1:0] switch;
// wires
wire [2:0] disp_RGB;
wire hsync;
wire vsync;
//时钟 50MHz
initial
begin
clock = 0;
forever #10 clock = ~clock;
end
//复位信号
initial
begin
sys_rst = 1;
# 10 sys_rst = 0;
# 50 sys_rst = 1;
end
initial
begin
switch[1:0] = 2'd1;
end
// assign statements (if any)
VGA2 VGA2_inst (
.sys_rst(sys_rst),
.clock(clock),
.disp_RGB(disp_RGB),
.hsync(hsync),
.switch(switch),
.vsync(vsync)
);
endmodule
8.3. TCL脚本文件
注意:本文件内容只适用于 EEPW-CPLD-DIY活动的板子。如果用于其它板子,请自己修改相关内容。
#CPLD 时钟脚
set_location_assignment PIN_12 -to clock
#CPLD 复位脚
set_location_assignment PIN_14 -to sys_rst
#行同步信号
set_location_assignment PIN_1 -to hsync
#场同步信号
set_location_assignment PIN_2 -to vsync
#RGB脚
set_location_assignment PIN_8 -to disp_RGB[0]
set_location_assignment PIN_5 -to disp_RGB[1]
set_location_assignment PIN_4 -to disp_RGB[2]
#拨码开关控制显示效果
set_location_assignment PIN_29 -to switch[0]
set_location_assignment PIN_30 -to switch[1]
9. 显示效果
9.1. 横条
9.2. 竖条
9.3. 方格
10. 参考资料
http://bbs.****.com/BLOG_ARTICLE_219303.HTM
http://www.epanorama.net/documents/pc/vga_timing.html
http://tinyvga.com/vga-timing/640x480@60Hz
http://share.eepw.com.cn/share/download/id/165148
27楼的内容,已经整理成word文档,上传到服务器。
http://share.eepw.com.cn/share/download/id/165361
51-FPGA 版主提供 640*480@60Hz的例程,可以从以下链接下载。
http://share.eepw.com.cn/share/download/id/165148
回复
有奖活动 | |
---|---|
【有奖活动——B站互动赢积分】活动开启啦! | |
【有奖活动】分享技术经验,兑换京东卡 | |
话不多说,快进群! | |
请大声喊出:我要开发板! | |
【有奖活动】EEPW网站征稿正在进行时,欢迎踊跃投稿啦 | |
奖!发布技术笔记,技术评测贴换取您心仪的礼品 | |
打赏了!打赏了!打赏了! |