做科研的时候从学校拿到一块基于Xilinx公司Spartan-6主芯片的FPGA开发板,因为之前一直在用Altera公司的FPGA,一开始接触它还真有点不太习惯。但毕竟核心的东西还是不会变的,于是按照惯例,先仔细瞄了瞄这块开发板,看看有哪些可用的资源--拨码开关、按键、LED、七段数码管、USB Host、USB UART、VGA、以太网接口,嗯哼,虽然比不上友晶的DE2那么强大,但是看来做一般的开发还是绰绰有余的。

  瞄完就是上网找资料了,首先找是板子的制造商--digilent,下载原理图,下载能用的开发工具,下载DEMO,然后就是学着怎么操作Xilinx的开发工具,将DEMO下载上去,确定板子有没有问题(这其间从熟悉的quartus转到陌生的Xilinx ISE着实花了我一番功夫,这里就不罗嗦了)。digilent上有一个DEMO挺有意思的,叫Nexys3_ISE_GPIO_UART的工程,它将拨码开关、按键、LED、七段数码管、USB UART这几种板子上的基础资源写进了一个工程里,效果好象是拨码开关控制LED,数码管循环显示几个单词,然后用USB数据线将UART口跟电脑相连,能通过按键控制向电脑发送特定的字符串,然后能在电脑上的超级终端接收到这个字符,看着挺牛的嘛!不过那总是人家的,要变成自己的还得自己动手!说干就干,那就拿他练手吧。

  按理来说这也不难吧,毕竟有现成的东西在,我开始也这么想的,不过当我打开那个DEMO里面的代码一看,顿时有种想骂人的冲动--居然是VHDL!我一开始学的就是Verilog,VHDL压根就没接触过,这两种语言差别还有点大呢!看这里面的代码,就像天书!要我看懂这些家伙我还不如自己研究呢(其实也没那么恐怖)!于是就准备自己写一份测试的DEMO,就当是练手了。

  原本的DEMO功能有点多,于是我就想把它分两部分来完成,把比较复杂的USB UART这部分单独拿出来,其他的放在一个DEMO里面。后来又想,既然有4个数码管,那就做个时钟吧,虽然没有小时部分,但作为练手还是无伤大雅的。这篇文章先说说这个数码管时钟,USB UART部分将在后面的文章中讨论。

  数码管时钟我想对于很多学电子的同学来说都不陌生了,特别是学过单片机的,这个东西是入门练手必备的。闲话不多说,先来说说这个原理。怎么点亮单个数码管我就不说了,无非分清共阴共阳,然后就是置高置低的问题。但是对于4个一起的数码管,要怎么控制他们看起来好像是在同时点亮的而且还能显示不同的内容就比较麻烦了。先上原理图:

  显然要控制一个数码管需要两部分的端口--CA...DP和AN0...AN3,我一般叫左边的为段选,右边的为位选。从原理图上来看,位选端接在PNP管的B极,要先让C、E极导通,相应的位选端应该是低电平,比如AN3...AN0四位为1110,就是选择了最后一个数码管。而当数码管选中时(即相应的三极管导通),数码管公共端接的是高电平,显然这些数码管应该是共阳的,于是CA...DP端的编码就可以按共阳数码管的编码方式做了。转换成Verilog代码,可以是一下的常数定义:

parameter AN1 = 'b1110,AN2 = 4'b1101,AN3 = 'b1011,AN4 = 4'b0111;//数码管位选定义
parameter zero = 'b0000_0011,one = 8'b1001_1111,two = 'b0010_0101,three = 8'b0000_1101,four = 'b1001_1001,
five = 'b0100_1001,six = 8'b0100_0001,seven = 'b0001_1111,eigth = 8'b0000_0001,nine = 'b0000_1001;//数码管段选定义

  好了,现在搞清楚了第一个问题--怎么将0-9几个数显示出来,接下来的问题是怎么让4个数码管看起来是一起显示的,而且还能显示不同的内容。这个问题相信做过单片机数码管实验的同学马上就猜到了,没错,原理就是利用人眼视觉的暂留性(我是这么叫的,再专业点就不是我研究的问题了)--当两个数码管之间的变化小于100ms(貌似是这么长时间)时,我们的眼睛是看不到他们之间的闪烁的。于是,当我们要显示1234这4个数时,我们可以在1ms时显示1,25ms时显示2,50ms时显示3,75ms时显示4,然后循环,看起来就是一起显示1234这4个数了。当然,我们的硬件可以做的更快,这个时间间隔可以根据实际情况选择更短。把它翻译成Verilog语言就有下面的程序段了:

