基于FPGA的数字钟设计.docx
《基于FPGA的数字钟设计.docx》由会员分享,可在线阅读,更多相关《基于FPGA的数字钟设计.docx(22页珍藏版)》请在冰点文库上搜索。
基于FPGA的数字钟设计
基于FPGA的多功能数字钟
一、设计题目
基于XilinxFPGA的多功能数字钟设计
二、设计目的
1.掌握可编程逻辑器件的应用开发技术
——设计输入、编译、仿真和器件编程;
2.熟悉一种EDA软件使用;
3.掌握Verilog设计方法;
4.掌握分模块分层次的设计方法;
5.用Verilog完成一个多功能数字钟设计;
6.学会FPGA的仿真。
三、设计内容
设计实验项目九多功能电子钟
✧功能要求:
利用实验板设计实现一个能显示时分秒的多功能电子钟,具体要求为:
基本功能:
1)准确计时,以数字形式显示时、分、秒,可通过按键选择指示当前显示时间范围模式;
2)计时时间范围00:
00:
00-23:
59:
59
3)可实现校正时间功能;
4)可通过实现时钟复位功能:
00:
00:
00
扩展功能:
1)定时报:
时间自定(不要求改变),闹1分钟(1kHz)---利用实验板LED或外接电路实现。
2)仿广播电台正点报时:
XX:
59:
[51,53,55,57(500Hz);59(1kHz)]---利用实验板LED或外接电路实现。
3)报整点时数:
XX:
00:
[00.5-XX.5](1kHz),自动、手动---利用实验板LED或外接电路实现。
4)手动输入校时;
5)手动输入定时闹钟;
6)万年历;
7)其他扩展功能;
✧设计步骤与要求:
1)计算并说明采用Basys2实验板时钟50MHz实现系统功能的基本原理。
2)在XilinxISE13.1软件中,利用层次化方法,设计实现模一百计数及显示的电路系统,设计模块间的连接调用关系,编写并输入所设计的源程序文件。
3)对源程序进行编译及仿真分析(注意合理设置,以便能够在验证逻辑的基础上尽快得出仿真结果)。
4)输入管脚约束文件,对设计项目进行编译与逻辑综合,生成下载所需.bit类型文件。
5)在Basys2实验板上下载所生成的.bit文件,观察验证所设计的电路功能。
四、总体设计思路
主体分为分频模块,正常时间模块(包含两个模60计数器和一个模24计数器子模块),闹钟模块(分为一个模60计数器模块,一个模24计数器模块,四个比较器模块),电台报时模块,数码管显示模块(分为模式选择模块,片选信号及扫描程序模块,和译码模块)。
将各模块连接好各接线口即得到数字钟顶层模块complete.sch。
五、各模块设计及源代码
1.分频模块
分频模块要将50MHz的时钟信号分成三个分别为1Hz,500Hz,1kHz的三个脉冲信号,设置三个计数器,在三个频率信号分别对应的延时时间进行翻转,就可以得到三个不同的脉冲信号。
源代码如下:
//freq.v
modulefreq(clk,_1Hz,_500Hz,_1kHz);
inputclk;
output_1Hz,_500Hz,_1kHz;
reg_1Hz=0,_500Hz=0,_1kHz=0;
reg[24:
0]cnt1=0,cnt2=0,cnt3=0;
always@(posedgeclk)
begin
if(cnt1<25'd24999999)
//if(cnt1<25'd249)
//做test仿真时让变化更快
cnt1<=cnt1+1;//未达到计数时间计数器加一
else
begin
_1Hz<=~_1Hz;//达到计时时间,信号翻转且计数器归零
cnt1<=0;
end
end
always@(posedgeclk)
begin
if(cnt2<25'd49999)
//if(cnt2<25'd49)
cnt2<=cnt2+1;
else
begin
_500Hz<=~_500Hz;
cnt2<=0;
end
end
always@(posedgeclk)
begin
if(cnt3<25'd24999)
//if(cnt3<25'd25)
cnt3<=cnt3+1;
else
begin
_1kHz<=~_1kHz;
cnt3<=0;
end
end
endmodule
2.时钟正常显示模块
正常显示模块分为时分秒三个子模块,分别对应一个模24计数器和两个模60计数器。
模24计数器和模60计数器设计都BCD码来表示时分秒的值,每个分为高位和低位,均对应一个十进制的数。
对模24计数器,每个脉冲信号来临时当低位小于9时加一;等于9时高位加一且低位置零;且高位小于3,低位小于9,超过时高低位均置零;当高位等于2时,低位只能到3,同时复位nCR低电平时高低位均置零。
同理可得模60计数器的设计。
根据这个思路,得到模24计数器源代码如下:
//counter24.v
modulecounter24(CntH,CntL,nCR,EN,CP);
inputnCR,EN,CP;
output[3:
0]CntH,CntL;//分别为高位和低位
reg[3:
0]CntH,CntL;
always@(posedgeCPornegedgenCR)
begin
if(~nCR){CntH,CntL}<=8'h00;//复位键清零
elseif(~EN){CntH,CntL}<={CntH,CntL};//使能信号有效则暂停
elseif((CntH>2)||(CntL>9)||((CntH==2)&&(CntL>=3)))
{CntH,CntL}<=8'b00;//出现计数错误全部清零
elseif((CntH==2)&&(CntL<3))
CntL<=CntL+1;//超过20时计数方式
elseif(CntL==9)
beginCntH<=CntH+1'b1;
CntL<=4'b0;//一般情况下计数方式
end
else
CntL<=CntL+1'b1;
end
模60计数器代码如下:
//counterM60.v
modulecounterM60(CntMH,CntML,nCR,EN,CP);
inputCP,EN,nCR;
output[3:
0]CntMH,CntML;
reg[3:
0]CntMH=0,CntML=0;
always@(posedgeCPornegedgenCR)
begin
if(~nCR)
{CntMH,CntML}<=8'b00;
elseif(~EN)
{CntMH,CntML}<={CntMH,CntML};
elseif(CntML>9||CntMH>5||(CntMH==5&&CntML==9))
begin
{CntMH,CntML}<=8'h00;
end
elseif(CntMH<5&&CntML==9)
begin
CntMH<=CntMH+1'b1;
CntML<=4'b0;
end
else
CntML<=CntML+1'b1;
end
endmodule
而时钟计时就是调用两个模60计数器和一个模24计数器,但相应的CP信号是需要修改的使得秒的60计数一周期可以使分加一,同时分钟跑一个周期能使得时针加一。
同时按照设计要求需要加入按键调时功能,将两个按键AdjHr和AdjMin分别作为调时针和分针的按键,按下时时针和分钟的脉冲信号变为1Hz脉冲,由此时针分钟会以1Hz的频率改变而达到调时间的目的。
设置分钟和时钟的驱动信号分别为MinCP,HrCP,则可以设置MinCP=AdjMin?
_1Hz:
(Second==8'h59);HrCP=AdjHr?
_1Hz:
({Minute,Second}==16'h5959);
源代码如下:
//Top_clock.v
moduletop_clock(Hour,Minute,Second,_1Hz,nCR,AdjMin,AdjHr);
input_1Hz,nCR,AdjMin,AdjHr;
output[7:
0]Hour,Minute,Second;//分别为时分秒输出
wire[7:
0]Hour,Minute,Second;
supply1Vdd;//设置使能
wireMinCP,HrCP;//定义激励信号
counterM60UT1(Second[7:
4],Second[3:
0],nCR,Vdd,_1Hz);
counterM60UT2(Minute[7:
4],Minute[3:
0],nCR,Vdd,~MinCP);
counter24UT3(Hour[7:
4],Hour[3:
0],nCR,Vdd,~HrCP);
assignMinCP=AdjMin?
_1Hz:
(Second==8'h59);//分钟激励
assignHrCP=AdjHr?
_1Hz:
({Minute,Second}==16'h5959);//时钟激励
endmodule
3.仿电台报时模块
设置ALARM为报时输出,Minute和Second分别为分钟信号和秒钟信号,先用if-else语句来进行Minute是否为59的判断,之后再用case语句在Second为51,53,55,57时输出500Hz的信号,在59时输出为1kHz的信号,在板子上没有输出声音的设备,就接入LED来验证是否正常报时。
//radio.v
moduleradio(ALARM,Minute,Second,_1kHz,_500Hz);
input_1kHz,_500Hz;
input[7:
0]Minute,Second;
outputALARM;
regALARM;
always@(MinuteorSecond)
if(Minute==8'h59)
case(Second)
8'h51,
8'h53,
8'h55,
8'h57:
ALARM=_500Hz;//500Hz方式输出,报时
8'h59:
ALARM=_1kHz;//59s以1kHz输出
default:
ALARM=1'b0;//一般不输出
endcase
elseALARM=1'b0;
endmodule
4.闹钟模块
闹钟模块分为设定闹钟(包含两个按键),闹钟正常响铃,一个关闭闹钟的按键CtrRing。
设定闹钟同样可以以1Hz脉冲为激励信号,其主体电路为一个以1Hz为激励信号的受SetMinkey控制的模60计数器和一个受SetHrkey控制的模24计数器,调用之前已经写好的模块即可。
同时响铃设置为ALARM_clock,受按键CtrRing控制。
设置四个比较器,分别为小时高位比较,小时低位比较,分钟高位比较和分钟低位比较。
当四个比较信号都为1时才会闹钟响铃。
比较器的设计很简单直接给出源代码:
//comparator.v
modulecomparator(EQU,A,B);
input[3:
0]A,B;
outputEQU;
assignEQU=(A==B);
endmodule
而闹钟主体源代码如下:
//ring.v
modulering(ALARM_clock,Set_Hr,Set_Min,Hour,Minute,Second,SetHrkey,SetMinkey,_1kHz,_500Hz,_1Hz,CtrRing);
outputALARM_clock;
output[7:
0]Set_Hr,Set_Min;//输出的闹钟设定时间
wireALARM_clock;
wire[7:
0]Set_Hr,Set_Min;
input_1kHz,_500Hz,_1Hz;
input[7:
0]Hour,Minute,Second;
inputSetHrkey,SetMinkey,CtrRing;//闹钟设定按键和关闭闹钟按键
supply1Vdd;//设置高电平
wireHrH_Cop,MinH_Cop,HrL_Cop,MinL_Cop;//中间变量,为设定闹钟时间和正常时间比较
wiretime_EQU;
counterM60SU1(Set_Min[7:
4],Set_Min[3:
0],Vdd,SetMinkey,_1Hz);//调用模60计数器模块
counter24SU2(Set_Hr[7:
4],Set_Hr[3:
0],Vdd,SetHrkey,_1Hz);
comparatorSU4(HrH_Cop,Set_Hr[7:
4],Hour[7:
4]);//调用比较器模块
comparatorSU5(HrL_Cop,Set_Hr[3:
0],Hour[3:
0]);
comparatorSU6(MinH_Cop,Set_Min[7:
4],Minute[7:
4]);
comparatorSU7(MinL_Cop,Set_Min[3:
0],Minute[3:
0]);
assigntime_EQU=(HrH_Cop&&HrL_Cop&&MinH_Cop&&MinL_Cop);//时间比较信号
assignALARM_clock=CtrRing?
(time_EQU&&(((Second[0]==1'b1)&&_500Hz)||((Second[0]==1'b0)&&_1kHz))):
1'b0;//设置闹铃输出
endmodule
5.数码管显示模块
之前的模块设置了两个音频信号输出ALARM和ALARM_clock,这里可以统一起来。
数码管既要显示闹钟设定时间,也要显示时钟正常显示时间。
设置一个按键mode来切换显示模式,为0时显示正常时间,为1时显示闹钟定时时间。
设置LED_Hr和LED_Min为得到的当前显示的时和分。
还有由LED_Hr和LED_Min到数码管的显示需要设计扫描程序,分为片选和译码模块。
分别贴出源代码如下:
模式选择模块:
//mode.v
modulemode(mode,Hour,Minute,Second,Set_Hr,Set_Min,LED_Hr,LED_Min,ALARM,ALARM_clock,ALARMout);
inputmode,ALARM,ALARM_clock;
input[7:
0]Hour,Minute,Second,Set_Hr,Set_Min;
output[7:
0]LED_Hr,LED_Min;
outputALARMout;
wireALARM,ALARM_clock;
wire[7:
0]Set_Hr,Set_Min;
assignALARMout=ALARM||ALARM_clock;//时钟响铃,闹钟和电台报时均有效
assignLED_Hr=mode?
Set_Hr:
Hour;//选择当前显示的小时
assignLED_Min=mode?
Set_Min:
Minute;//选择当前显示的分
endmodule
之后是译码模块
在配置引脚时将小数点DP配置为最高位,其余G,F,E,D,C,B,A按从高到低的顺序配置,低电平段码管亮,由此可以写出对应段码,源代码如下:
//translate.v
moduletranslate(Seg_in,Seg_outH,Seg_outL);
input[7:
0]Seg_in;//输入的数码
output[7:
0]Seg_outH;//翻译后的高位段码
output[7:
0]Seg_outL;//翻译后的低位段码
wire[7:
0]Seg_outH,Seg_outL;
reg[7:
0]Seg_outH1,Seg_outL1;
always@(Seg_in[7:
4])//高位译码
case(Seg_in[7:
4])
4'b0000:
Seg_outH1=8'b11000000;//0的段码
4'b0001:
Seg_outH1=8'b11111001;//1
4'b0010:
Seg_outH1=8'b10100100;//2
4'b0011:
Seg_outH1=8'b10110000;/3
4'b0100:
Seg_outH1=8'b10011001;//4
4'b0101:
Seg_outH1=8'b10010010;//5
4'b0110:
Seg_outH1=8'b10000010;//6
4'b0111:
Seg_outH1=8'b11111000;//7
4'b1000:
Seg_outH1=8'b10000000;//8
4'b1001:
Seg_outH1=8'b10010000;//9
default:
Seg_outH1=8'b10100011;//出错显示
endcase
always@(Seg_in[3:
0])//低位译码
case(Seg_in[3:
0])
4'b0000:
Seg_outL1=8'b11000000;
4'b0001:
Seg_outL1=8'b11111001;
4'b0010:
Seg_outL1=8'b10100100;
4'b0011:
Seg_outL1=8'b10110000;
4'b0100:
Seg_outL1=8'b10011001;
4'b0101:
Seg_outL1=8'b10010010;
4'b0110:
Seg_outL1=8'b10000010;
4'b0111:
Seg_outL1=8'b11111000;
4'b1000:
Seg_outL1=8'b10000000;
4'b1001:
Seg_outL1=8'b10010000;
default:
Seg_outL1=8'b10100011;
endcase
assignSeg_outH=Seg_outH1;
assignSeg_outL=Seg_outL1;
endmodule
之后是数码管片选信号的设置和扫描程序,以500Hz为扫描频率。
代码如下:
//trans.v
moduletrans(_500Hz,LED_Hr,LED_Min,SGG,SELE);
input[7:
0]LED_Hr,LED_Min;
input_500Hz;
output[7:
0]SGG;
output[3:
0]SELE;
wire[7:
0]SGML,SGMH,SGHL,SGHH;//经过翻译的段码
reg[1:
0]count=2'b00;//扫描的计数控制
wire[3:
0]SELE;//片选信号
wire[7:
0]SGG;//输入数码管的输出信号
reg[7:
0]SG;//数码管段码中间变量
reg[3:
0]SEL=1110;//片选中间变量
translateST1(LED_Hr,SGHH,SGHL);
translateST2(LED_Min,SGMH,SGML);
always@(posedge_500Hz)
case(count)
2'b00:
beginSG=SGML;//选中第一个数码管
SEL=4'b1110;
count=count+1'b1;//扫描信号不断加一,从而实现扫描
end
2'b01:
beginSG=SGMH;//选中第二个管
SEL=4'b1101;
count=count+1'b1;
end
2'b10:
beginSG=SGHL;//选中第三个管
SEL=4'b1011;
count=count+1'b1;
end
2'b11:
beginSG=SGHH;//选中第四个管
SEL=4'b0111;
count=count+1'b1;
end
endcase
assignSELE=SEL;//设置片选信号
assignSGG=SG;//设置数码管显示信号
endmodule
6、多功能数字钟顶层模块设计
采用图形设计方法,将以上已经写好的各模块都选择createschematicsymbol封装成各个器件。
名字设置为complete.sch,在这个文件里将各个器件拖出来进行各引脚连线和标记输入输出端口。
由于图太小截图看不清楚,只能先给个大概如下:
其生成的vf文件如下:
//complete.vf
modulecomplete(XLXN_10,
XLXN_15,
XLXN_16,
XLXN_17,
XLXN_19,
XLXN_20,
XLXN_21,
XLXN_33,
XLXN_32,
XLXN_39,
XLXN_40);
inputXLXN_10;
inputXLXN_15;
inputXLXN_16;
inputXLXN_17;
inputXLXN_19;
inputXLXN_20;
inputXLXN_21;
inputXLXN_33;
outputXLXN_32;
output[7:
0]XLXN_39;
output[3:
0]XLXN_40;
wireXLXN_4;
wireXLXN_5;
wire[7:
0]XLXN_6;
wire[7:
0]XLXN_7;
wire[7:
0]XLXN_8;
wireXLXN_11;
wire[7:
0]XLXN_13;
wire[7:
0]XLXN_14;
wireXLXN_22;
wireXLXN_24;
wire[7:
0]XLXN_37;
wire[7:
0]XLXN_38;
freqXLXI_1(.clk(XLXN_10),
._1Hz(XLXN_24),
._1kHz(XLXN_4),
._500Hz(XLXN_5));
radioXLXI_5(.Minute(XLXN_7[7:
0]),
.Second(XLXN_8[7:
0]),
._1kHz(XLXN_4),
._500Hz(XLXN_5),
.ALARM(XLXN_11));
ringXLXI_6(.CtrRing(XLXN_21),
.Hour(XLXN_6[7:
0]),
.Minute(XLXN_7[7:
0]),
.Second(XLXN_8[7:
0]),
.SetHrkey(XLXN_19),
.SetMinkey(XLXN_20),
._1Hz(XLXN_24),
._1kHz(XLXN_4),
._500Hz(XLXN_5),
.ALARM_clock(XLXN_22),
.Set_Hr(XLXN_13[7:
0]),
.Set_Min(XLXN_14[7:
0]));
top_clockXLXI_7(.AdjHr(XLXN_16),
.AdjMin(XLXN_15),
.nCR(XLXN_17),
._1Hz(XLXN_24),
.Hour(XLXN_6[7:
0]),
.Minute(XLXN_7[7:
0]),
.Second(XLXN_8[7:
0]));
modeXLXI_8(.ALARM(XLXN_11),
.ALARM_clock(XLXN_22),
.Hour(XLXN_6[7:
0]),
.Minute(XLXN_7[7:
0]),
.mode(XLXN_33