资源描述
ISE实现多功能数字钟设计
一、 任务要求
用FPGA器件和EDA技术实现多功能数字钟的设计。
基本功能要求:能显示小时、分钟、秒钟(时、分用7段LED显示器,秒用LED灯)。
小时计数器为同步24进制;
要求手动校时、校分。
扩展功能要求:任意时刻闹钟;
小时显示(12/24)切换电路
自动报整点时数。
二、 建立工程
在ISE 14,9软件中建立名为clock 的工程文件。芯片系列选择Spatan3E,具体芯片型号选择XC3S100E,封装类型选择CP132,速度信息选择-5。
三、 原理设计
四、 顶层模块设计
创建名为top_clock的文件,本设计中顶层模块用于调用各个子模块,以与将闹钟与整点报时模块综合在内,顶层源码如下:
module top_clock(
input Hchange, //24小时,12小时切换信号
input Change, //用来进行时分和秒的显示切换
input CLK_50, //50MHz时钟
input nCR,EN,Clock_EN,
input Adj_Min,Adj_Hour,Adj_Clock, //使能信号,小时分钟调时允许信号,闹钟使能信号(拨钮开关)
output [6:0] HEX0,
output reg Led_Alarm,
output reg [3:0] HEX //共阳极数码管对应端
);
reg Alarm;
wire ENM_L,ENM_H,ENH;
wire [7:0] Hour_24,TMinute,TSecond,CHour,CMinute;//中间变量声明,正常时钟变量和闹钟时钟变量
wire [7:0] Hour_12,Display_HourT,Display_HourAdjust;
reg [3:0] bcd=4'b0000; //记载数码管所要显示的数据
reg [7:0] Display_Hour,Minute;
reg LD_6_RADIO;
reg [7:0] sum,counter;
supply1 Vdd;
wire CP_1Hz;
//===========分频=============
Divider50MHz U0(.CLK_50M(CLK_50),
.nCLR(nCR),
.CLK_1HzOut(CP_1Hz));//用以时钟计数的CP
defparam U0.N = 25,
U0.CLK_Freq = 50000000,
U0.OUT_Freq = 1;
Divider50MHz U1(.CLK_50M(CLK_50),
.nCLR(nCR),
.CLK_1HzOut(CP_200Hz));//用以动态扫描的CP,供给数码管
defparam U1.N = 18,
U1.CLK_Freq = 50000000,
U1.OUT_Freq = 200;
//===========60进制秒计数器=========
Scounter10 S0(TSecond[3:0],nCR,EN,CP_1Hz);//秒:个位
Scounter6 S1(TSecond[7:4],nCR,(TSecond[3:0]==4'h9),CP_1Hz);//秒:十位
//===========60进制分计数器=========
Mcounter10 M0(TMinute[3:0],nCR,ENM_L,EN,CP_1Hz);//分:个位
Mcounter6 M1(TMinute[7:4],nCR,ENM_H,EN,CP_1Hz);//分:十位
assign ENM_L=Adj_Min?Vdd:(TSecond==8'h59);//分钟按书上CP调时
assign ENM_H=(Adj_Min&&(TMinute[3:0]==4'h9))||(TMinute[3:0]==4'h9)&&(TSecond==8'h59); //24小时制
counter24 H0(Hour_24[7:4],Hour_24[3:0],nCR,ENH,EN,CP_1Hz);
assign ENH = Adj_Hour?Vdd:((TMinute==8'h59)&&(TSecond==8'h59));
//===========12小时与24小时进制切换控制==========
assign Display_HourAdjust=((Hour_24==8'h20)||(Hour_24==8'h21))?(Hour_24-24):(Hour_24-18);
assign Hour_12 = (Hour_24<8'h13)?Hour_24:Display_HourAdjust;
assign Display_HourT = Hchange?Hour_12:Hour_24;
//===========闹钟============
//----------时钟秒---------
//counter60 CCS(nCR,CP_1Hz,EN,CSecond[7:4],CSecond[3:0]);
//----------时钟分---------
counter60 CCM(nCR,CP_1Hz,CMin_EN,CMinute[7:4],CMinute[3:0]);
//--产生分使能信号--
assign CMin_EN = (!EN && Adj_Clock && Adj_Min);
//----------时钟时---------
Counter24C CCH(nCR,CP_1Hz,CHour_EN,CHour[7:4],CHour[3:0]);
//--产生时使能信号--
assign CHour_EN = (!EN && Adj_Clock && Adj_Hour);
//--闹钟响--
always@(EN or Clock_EN) //闹钟开关
begin
if(EN && Clock_EN && (CHour == Display_HourT) && (CMinute == TMinute))Alarm <= 1;
else Alarm <= 0;
end
always@(posedge CLK_50 or negedge EN or negedge Alarm) //表示闹钟的LED
begin
if(~EN) Led_Alarm <= 0;
else
begin
if(~Alarm) Led_Alarm <= 0;
else Led_Alarm <= ~Led_Alarm;
end
end
//alarm_clock AL0(Hour24,Minute,CP_1Hz,Set_Alarm,Close_clock,nCR,KeySet_Hour_ev,KeySet_Minute_ev,LD_7,Alarm_Hour,Alarm_Minute);
//===========数码显示=========
always@(Adj_Clock)//确定数码管显示闹钟还是正常时钟
begin
if(Adj_Clock) begin Display_Hour <= CHour;Minute <= CMinute;end
else begin Display_Hour <= Display_HourT;Minute <= TMinute;end
end
always@(posedge CP_200Hz)
begin
if(Change==1) //数码管进行时分显示
begin
case(HEX)
4'b1110: begin HEX<=4'b0111; bcd<= Display_Hour [7:4]; end //第一根数码管显示小时十位
4'b0111: begin HEX<=4'b1011; bcd<= Display_Hour [3:0]; end //第二根显示小时个位
4'b1011: begin HEX<=4'b1101; bcd<= Minute [7:4]; end //第三根显示分钟十位
4'b1101: begin HEX<=4'b1110; bcd<= Minute [3:0]; end //第四根显示分钟个位
default: begin HEX<=0111; bcd<=Display_Hour [7:4]; end
endcase
end
else //数码管进行秒显示,change为低电平时显示秒
begin
case(HEX)
4'b1110: begin HEX<=4'b1101; bcd<= TSecond [7:4]; end //第三根显示秒十位
4'b1101: begin HEX<=4'b1110; bcd<= TSecond [3:0]; end //第四根显示秒个位
default: begin HEX<=1101; bcd<= TSecond [7:4]; end
endcase
end
end
SEG7_LUT L0(HEX0,bcd); //调用数码管子函数
//======整点报时==========
assign LD_6 = LD_6_RADIO;
always@(CP_1Hz)
begin
if((Minute[7:0] == 8'h00) && (counter[7:0] < (Hour_24[7:4]*10 + Hour_24[3:0])))
begin
LD_6_RADIO <= CP_1Hz;
end
else
begin
LD_6_RADIO <= 0;
end
end
always@(posedge CP_1Hz)
if(Minute[7:0]==8'h00)
begin
counter[7:0]<=counter[7:0]+1'b1;
end
else
begin
counter[7:0]<=8'h00;
end
endmodule
五、 顶层模块设计图
六、 子模块设计
1、 50MHz分频器
module Divider50MHz(CLK_50M,nCLR,CLK_1HzOut);
parameter N = 25; //位宽
parameter CLK_Freq = 50000000; //50MHz时钟输入
parameter OUT_Freq = 1; //1Hz时钟输出
input nCLR,CLK_50M; //输入端口说明
output reg CLK_1HzOut; //输出端口说明
reg [N-1:0] Count_DIV; //内部节点,存放计数器的输出值
always@(posedge CLK_50M or negedge nCLR)
begin
if(!nCLR) begin CLK_1HzOut <= 0; Count_DIV <= 0; end
else begin
if(Count_DIV <(CLK_Freq/(2*OUT_Freq)-1))//计数器模
Count_DIV <= Count_DIV + 1'b1; //分频器计数加1
else begin
Count_DIV <= 0; //分频器输出清零
CLK_1HzOut <= ~CLK_1HzOut; //输出信号取反
end
end
end
endmodule
2、 秒模10计数器
module Scounter10(Q,nCR,EN,CP);
input CP,nCR,EN;
output Q;
reg [3:0] Q;
always @(posedge CP or negedge nCR)
begin
if(~nCR) Q <= 4'b0000;//异步清零
else if(~EN) Q <= Q; //暂停计数
else if(Q==4'b1001) Q <= 4'b0000;
else Q <= Q + 1'b1;
end
3、 秒模6计数器
module Scounter6(Q,nCR,EN,CP);
input CP,nCR,EN;
output Q;
reg [3:0] Q;
always @(posedge CP or negedge nCR)
begin
if(~nCR) Q <= 4'b0000;//异步清零
else if(~EN) Q <= Q; //暂停计数
else if(Q==4'b0101) Q <= 4'b0000;
else Q <= Q + 1'b1;
end
4、 分模10计数器
module Mcounter10(Q,nCR,EN1,EN2,CP);
input CP,nCR,EN1,EN2;
output Q;
reg [3:0] Q;
always @(posedge CP or negedge nCR)
begin
if(~nCR) Q <= 4'b0000;//异步清零
else if(~EN1||!EN2) Q <= Q; //暂停计数
else if(Q==4'b1001) Q <= 4'b0000;
else Q <= Q + 1'b1;
end
5、 分模6计数器
module Mcounter6(Q,nCR,EN1,EN2,CP);
input CP,nCR,EN1,EN2;
output Q;
reg [3:0] Q;
always @(posedge CP or negedge nCR)
begin
if(~nCR) Q <= 4'b0000;//异步清零
else if(~EN1||~EN2) Q <= Q; //暂停计数
else if(Q==4'b0101) Q <= 4'b0000;
else Q <= Q + 1'b1;
end
6、 模24计数器
module counter24(CntH,CntL,nCR,EN1,EN2,CP);
input CP,nCR,EN1,EN2;
output reg [3:0] CntH,CntL;//小时的十位和个位输出
always@(posedge CP or negedge nCR)
begin
if(~nCR) {CntH,CntL} <= 8'h00; //异步清零
else if(~EN1||~EN2) {CntH,CntL} <= {CntH,CntL};//暂停计数
else if((CntH)>2||(CntL>9)||(CntH)==2&&(CntL)>=3)
{CntH,CntL} <= 8'h00; //对小时计数器出错时的处理
else if((CntH)==2&&(CntL)<3) //进行20~23计数
begin CntH <=CntH; CntL <= CntL + 1'b1; end
else if(CntL==9) //小时十位的计数
begin CntH <=CntH + 1'b1; CntL <= 4'b0000; end
else
begin CntH <= CntH; CntL <= CntL + 1'b1; end
end
endmodule
7、 模60计数器
module counter60(nCLR,Clk,EN,CntH,CntL);
input nCLR,Clk,EN;
output reg [3:0] CntH,CntL;
always@(posedge Clk or negedge nCLR)
begin
if(~nCLR)
{CntH,CntL} <= 0; //异步清零
else if(~EN)
{CntH,CntL} <= {CntH,CntL}; //暂停信号
else if(((CntH > 5)||(CntL > 9))||((CntH == 5)&&(CntL == 9)))
{CntH,CntL} <= 8'h00; //异常处理
else if(CntL == 9)
begin CntH <= CntH + 1'b1;CntL <= 0;end //十位计数
else
begin CntH <= CntH;CntL <= CntL + 1'b1;end //个位计数
end
endmodule
8、 数码管显示
module SEG7_LUT(oSEG,iDIG);
input [3:0] iDIG; //二进制输入
output reg [6:0] oSEG; //7段码输出
always@(iDIG)
begin
case(iDIG)
4'h0: oSEG = 7'b000_0001;
4'h1: oSEG = 7'b100_1111;
4'h2: oSEG = 7'b001_0010;
4'h3: oSEG = 7'b000_0110;
4'h4: oSEG = 7'b100_1100;
4'h5: oSEG = 7'b010_0100;
4'h6: oSEG = 7'b010_0000;
4'h7: oSEG = 7'b000_1111;
4'h8: oSEG = 7'b000_0000;
4'h9: oSEG = 7'b000_0100;
default: oSEG=7'b1111111;
endcase
end
endmodule
七、 各模块仿真
1、 模10计数器
测试代码:
// Inputs
reg nCR;
reg EN;
reg CP;
// Outputs
wire [3:0] Q;
// Instantiate the Unit Under Test (UUT)
counter10 uut (
.Q(Q),
.nCR(nCR),
.EN(EN),
.CP(CP)
);
parameter PERIOD =40;//时钟信号周期设置为40ns
always begin
CP=1'b0;
#(PERIOD/2) CP=1'b1;
#(PERIOD/2);
end
initial begin
// Initialize Inputs
nCR = 0;
EN = 1;
CP = 1;
// Wait 100 ns for global reset to finish
#100;
nCR=1;
// Add stimulus here
end
endmodule
2、 模6计数器
测试代码:
// Inputs
reg nCR;
reg EN;
reg CP;
// Outputs
wire [3:0] Q;
// Instantiate the Unit Under Test (UUT)
counter6 uut (
.Q(Q),
.nCR(nCR),
.EN(EN),
.CP(CP)
);
parameter PERIOD =40;//时钟信号周期设置为40ns
always begin
CP=1'b0;
#(PERIOD/2) CP=1'b1;
#(PERIOD/2);
end
initial begin
// Initialize Inputs
nCR = 0;
EN = 1;
CP = 1;
// Wait 100 ns for global reset to finish
#100;
nCR =1;
// Add stimulus here
end
endmodule
3、 模24计数器
测试代码:
// Inputs
reg nCR;
reg EN;
reg CP;
// Outputs
wire [3:0] CntH;
wire [3:0] CntL;
// Instantiate the Unit Under Test (UUT)
counter24 uut (
.CntH(CntH),
.CntL(CntL),
.nCR(nCR),
.EN(EN),
.CP(CP)
);
parameter PERIOD =40;//时钟信号周期设置为40ns
always begin
CP=1'b0;
#(PERIOD/2) CP=1'b1;
#(PERIOD/2);
end
initial begin
// Initialize Inputs
nCR = 0;
EN = 1;
CP = 1;
// Wait 100 ns for global reset to finish
#100;
nCR=1;
// Add stimulus here
end
endmodule
4、 模60计数器
测试代码:
// Inputs
reg nCLR;
reg Clk;
reg EN;
// Outputs
wire [3:0] CntH;
wire [3:0] CntL;
// Instantiate the Unit Under Test (UUT)
counter60 uut (
.nCLR(nCLR),
.Clk(Clk),
.EN(EN),
.CntH(CntH),
.CntL(CntL)
);
parameter PERIOD =40;//时钟信号周期设置为40ns
always begin
Clk=1'b0;
#(PERIOD/2) Clk=1'b1;
#(PERIOD/2);
end
initial begin
// Initialize Inputs
nCLR = 0;
Clk = 1;
EN = 1;
// Wait 100 ns for global reset to finish
#100;
nCLR=1;
// Add stimulus here
end
endmodule
八、 引脚分配
NET "CLK_50" TNM_NET = CLK_50;
TIMESPEC TS_CLK_50 = PERIOD "CLK_50" 20 ns HIGH 50%;
NET "CLK_50" LOC = B8;
NET "nCR" LOC = P11;
NET "EN" LOC = L3;
NET "Adj_Min" LOC = K3;
NET "Adj_Hour" LOC = B4;
NET "Change" LOC = G3;
NET "Led_Alarm" LOC = N4;
NET "Adj_Clock" LOC = E2;
NET "Clock_EN" LOC = N3;
NET "HEX0[6]" LOC = L14;
NET "HEX0[5]" LOC = H12;
NET "HEX0[4]" LOC = N14;
NET "HEX0[3]" LOC = N11;
NET "HEX0[2]" LOC = P12;
NET "HEX0[1]" LOC = L13;
NET "HEX0[0]" LOC = M12;
NET "HEX[0]" LOC = F12;
NET "HEX[1]" LOC = J12;
NET "HEX[2]" LOC = M13;
NET "HEX[3]" LOC = K14;
NET "Hchange" LOC = F3;
NET "CLK_50" SLEW = FAST;
NET "LD_6" LOC = G1;
九、 设计实现
1. 运行Implement Design选项,进行转换、映射、布局布线操作。
2. 选择Manage Configuration Project选项
l 选择Boundary Scan,在窗口空白处点击鼠标右键,选中Initialize Chain选项.
l 将clock.bit文件导入至xc3s5001中
l 右击xc3s5001,选择program选项,将程序烧录至FPGA实验板中
3. 观察运行情况
1) 测试基本功能:
打开使能开关L3,数码管可以正常显示分和时,拨动时分和秒的切换显示开关G3,数码管可以切换显示秒。拨动K3和B4,可以实现校时功能。拨动清零开关P11,可以实现清零功能。当时间到23:59时,能运行至00:00。
2) 测试扩展功能:
拨动F3,可实现24小时与12小时的切换。当时间为整点时,LED灯G1会根据当前整点时数闪烁对应次数,拨动N3,闹钟使能。拨动E2,进入闹钟设置状态,此时设置闹钟时间。当正常时间跳到闹钟设置时刻时,闹钟对应LED灯N4会常亮,表示闹钟响。当正常时间已经越过闹钟设置时间时,N4会熄灭。闹钟响时关闭闹钟使能端N3时,N4也会熄灭。
十、 实验总结
本次实验是一个中等规模的设计实验,相比步进电机实验,难度明显上升,这就对我们的分析和设计能力有了更高的要求。要实现这个多功能的数字钟,关键要做到思路清晰,先构思好顶层架构,再一一考虑需要调用的模块,最后依次编写各个模块。在调试的时候,先要做到各子模块调试无误,再通过整机联调观察出现的不正常结果,列出可能的错误,再去对应的位置进行改正。
本次实验中,不可避免地出现了若干错误之处,例如在实现24进制和12进制转换时,没有发现代码中的计算方式是以8421BCD码来进行计算的,于是在最后的12进制显示中出现了很多不正常状态,花费了一些时间才找到解决办法。这也提醒我在之后的学习过程中要保持细心的态度,不断学习纠正错误的方法,提高效率。
27 / 27
展开阅读全文