作科研的时候从学校拿到一块基于Xilinx公司Spartan-6主芯片的FPGA开发板,由于以前一直在用Altera公司的FPGA,一开始接触它还真有点不太习惯。但毕竟核心的东西仍是不会变的,因而按照惯例,先仔细瞄了瞄这块开发板,看看有哪些可用的资源--拨码开关、按键、LED、七段数码管、USB Host、USB UART、VGA、以太网接口,嗯哼,虽然比不上友晶的DE2那么强大,可是看来作通常的开发仍是绰绰有余的。ide
瞄完就是上网找资料了,首先找是板子的制造商--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 = 4'b1110,AN2 = 4'b1101,AN3 = 4'b1011,AN4 = 4'b0111;//数码管位选定义 parameter zero = 8'b0000_0011,one = 8'b1001_1111,two = 8'b0010_0101,three = 8'b0000_1101,four = 8'b1001_1001, five = 8'b0100_1001,six = 8'b0100_0001,seven = 8'b0001_1111,eigth = 8'b0000_0001,nine = 8'b0000_1001;//数码管段选定义
好了,如今搞清楚了第一个问题--怎么将0-9几个数显示出来,接下来的问题是怎么让4个数码管看起来是一块儿显示的,并且还能显示不一样的内容。这个问题相信作过单片机数码管实验的同窗立刻就猜到了,没错,原理就是利用人眼视觉的暂留性(我是这么叫的,再专业点就不是我研究的问题了)--当两个数码管之间的变化小于100ms(貌似是这么长时间)时,咱们的眼睛是看不到他们之间的闪烁的。因而,当咱们要显示1234这4个数时,咱们能够在1ms时显示1,25ms时显示2,50ms时显示3,75ms时显示4,而后循环,看起来就是一块儿显示1234这4个数了。固然,咱们的硬件能够作的更快,这个时间间隔能够根据实际状况选择更短。把它翻译成Verilog语言就有下面的程序段了:spa
//数码管刷新显示模块 always@(posedge div1000hz) begin if(rest) begin seg_count <= 0; an <= 4'b0000; seg <= 8'b0000_0000; end else begin case(seg_count) 0 : begin case(sec1) 0 : seg <= zero; 1 : seg <= one; 2 : seg <= two; 3 : seg <= three; 4 : seg <= four; 5 : seg <= five; 6 : seg <= six; 7 : seg <= seven; 8 : seg <= eigth; 9 : seg <= nine; endcase an <= AN1; end 1 : begin case(sec2) 0 : seg <= zero; 1 : seg <= one; 2 : seg <= two; 3 : seg <= three; 4 : seg <= four; 5 : seg <= five; 6 : seg <= six; 7 : seg <= seven; 8 : seg <= eigth; 9 : seg <= nine; endcase an <= AN2; end 2 : begin case(min1) 0 : seg <= zero; 1 : seg <= one; 2 : seg <= two; 3 : seg <= three; 4 : seg <= four; 5 : seg <= five; 6 : seg <= six; 7 : seg <= seven; 8 : seg <= eigth; 9 : seg <= nine; endcase an <= AN3; end 3 : begin case(min2) 0 : seg <= zero; 1 : seg <= one; 2 : seg <= two; 3 : seg <= three; 4 : seg <= four; 5 : seg <= five; 6 : seg <= six; 7 : seg <= seven; 8 : seg <= eigth; 9 : seg <= nine; endcase an <= AN4; end endcase seg_count <= seg_count + 1; end end
好,如今剩最后一个问题了,那就是时钟控制部分。熟悉C的同窗会说,那还不简单,分频计数,而后整除求余,将秒、分的十位各位分开不就能够了吗?没错,这是最简单的方式,可是,你们千万不要忽略了咱们最后的代码是要变成硬件电路的。先不说Xilinx的IDE内嵌的XST综合器不支持对除号的综合,要实现除法必需要设计除法器(调用IP或本身设计),就算是设计出来了,一个除法器占用的硬件资源可能会比咱们剩余部分电路占用的资源还多。从这方面出发考虑,咱们要想一想是否是有更好的方法实现它。其实只要咱们稍微思考一下仍是不难的,下面的代码就能很简洁的完成这部分的设计:翻译
//时钟模块 always@(posedge div1hz) begin if(rest) begin sec1 <= 0; sec2 <= 0; min1 <= 0; min2 <= 0; end else begin sec1 <= sec1 + 1; if(sec1 == 9) begin sec2 <= sec2 + 1; sec1 <= 0; end if(sec2 == 5 && sec1 == 9) begin min1 <= min1 + 1; sec2 <= 0; end if(min1 == 9) begin min2 <= min2 + 1; min1 <= 0; end if(min2 == 5 && min1 == 9) begin min2 <= 0; end end end
可能咱们会多写几行代码,可是最后设计出来的电路的性能每每会有大的飞跃,这是咱们在作硬件开发时须要时常提醒本身的。设计
好了,以上就是这个DEMO的关键部分,其实也是至关基础的,把它拿出来做为个人第一篇技术文章与你们分享,有不当之处还请你们指出。下面是完整的程序,中间还将LED显示加进去了,让八个LED间隔1s亮灭:rest
1 `timescale 1ns / 1ps 2 ////////////////////////////////////////////////////////////////////////////////// 3 // Company: 4 // Engineer: lwy 5 // 6 // Create Date: 00:33:08 11/09/2013 7 // Design Name: 8 // Module Name: segclk 9 // Project Name: 10 // Target Devices: 11 // Tool versions: 12 // Description:NEXYS3开发板测试程序,用数码管实现时钟功能 13 // 14 // Dependencies: 15 // 16 // Revision: 17 // Revision 0.01 - File Created 18 // Additional Comments: 19 // 20 ////////////////////////////////////////////////////////////////////////////////// 21 module segclk( 22 clk,//时钟输入,100M 23 rest,//时钟复位信号,BTNU,高电平时复位 24 seg,//数码管段选 25 an,//数码管位选 26 led//8个LED 27 ); 28 29 input clk,rest; 30 output reg [7:0] seg; 31 output reg [3:0] an; 32 output reg [7:0] led; 33 34 parameter AN1 = 4'b1110,AN2 = 4'b1101,AN3 = 4'b1011,AN4 = 4'b0111;//数码管位选定义 35 parameter zero = 8'b0000_0011,one = 8'b1001_1111,two = 8'b0010_0101,three = 8'b0000_1101,four = 8'b1001_1001, 36 five = 8'b0100_1001,six = 8'b0100_0001,seven = 8'b0001_1111,eigth = 8'b0000_0001,nine = 8'b0000_1001;//数码管段选定义 37 parameter led_on = 8'b1111_1111,led_off = 8'b0000_0000;//LED亮灭定义 38 39 reg [25:0] div1hz_temp;//1秒分频计数 40 reg [12:0] div1000hz_temp;//数码管刷新时间分频计数 41 reg div1hz;//1Hz时钟 42 reg div1000hz;//数码管刷新时钟,频率为(100M/2^13)Hz 43 reg led_temp;//LED亮灭 44 45 reg [3:0] sec1;//秒个位 46 reg [3:0] sec2;//秒十位 47 reg [3:0] min1;//分个位 48 reg [3:0] min2;//分十位 49 50 reg [1:0] seg_count;//数码管位选定位 51 52 //1秒分频模块 53 always@(posedge clk) 54 begin 55 div1hz_temp <= div1hz_temp + 1; 56 if(div1hz_temp == 50000000) 57 begin 58 div1hz_temp <= 0; 59 div1hz <= ~div1hz; 60 end 61 end 62 63 //数码管刷新时间分频模块 64 always@(posedge clk) 65 begin 66 div1000hz_temp <= div1000hz_temp + 1; 67 if(div1000hz_temp == 8191) 68 begin 69 div1000hz <= ~div1000hz; 70 end 71 end 72 73 //时钟模块 74 always@(posedge div1hz) 75 begin 76 if(rest) 77 begin 78 sec1 <= 0; 79 sec2 <= 0; 80 min1 <= 0; 81 min2 <= 0; 82 end 83 else 84 begin 85 sec1 <= sec1 + 1; 86 if(sec1 == 9) 87 begin 88 sec2 <= sec2 + 1; 89 sec1 <= 0; 90 end 91 if(sec2 == 5 && sec1 == 9) 92 begin 93 min1 <= min1 + 1; 94 sec2 <= 0; 95 end 96 if(min1 == 9) 97 begin 98 min2 <= min2 + 1; 99 min1 <= 0; 100 end 101 if(min2 == 5 && min1 == 9) 102 begin 103 min2 <= 0; 104 end 105 end 106 end 107 108 //LED亮灭控制模块 109 always@(posedge div1hz) 110 begin 111 led_temp = ~led_temp; 112 if(led_temp) 113 led <= led_on; 114 else 115 led <= led_off; 116 end 117 118 //数码管刷新显示模块 119 always@(posedge div1000hz) 120 begin 121 if(rest) 122 begin 123 seg_count <= 0; 124 an <= 4'b0000; 125 seg <= 8'b0000_0000; 126 end 127 else 128 begin 129 case(seg_count) 130 0 : begin 131 case(sec1) 132 0 : seg <= zero; 133 1 : seg <= one; 134 2 : seg <= two; 135 3 : seg <= three; 136 4 : seg <= four; 137 5 : seg <= five; 138 6 : seg <= six; 139 7 : seg <= seven; 140 8 : seg <= eigth; 141 9 : seg <= nine; 142 endcase 143 an <= AN1; 144 end 145 1 : begin 146 case(sec2) 147 0 : seg <= zero; 148 1 : seg <= one; 149 2 : seg <= two; 150 3 : seg <= three; 151 4 : seg <= four; 152 5 : seg <= five; 153 6 : seg <= six; 154 7 : seg <= seven; 155 8 : seg <= eigth; 156 9 : seg <= nine; 157 endcase 158 an <= AN2; 159 end 160 2 : begin 161 case(min1) 162 0 : seg <= zero; 163 1 : seg <= one; 164 2 : seg <= two; 165 3 : seg <= three; 166 4 : seg <= four; 167 5 : seg <= five; 168 6 : seg <= six; 169 7 : seg <= seven; 170 8 : seg <= eigth; 171 9 : seg <= nine; 172 endcase 173 an <= AN3; 174 end 175 3 : begin 176 case(min2) 177 0 : seg <= zero; 178 1 : seg <= one; 179 2 : seg <= two; 180 3 : seg <= three; 181 4 : seg <= four; 182 5 : seg <= five; 183 6 : seg <= six; 184 7 : seg <= seven; 185 8 : seg <= eigth; 186 9 : seg <= nine; 187 endcase 188 an <= AN4; 189 end 190 endcase 191 seg_count <= seg_count + 1; 192 end 193 end 194 195 endmodule