//数码管刷新显示模块
always@(posedge div1000hz)
begin
if(rest)
begin
seg_count <= ;
an <= 'b0000;
seg <= 'b0000_0000;
end
else
begin
case(seg_count)
: begin
case(sec1)
: seg <= zero;
: seg <= one;
: seg <= two;
: seg <= three;
: seg <= four;
: seg <= five;
: seg <= six;
: seg <= seven;
: seg <= eigth;
: seg <= nine;
endcase
an <= AN1;
end
: begin
case(sec2)
: seg <= zero;
: seg <= one;
: seg <= two;
: seg <= three;
: seg <= four;
: seg <= five;
: seg <= six;
: seg <= seven;
: seg <= eigth;
: seg <= nine;
endcase
an <= AN2;
end
: begin
case(min1)
: seg <= zero;
: seg <= one;
: seg <= two;
: seg <= three;
: seg <= four;
: seg <= five;
: seg <= six;
: seg <= seven;
: seg <= eigth;
: seg <= nine;
endcase
an <= AN3;
end
: begin
case(min2)
: seg <= zero;
: seg <= one;
: seg <= two;
: seg <= three;
: seg <= four;
: seg <= five;
: seg <= six;
: seg <= seven;
: seg <= eigth;
: seg <= nine;
endcase
an <= AN4;
end
endcase
seg_count <= seg_count + ;
end
end

  好,现在剩最后一个问题了,那就是时钟控制部分。熟悉C的同学会说,那还不简单,分频计数,然后整除求余,将秒、分的十位各位分开不就可以了吗?没错,这是最简单的方式,但是,大家千万不要忽略了我们最后的代码是要变成硬件电路的。先不说Xilinx的IDE内嵌的XST综合器不支持对除号的综合,要实现除法必须要设计除法器(调用IP或自己设计),就算是设计出来了,一个除法器占用的硬件资源可能会比我们剩余部分电路占用的资源还多。从这方面出发考虑,我们要想想是不是有更好的方法实现它。其实只要我们稍微思考一下还是不难的,下面的代码就能很简洁的完成这部分的设计:

//时钟模块
always@(posedge div1hz)
begin
if(rest)
begin
sec1 <= ;
sec2 <= ;
min1 <= ;
min2 <= ;
end
else
begin
sec1 <= sec1 + ;
if(sec1 == )
begin
sec2 <= sec2 + ;
sec1 <= ;
end
if(sec2 == && sec1 == )
begin
min1 <= min1 + ;
sec2 <= ;
end
if(min1 == )
begin
min2 <= min2 + ;
min1 <= ;
end
if(min2 == && min1 == )
begin
min2 <= ;
end
end
end

