时钟实现的功能:
1.数码管显示时间
2.有计时功能
3.可实现定点报时
多功能时钟共两种工作状态:
1.正常的时钟显示时间
2.计时状态
由于部分原因,本例中调节时间的功能并没有添加,程序大体功能完整,可能出现少许BUG,可以做个参考,望知。
实现资源:
一共有5个输入,包括四个按键及一个时钟输入
3个输出,包括位、段数码管及蜂鸣器
module multfun_clock(clk,key1,key2,key3,key0,beep,dxuan, wxuan,count,divclk,wei_cnt,disp_state );
input clk;
input key1;
input key2;
input key3;
input key0;
output reg [7:0] dxuan;
output reg [5:0] wxuan;
output reg beep;
reg [25:0] count;
reg divclk; //分频产生1次/秒的脉冲用于时钟计数
reg [4:0] cnth,cnthh;
reg [5:0] cntd,cntdd;
reg [5:0] cnts,cntss;
reg [1:0] kstate;
//******************数码管数值定义*********************************//
parameter disp0=8'b1100_0000;
parameter disp1=8'b1111_1001;
parameter disp2=8'b1010_0100;
parameter disp3=8'b1011_0000;
parameter disp4=8'b1001_1001;
parameter disp5=8'b1001_0010;
parameter disp6=8'b1000_0010;
parameter disp7=8'b1111_1000;
parameter disp8=8'b1000_0000;
parameter disp9=8'b1001_0000;
利用脉冲的递送产生的上升沿来控制时钟状态转换
reg low_g;
reg low_g_r;
reg d1;
reg [19:0] cnt;
always @ (posedge clk )
if (cnt == 20'hfffff) low_g <= key3;
else cnt <= cnt + 1'b1;
always @ (posedge clk )
low_g_r <= low_g;
wire led_ctr = low_g_r & (~low_g);
always@(posedge clk)
begin
if(led_ctr==0) //按键3用来切换时钟、计数器与调节时钟状态
begin
if(kstate>2'b10)
kstate<=2'b00;
else
kstate<=kstate+2'b01;
end
else kstate<=kstate;
end
该always语句用于产生时钟所用脉冲,将50Mhz脉冲分频,计数2500_0000次即为1秒钟时间。50Mhz脉冲每个脉冲为20ns,1秒钟需要5000_0000个脉冲,那么产生的分频信号应该计数至2500_0000就翻转一次
always@(posedge clk) //时钟分频
begin
if(count<=26'd2500_0000) count<=count+26'd1;
else
begin
divclk=~divclk;
count<=26'd0;
end
end
无论当前多功能时钟计数状态为什么,时钟模块永远处于工作状态,按秒、分、时依次进位
always@(posedge divclk)
begin
if(cnts<=6'd58)
cnts=cnts+6'd1;
else
begin //计时60秒,秒计数清零,分计数加1
cnts<=6'd0;
if(cntd<=6'd58)
cntd<=cntd+6'd1;
else
begin //计数60分,分计数清零,时计数加1
cntd<=6'd0;
if(cnth<=5'd22)
cnth<=cnth+5'd1;
else
cnth<=5'd0; //计数24时,时计数清零
end
end
end
本例中计时最大限度设为2小时,可清零
always@(posedge divclk)
if(kstate==2'b01) begin//切换到计时
if(key0==1'b0) //开关0清零
begin
cntss<=0;
cntdd<=0;
cnthh<=0;
end
else if(key1==1'b0) begin //复位端无效
if(cntss<=6'd59) cntss=cntss+6'd1;
else begin //计时60秒,秒计数清零,分计数加1
cntss<=6'd0;
if(cntdd<=6'd59) cntdd<=cntdd+6'd1;
else begin //计数60分,分计数清零,时计数加1
cntdd<=0;
if(cnthh<=5'd31) cnthh<=cnthh+5'd1;
else cnthh<=5'd0;
end //计数2小时,时计数清零
end
end
else begin
cntss<=cntss;
cntdd<=cntdd;
cnthh<=cnthh;
end
end
cnt_symin用于选择八段数码管的输入显示
case(cnt_symin)
显示分为时钟显示和计时显示,根据状态不同进行切换
if(kstate==2'b01) begin ……
else begin ……
下面为该模块所有代码:
reg [15:0] wei_cnt;
reg [ 2:0] disp_state;
reg [ 7:0] cnt_symin;
reg [ 7:0] cnt_symout;
always@(posedge clk) //位数码管切换
begin
if(wei_cnt<=16'd5_0000) begin
disp_state<=disp_state;
wei_cnt<=wei_cnt+12'd1; end
else begin
wei_cnt<=16'd0;
if(disp_state==3'd5) disp_state<=3'd0;
else disp_state<=disp_state+3'd1;
end
end
always@(posedge clk) //段数码管切换
begin
case(cnt_symin)
1:dxuan<=disp1;
2:dxuan<=disp2;
3:dxuan<=disp3;
4:dxuan<=disp4;
5:dxuan<=disp5;
6:dxuan<=disp6;
7:dxuan<=disp7;
8:dxuan<=disp8;
9:dxuan<=disp9;
0:dxuan<=disp0;
default:dxuan<=disp0;
endcase
end
always@(posedge clk)
begin
if(kstate==2'b01) begin
case(disp_state)
0:begin wxuan<=6'b011_111;cnt_symin<=cnthh/10; end
1:begin wxuan<=6'b101_111;cnt_symin<=cnthh%10; end
2:begin wxuan<=6'b110_111;cnt_symin<=cntdd/10; end
3:begin wxuan<=6'b111_011;cnt_symin<=cntdd%10; end
4:begin wxuan<=6'b111_101;cnt_symin<=cntss/10; end
5:begin wxuan<=6'b111_110;cnt_symin<=cntss%10; end
default:wxuan<=6'b111_111;
endcase
end
else begin
case(disp_state)
0:begin wxuan<=6'b011_111;cnt_symin<=cnth/10; end
1:begin wxuan<=6'b101_111;cnt_symin<=cnth%10; end
2:begin wxuan<=6'b110_111;cnt_symin<=cntd/10; end
3:begin wxuan<=6'b111_011;cnt_symin<=cntd%10; end
4:begin wxuan<=6'b111_101;cnt_symin<=cnts/10; end
5:begin wxuan<=6'b111_110;cnt_symin<=cnts%10; end
default:wxuan<=6'b111_111;
endcase
end
end
这个模块因为某些原因并没有实现功能,可做参考
reg [15:0] beep_cnt;
always@(posedge clk)
begin
if(cnts==6'd58) begin //59秒时开始提醒
if(beep_cnt==16'd1000) begin //如果计时等于0.01秒,则停止蜂鸣
beep<=0;
beep_cnt<=0; end
else begin
beep<=~beep; //否则一直蜂鸣直到计时结束
beep_cnt<=beep_cnt+16'd1;
end
end
else beep=0;
end
【注】:个人学习记录,如有错误,望不吝赐教
邮箱:[email protected]