可能我们会多写几行代码,但是最后设计出来的电路的性能往往会有大的飞跃,这是我们在做硬件开发时需要时常提醒自己的。

  好了,以上就是这个DEMO的关键部分,其实也是相当基础的,把它拿出来作为我的第一篇技术文章与大家分享,有不当之处还请大家指出。下面是完整的程序,中间还将LED显示加进去了,让八个LED间隔1s亮灭:

 `timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer: lwy
//
// Create Date: 00:33:08 11/09/2013
// Design Name:
// Module Name: segclk
// Project Name:
// Target Devices:
// Tool versions:
// Description:NEXYS3开发板测试程序,用数码管实现时钟功能
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////
module segclk(
clk,//时钟输入,100M
rest,//时钟复位信号,BTNU,高电平时复位
seg,//数码管段选
an,//数码管位选
led//8个LED
); input clk,rest;
output reg [:] seg;
output reg [:] an;
output reg [:] led; parameter AN1 = 'b1110,AN2 = 4'b1101,AN3 = 'b1011,AN4 = 4'b0111;//数码管位选定义
parameter zero = 'b0000_0011,one = 8'b1001_1111,two = 'b0010_0101,three = 8'b0000_1101,four = 'b1001_1001,
five = 'b0100_1001,six = 8'b0100_0001,seven = 'b0001_1111,eigth = 8'b0000_0001,nine = 'b0000_1001;//数码管段选定义
parameter led_on = 'b1111_1111,led_off = 8'b0000_0000;//LED亮灭定义 reg [:] div1hz_temp;//1秒分频计数
reg [:] div1000hz_temp;//数码管刷新时间分频计数
reg div1hz;//1Hz时钟
reg div1000hz;//数码管刷新时钟,频率为(100M/2^13)Hz
reg led_temp;//LED亮灭 reg [:] sec1;//秒个位
reg [:] sec2;//秒十位
reg [:] min1;//分个位
reg [:] min2;//分十位 reg [:] seg_count;//数码管位选定位 //1秒分频模块
always@(posedge clk)
begin
div1hz_temp <= div1hz_temp + ;
if(div1hz_temp == )
begin
div1hz_temp <= ;
div1hz <= ~div1hz;
end
end //数码管刷新时间分频模块
always@(posedge clk)
begin
div1000hz_temp <= div1000hz_temp + ;
if(div1000hz_temp == )
begin
div1000hz <= ~div1000hz;
end
end //时钟模块
always@(posedge div1hz)
begin
if(rest)
begin
sec1 <= ;
sec2 <= ;
min1 <= ;
min2 <= ;
end
else
begin
sec1 <= sec1 + ;
if(sec1 == )
begin
sec2 <= sec2 + ;
sec1 <= ;
end
if(sec2 == && sec1 == )
begin
min1 <= min1 + ;
sec2 <= ;
end
if(min1 == )
begin
min2 <= min2 + ;
min1 <= ;
end
if(min2 == && min1 == )
begin
min2 <= ;
end
end
end //LED亮灭控制模块
always@(posedge div1hz)
begin
led_temp = ~led_temp;
if(led_temp)
led <= led_on;
else
led <= led_off;
end //数码管刷新显示模块
always@(posedge div1000hz)
begin
if(rest)
begin
seg_count <= ;
an <= 'b0000;
seg <= 'b0000_0000;
end
else
begin
case(seg_count)
: begin
case(sec1)
: seg <= zero;
: seg <= one;
: seg <= two;
: seg <= three;
: seg <= four;
: seg <= five;
: seg <= six;
: seg <= seven;
: seg <= eigth;
: seg <= nine;
endcase
an <= AN1;
end
: begin
case(sec2)
: seg <= zero;
: seg <= one;
: seg <= two;
: seg <= three;
: seg <= four;
: seg <= five;
: seg <= six;
: seg <= seven;
: seg <= eigth;
: seg <= nine;
endcase
an <= AN2;
end
: begin
case(min1)
: seg <= zero;
: seg <= one;
: seg <= two;
: seg <= three;
: seg <= four;
: seg <= five;
: seg <= six;
: seg <= seven;
: seg <= eigth;
: seg <= nine;
endcase
an <= AN3;
end
: begin
case(min2)
: seg <= zero;
: seg <= one;
: seg <= two;
: seg <= three;
: seg <= four;
: seg <= five;
: seg <= six;
: seg <= seven;
: seg <= eigth;
: seg <= nine;
endcase
an <= AN4;
end
endcase
seg_count <= seg_count + ;
end
end endmodule

最新文章

  1. [MySQL] 分页优化
  2. linux文件系统节点详解
  3. Kingdom of Obsession---hdu5943(二分匹配)
  4. SQLite常用网址
  5. 手持PDA智能条码扫描RFID打印POS机
  6. Java中的大数处理类BigInteger和BigDecimar浅析
  7. 中断——中断描述符表的定义和初始化(二) (基于3.16-rc4)
  8. JavaScrip对象
  9. Scrapy 1.4 文档 05 命令行工具
  10. 什么是LDAP?
  11. Codeforces1063D Candies for Children 【分类讨论】【暴力】
  12. 廉价的SUP掌机拆解
  13. 【分布式事务】阿里fescar
  14. 使用 urllib 构造请求对象
  15. 纯css和js版下拉菜单
  16. Android图形动画
  17. [转载] 我的WafBypass之道(SQL注入篇)
  18. 009-mac下记事本工具 sublime text3
  19. 用CRF做命名实体识别(二)
  20. group by 和 聚合函数

热门文章

  1. HDU 2178.猜数字【分析能力练习】【读题能力练习】【8月10】
  2. 使用Yii 1.1框架搭建第一个web应用程序
  3. 去除icon图标特效,阴影,反光
  4. Windows平台Hadoop编译、安装、配置与运行(转)
  5. MVC3中给Html.TextAreaFor设置默认值(初始值)
  6. svn,git,scp,rsync,rake,ssh,wget,axel,aria2,nohup,grep,tail,siege,mitmproxy,ulimit,netstat,lsof
  7. DevExpress GridControl 动态创建字段及主细关系表过程
  8. html+css+js实现网页拼图游戏
  9. HTML5 CANVAS 弹幕插件
  10. 添加space_key, enter_key, clear_key, delete_key的处理。