FPGA实现多功能数字钟(Verilog)
FPGA实现多功能数字钟(Verilog)
- 介绍
- 整体框架
- 模块介绍
- 按键模块
- 主体计数模块
- 调整时间(日期)和设置闹钟
- 秒表模块
- 闹钟音乐模块
- 数码管显示
介绍
本文设计的数字钟的功能包括:正常时钟、日期显示、调整时间(日期)、整点报时、闹钟(包括闹钟音乐)、秒表、数码管显示。使用的HDL语言为Verilog,参考了一些别人的设计,对一些模块进行了仿真,并对整个系统进行了仿真,功能基本正确。接下来我将详细介绍各个模块的功能以及设计。下图为设计的所有模块:
整体框架
顶层rtl视图(不知道看不看得清):
顶层模块的输入输出有:
input clk, //50MHz
input rst_n,
input [6:0] key_in, //按键输入
output [5:0] sel, //数码管位选
output [6:0] seg, //数码管段选
output time_led, //整点报时(闪烁)
output beep //闹钟音乐
模块介绍
按键模块
消抖模块
其中,key_filter.v文件为按键消抖模块,由于每个按键都需要对其进行消抖,所以将这个模块独立出来,方便复用。
module key_filter(input clk,input rst_n,input key_in,output reg key_state, //预留output key_flag
);parameter CNT_MAX = 5; //20ms计数值 1_000_000reg [19:0] key_H, key_L; //计数变量//------------------按键为低 计数-------------------------------------------------------------------------------------------------always@(posedge clk, negedge rst_n) beginif(!rst_n)key_H <= 20'd0;else beginif(key_in)key_H <= key_H + 1;elsekey_H <= 20'd0; end end //-------------------按键为高 计数----------------------------------------------------------------------------------------------------- always@(posedge clk, negedge rst_n) beginif(!rst_n)key_L <= 20'd0;else beginif(!key_in)key_L <= key_L + 1;elsekey_L <= 20'd0; end end //-------------------key_state输出判断------------------------------------------------------------------------------------------------------- always@(posedge clk, negedge rst_n) beginif(!rst_n)key_state <= 1'b1;else beginif(key_H > CNT_MAX)key_state <= 1'b1;else if(key_L > CNT_MAX)key_state <= 1'b0; end end
//---------------------按键按下 下降沿判断------------------------------------------------------------- reg state1, state2;always@(posedge clk, negedge rst_n) beginif(!rst_n) beginstate1 <= 1'b0;state2 <= 1'b0;end else beginstate1 <= key_state;state2 <= state1;end end assign key_flag = (!state1) & state2; //按键按下,flag为高endmodule
为了简单,这里消抖直接使用的是两个计数器来判断按键持续的时间,为了仿真方便,这里直接设置计数值为5。也可以使用状态机来判断按键初始状态、按下滤波、按下、释放滤波、回到初始态,当然其核心还是计数器。
模式转化
module model_change(input clk,input rst_n,input key_in,output [1:0] model
);//------------------------------------------- 模式:00:时钟,01:闹钟,10:秒表,11:调时 --------------------------------------------------------------------------reg [1:0] cnt;always@(posedge clk, negedge rst_n) beginif(!rst_n) begincnt <= 2'd0;endelse if(key_in) //按键按下cnt <= cnt + 1'b1; // 00 01 10 11循环else cnt <= cnt;end assign model = cnt; endmodule
按键输出模块
//---- key1 日期,时间切换
//---- key2 模式切换 (时间显示,校准时间,闹钟, 秒表)
//---- key3 调时和设置闹钟时,小时、分钟左右切捯
//---- key4 +1
//---- key5 -1
//---- key6 秒表暂停(开始)切换
//---- key7 秒表清除module key_module(input clk,input rst_n,input [6:0] key_in,output reg date_time_ch, //时间和日期切换标志位,0为时间,1为日期output [1:0] model, //模式:00:时钟,01:闹钟,10:秒表,11:调时output reg [1:0] adjust_shif, //调整时间时,调整位置:00:秒个位,01:分个位,10:时个位output key_up, //调整时间+output key_down, //调整时间-output reg pause, //秒表暂停/开始 0:暂停,1:开始output clear //秒表清除
);//-------------------------------------- 例化key_filter,产生按键对应的高电平脉冲 ----------------------------------------------------------------wire [6:0] key_out;genvar i;generatefor(i = 0; i < 7; i = i + 1) begin: specify7_key_filter //块名key_filter fliter1(.clk (clk),.rst_n (rst_n),.key_in (key_in[i]),.key_state (),.key_flag (key_out[i]));end endgenerate assign key_up = key_out[3];assign key_down = key_out[4];assign clear = key_out[6];
//-------------------------------------------- 例化 model_change ------------------------------------------------------------------------------------------- model_change model1(.clk (clk),.rst_n (rst_n),.key_in (key_out[1]),.model (model));
//-------------------------------------------- adjust_shif 调整时间位置 ------------------------------------------------------------------------------------always@(posedge clk, negedge rst_n) beginif(!rst_n) beginadjust_shif <= 2'b00;end else beginif(key_out[2]) beginadjust_shif <= adjust_shif + 1'b1; //非阻塞赋值,需要到过程块结束才赋新值,所以 adjust_shif == 2'b10,而不是11if(adjust_shif == 2'b10) adjust_shif <= 2'b00;end if(key_out[1]) // model模式改变时,回到初始位adjust_shif <= 2'b00;end end
//--------------------------------------------- date_time_ch 时间和日期切换标志位--------------------------------------------------------------------------------------------------------------- always@(posedge clk, negedge rst_n) beginif(!rst_n)date_time_ch <= 1'b0;else if(key_out[0]) begindate_time_ch <= ~date_time_ch; //按键摁下,标志位翻转 end elsedate_time_ch <= date_time_ch;end
//------------------------------------------------ pause 秒表暂停/开始 --------------------------------------------------------------------------------------------------always@(posedge clk, negedge rst_n) beginif(!rst_n)pause <= 1'b0;else if(key_out[5])pause <= ~pause;else if(model != 2'b10) //退出秒表模式暂停pause <= 1'b0; else pause <= pause; endendmodule
在整个设计中,需要特别注意的是阻塞赋值语句的特点,阻塞赋值语句是在过程块结束的时候进行赋值更新的,所以对被赋值语句判断时需要注意一下,而且在一个过程块中对同一变量重复阻塞赋值,只更新最后一次的赋值。
主体计数模块
时钟分频模块
为了简单起见,这里分频模块没有考虑奇偶分频的情况,因为分频系数会很大,可以忽略这部分的影响。
module clk_div( //clk_out的周期,可在上层模块例化时修改 input clk_in,input rst_n,output clk_out
);//-------------------------------------------------------------------------------- parameter CNT_MAX = 25_000_000; //计数值/2reg clk_reg;reg [27:0] cnt;//------------------------------------------------------------------------------------- assign clk_out = clk_reg;always@(posedge clk_in, negedge rst_n) beginif(!rst_n) beginclk_reg <= 1'b0;cnt <= 28'd0;end else beginif(cnt == CNT_MAX - 1) begincnt <= 28'd0;clk_reg <= ~clk_reg; end else begin cnt <= cnt + 1'b1;clk_reg <= clk_reg;endend endendmodule
计数模块
module time_counter(input clk,//input clk_1s,input rst_n,input [1:0] model,input date_time_ch, //时间和日期切换标志位D?o时间,1为日朠 input [23:0] adjust_time_num, //时间调整倠 input [23:0] adjust_date_num, //日期调整倠 output [23:0] time_num,output [23:0] data_num,output time_led
);//---------------------------- 秒表显示变量 -------------------------------- reg [3:0] toc_10ms;reg [3:0] toc_100ms;reg [3:0] toc_1s;reg [3:0] toc_10s;reg [3:0] toc_1m;reg [3:0] toc_10m;
//------------------------- 1s时钟上升沿检浭----------------------------------------wire clk_1s;clk_div #(.CNT_MAX(10)) clk_div_1s(.clk_in (clk),.rst_n (rst_n),.clk_out (clk_1s)); reg clk_reg1, clk_reg2;wire pos_1s;always@(posedge clk, negedge rst_n) beginif(!rst_n) beginclk_reg1 <= 1'b0;clk_reg2 <= 1'b0;end else beginclk_reg1 <= clk_1s;clk_reg2 <= clk_reg1;end end assign pos_1s = clk_reg1 & (!clk_reg2);
//---------------------正常时间计数(时、分、秒M-----------------------------reg [3:0] sec0;reg [3:0] sec1;reg [3:0] min0;reg [3:0] min1;reg [3:0] hour0;reg [3:0] hour1; always@(posedge clk, negedge rst_n) beginif(!rst_n) beginsec0 <= 4'd9; //默认时间23:56:49sec1 <= 4'd4;min0 <= 4'd8;min1 <= 4'd5;hour0 <= 4'd3;hour1 <= 4'd2; endelse beginif(pos_1s) beginsec0 <= sec0 + 1'b1;if(sec0 == 4'd9) beginsec0 <= 4'd0;sec1 <= sec1 + 1'b1;if(sec1 == 4'd5) beginsec1 <= 4'd0;min0 <= min0 + 1'b1;if(min0 == 4'd9) beginmin0 <= 4'd0;min1 <= min1 + 1'b1; if(min1 == 4'd5) beginmin1 <= 4'd0;hour0 <= hour0 + 1'b1; if(hour0 == 4'd9) beginhour0 <= 4'd0;hour1 <= hour1 + 1'b1; end end end end end end if(hour1 ==4'd2 && hour0 == 4'd4) begin hour1 <= 4'd0;hour0 <= 4'd0; end if((model == 2'b11) && !date_time_ch) begin //时间校准模式{hour1, hour0, min1, min0, sec1, sec0} <= adjust_time_num[23:0];end end end
//--------------------- 日期时间(年、月、日M------------------------------ reg [3:0] day0; reg [3:0] day1; reg [3:0] month0; //年、月、日计数变量reg [3:0] month1; reg [3:0] year0;reg [3:0] year1;localparam mon1 = 8'h31, //每个月天敠 mon2 = 8'h28,mon3 = 8'h31,mon4 = 8'h30,mon5 = 8'h31,mon6 = 8'h30,mon7 = 8'h31,mon8 = 8'h31, mon9 = 8'h30,mon10 = 8'h31,mon11 = 8'h30,mon12 = 8'h31;always@(posedge clk, negedge rst_n) beginif(!rst_n) beginday0 <= 4'd8; //为了方便仿真看到变化,默认日期为2020-12-28day1 <= 4'd2;month0 <= 4'd2;month1 <= 4'd1;year0 <= 4'd0;year1 <= 4'd2;end else beginif(hour1 ==4'd2 && hour0 == 4'd4) begin //24小时删 day0 <= day0 + 1;if(day0 == 4'd9) beginday0 <= 4'd0;day1 <= day1 + 1'b1; end case({month1, month0}) //根据月份,来改变8'h01: if({day1, day0} == mon1) beginday0 <= 4'd1;day1 <= 4'd0;month0 <= month0 + 1'b1;end 8'h02:if({day1, day0} == mon2) beginday0 <= 4'd1;day1 <= 4'd0;month0 <= month0 + 1'b1;end 8'h03:if({day1, day0} == mon3) beginday0 <= 4'd1;day1 <= 4'd0;month0 <= month0 + 1'b1;end 8'h04:if({day1, day0} == mon4) beginday0 <= 4'd1;day1 <= 4'd0;month0 <= month0 + 1'b1;end 8'h05:if({day1, day0} == mon5) beginday0 <= 4'd1;day1 <= 4'd0;month0 <= month0 + 1'b1;end 8'h06:if({day1, day0} == mon6) beginday0 <= 4'd1;day1 <= 4'd0;month0 <= month0 + 1'b1;end 8'h07:if({day1, day0} == mon7) beginday0 <= 4'd1;day1 <= 4'd0;month0 <= month0 + 1'b1;end 8'h08:if({day1, day0} == mon8) beginday0 <= 4'd1;day1 <= 4'd0;month0 <= month0 + 1'b1;end 8'h09:if({day1, day0} == mon9) beginday0 <= 4'd1;day1 <= 4'd0;month0 <= 4'd0;month1 <= 4'd1; //月份高位也变end 8'h10:if({day1, day0} == mon10) beginday0 <= 4'd1;day1 <= 4'd0;month0 <= month0 + 1'b1;end 8'h11:if({day1, day0} == mon11) beginday0 <= 4'd1;day1 <= 4'd0;month0 <= month0 + 1'b1;end 8'h12: if({day1, day0} == mon12) begin //12月最后一夠 day0 <= 4'd1;day1 <= 4'd0;month0 <= 4'd1;month1 <= 4'd0;year0 <= year0 + 1'b1; //下一幠 if(year0 == 4'd9) beginyear0 <= 4'd0;year1 <= year1 + 1'b1;end end default: beginday0 <= 4'hx; //为了方便仿真,设为xday1 <= 4'hx;month0 <= 4'hx;month1 <= 4'hx;year0 <= 4'hx;year1 <= 4'hx; end endcaseend if((model == 2'b11) && date_time_ch) //时间校准模式,并且处于日期显示下{year1, year0, month1, month0, day1, day0} <= adjust_date_num[23:0];end end
//------------------------------------------------------------ 整点报时 --------------------------------------------------------------------- reg len_flag;always@(posedge clk, negedge rst_n) beginif(!rst_n)len_flag <= 1'b0;else if(time_num[15:0] == 16'b0) //分钟和秒都归零len_flag <= 1'b1;else if(min0 == 4'd1) //1分钟len_flag <= 1'b0;elselen_flag <= len_flag;endassign time_led = len_flag ? clk_1s : 1'b0; //1s闪烁,持续1分钟
//---------------------------------------------------------- 输出赋值----------------------------------------------------------------------- assign time_num = {hour1, hour0, min1, min0, sec1, sec0};assign data_num = {year1, year0, month1, month0, day1, day0}; endmodule
这里没有采用其它时钟(例如:posedge clk_1s),也是为了简单起见,避免信号在不同的时钟下传递的麻烦。
并且没有考虑闰年的情况,可以考虑加上个判断闰年的逻辑,改变二月的天数。
代码中 if(hour1 ==4’d2 && hour0 == 4’d4) begin
hour1 <= 4’d0;
hour0 <= 4’d0;
end
会产生一个时钟周期的噪声,即不希望出现的24:00:00,这是故意放出来的,后面进行日期的计数就用到了这一个时钟周期的24:00:00。若是希望完美,则可以将这个语句放在分钟min的if语句的里面,就不会出现这个,但是后面的计数日期和整点报时判断需要更改策略,将1Hz的信号变成50MHz等。
调整时间(日期)和设置闹钟
module adjust_module(input clk,input rst_n, input [1:0] model, //模式P:时钟,01:闹钟,10:秒表,11:调无 input date_time_ch,input [1:0] adjust_shif, //调整时间时,调整位置P:秒个位Q:分个位P:时个位input key_up, //调整时间+input key_down, //调整时间- input [23:0] time_num,input [23:0] data_num,output [23:0] adjust_time_num,output [23:0] adjust_date_num,output [15:0] adjust_clock_num
);
//---------------------------- 闹钟变量 ------------------------------------------------------------------------------------------- reg [3:0] clock_min0;reg [3:0] clock_min1;reg [3:0] clock_hour0;reg [3:0] clock_hour1;
//----------------------------- 闹钟设置 ------------------------------------------------------------------------------------------------always@(posedge clk, negedge rst_n) beginif(!rst_n) beginclock_min0 <= 4'd0;clock_min1 <= 4'd0; clock_hour0 <= 4'd0;clock_hour1 <= 4'd0;endelse if(model == 2'b01) begin //闹钟模式,只有小时、分钟 if(adjust_shif == 2'b00) begin //调分钟个位 if(key_up) beginclock_min0 <= clock_min0 + 1'b1;if(clock_min0 == 4'd9)clock_min0 <= 4'd0;end else if(key_down) beginclock_min0 <= clock_min0 - 1'b1;if(clock_min0 == 4'd0)clock_min0 <= 4'd9;end end else if(adjust_shif == 2'b01) begin //调分钟十位 if(key_up) beginclock_min1 <= clock_min1 + 1'b1;if(clock_min1 == 4'd5)clock_min1 <= 4'd0;end else if(key_down) beginclock_min1 <= clock_min1 - 1'b1;if(clock_min1 == 4'd0)clock_min1 <= 4'd5;end end else begin //小时个位if(key_up) beginclock_hour0 <= clock_hour0 + 1'b1;if(clock_hour0 == 4'd9) begin clock_hour0 <= 4'd0;clock_hour1 <= clock_hour1 + 1'b1;endif(clock_hour1 ==4'd2 && clock_hour0 == 4'd3) begin clock_hour1 <= 4'd0;clock_hour0 <= 4'd0; endend else if(key_down) beginclock_hour0 <= clock_hour0 - 1'b1;if(clock_hour0 == 4'd0) beginclock_hour0 <= 4'd9;clock_hour1 <= clock_hour1 - 1'b1;end if(clock_hour1 ==4'd0 && clock_hour0 == 4'd0) begin clock_hour1 <= 4'd2;clock_hour0 <= 4'd3; endendendendend //-------------------------- 校准时间变量 ------------------------------------------------------------------------------------------------ reg [3:0] adj_sec0;reg [3:0] adj_sec1;reg [3:0] adj_min0;reg [3:0] adj_min1;reg [3:0] adj_hour0;reg [3:0] adj_hour1;/*------------------------*/ reg [3:0] adj_day0;reg [3:0] adj_day1;reg [3:0] adj_month0;reg [3:0] adj_month1;reg [3:0] adj_year0;reg [3:0] adj_year1;//----------------------------- 校准时间和日期--------------------------------------------------------------------------------------------------------always@(posedge clk, negedge rst_n) beginif(!rst_n) beginadj_sec0 <= 4'd9;adj_sec1 <= 4'd4; adj_min0 <= 4'd6;adj_min1 <= 4'd5; adj_hour0 <= 4'd3;adj_hour1 <= 4'd2; adj_day0 <= 4'd8;adj_day1 <= 4'd0; adj_month0 <= 4'd6;adj_month1 <= 4'd0; adj_year0 <= 4'd0;adj_year1 <= 4'd2; end else if((model == 2'b11) && !date_time_ch)begincase(adjust_shif) //调时 位置2'b00: begin //秒个位 if(key_up) beginadj_sec0 <= adj_sec0 + 1'b1;if(adj_sec0 == 4'd9) beginadj_sec0 <= 4'd0;adj_sec1 <= adj_sec1 + 1'b1;if(adj_sec1 == 4'd5) beginadj_sec1 <= 4'd0;end end end else if(key_down) beginadj_sec0 <= adj_sec0 - 1'b1;if(adj_sec0 == 4'd0) beginadj_sec0 <= 4'd9;adj_sec1 <= adj_sec1- 1'b1;if(adj_sec1 == 4'd0) beginadj_sec1 <= 4'd5;end endend end2'b01: begin //分个位 if(key_up) beginadj_min0 <= adj_min0 + 1'b1;if(adj_min0 == 4'd9) beginadj_min0 <= 4'd0;adj_min1 <= adj_min1 + 1'b1;if(adj_min1 == 4'd5) beginadj_min1 <= 4'd0;end end endelse if(key_down) beginadj_min0 <= adj_min0 - 1'b1;if(adj_min0 == 4'd0) beginadj_min0 <= 4'd9;adj_min1 <= adj_min1- 1'b1;if(adj_min1 == 4'd0) beginadj_min1 <= 4'd5;end endendend2'b10: begin //时个位 if(key_up) beginadj_hour0 <= adj_hour0 + 1'b1;if(adj_hour0 == 4'd9) beginadj_hour0 <= 4'd0;adj_hour1 <= adj_hour1 + 1'b1;end if(adj_hour1 ==4'd2 && adj_hour0 == 4'd3) begin adj_hour1 <= 4'd0;adj_hour0 <= 4'd0; endendif(key_down) beginadj_hour0 <= adj_hour0 - 1'b1;if(adj_hour0 == 4'd0) beginadj_hour0 <= 4'd9;adj_hour1 <= adj_hour1 - 1;end if(adj_hour1 ==4'd0 && adj_hour0 == 4'd0) begin adj_hour1 <= 4'd2;adj_hour0 <= 4'd3; endendenddefault: beginadj_sec0 <= 4'dx;adj_sec1 <= 4'dx; adj_min0 <= 4'dx;adj_min1 <= 4'dx; adj_hour0 <= 4'dx;adj_hour1 <= 4'dx; end endcase end else if((model == 2'b11) && date_time_ch) begincase(adjust_shif) //调时 位置2'b00: begin//日个位 if(key_up) beginadj_day0 <= adj_day0 + 1'b1;if(adj_day0 == 4'd9) beginadj_day0 <= 4'd0;adj_day1 <= adj_day1 + 1; end if(adj_day1 == 4'd3 && adj_day0 == 4'd1) begin //为了简化逻辑,调整天数,在0-31号之间adj_day1 <= 4'd0; adj_day0 <= 4'd1;end end if(key_down) beginadj_day0 <= adj_day0 - 1'b1;if(adj_day0 == 4'd0) beginadj_day0 <= 4'd9; adj_day1 <= adj_day1 - 1;end if(adj_day1 == 4'd0 && adj_day0 == 4'd1) begin //1号再减一,31号adj_day1 <= 4'd3;adj_day0 <= 4'd1;endendend2'b01: begin //月个位 if(key_up) beginadj_month0 <= adj_month0 + 1'b1;if(adj_month0 == 4'd9) beginadj_month0 <= 4'd0;adj_month1 <= adj_month1 + 1; end if(adj_month1 == 4'd1 && adj_month0 == 4'd2) begin //十二月加一,到十二月adj_month1 <= 4'd0;adj_month0 <= 4'd1;end endif(key_down) beginadj_month0 <= adj_month0 - 1'b1;if(adj_month0 == 4'd0) beginadj_month0 <= 4'd9;adj_month1 <= adj_month1 - 1;end if(adj_month1 == 4'd0 && adj_month0 == 4'd1) begin //一月再减一,到十二月adj_month1 <= 4'd1;adj_month0 <= 4'd2;endendend2'b10: begin //年个位 if(key_up) beginadj_year0 <= adj_year0 + 1'b1;if(adj_year0 == 4'd9) beginadj_year0 <= 4'd0;adj_year1 <= adj_year1 + 1; if(adj_year1 == 4'd9) beginadj_year1 <= 4'd0;end end endif(key_down) beginadj_year0 <= adj_year0 - 1'b1;if(adj_year0 == 4'd0) beginadj_year0 <= 4'd9;if(adj_year1 > 4'd0)adj_year1 <= adj_year1 - 1;end endenddefault: beginadj_day0 <= 4'hx; //方便仿真时能看到错误adj_day1 <= 4'hx; adj_month0 <= 4'dx;adj_month1 <= 4'dx; adj_year0 <= 4'dx;adj_year1 <= 4'dx; end endcase end else begin{adj_hour1, adj_hour0, adj_min1, adj_min0, adj_sec1, adj_sec0} <= time_num; //将当前时间赋给调节变量{adj_year1, adj_year0, adj_month1, adj_month0, adj_day1, adj_day0} <= data_num; //即:在当前时间的基础上调节endend //------------------------------------------- 输出 ---------------------------------------------------------------------------------assign adjust_time_num = {adj_hour1, adj_hour0, adj_min1, adj_min0, adj_sec1, adj_sec0};assign adjust_date_num = {adj_year1, adj_year0, adj_month1, adj_month0, adj_day1, adj_day0};assign adjust_clock_num = {clock_hour1, clock_hour0, clock_min1, clock_min0};endmodule
这个模块可以直接设计在计时模块中,这样会简单很多,之所以独立出来,是因为混在一起会出现调时的时候时间也在走,不能随心所欲的调秒钟。
秒表模块
module stop_watch(input clk,input rst_n,input [1:0] model,input pause, //秒表暂停/开始 0:暂停,1:开始input clear, //秒表清除output [23:0] stop_watch_num //输出显示
);//-------------------------------------- 10ms上升沿 -------------------------------------------------------------------------- wire clk_10ms;reg clk_10ms_reg1, clk_10ms_reg2;wire pos_10ms;clk_div #(.CNT_MAX(20)) clk_div_10ms( //为了缩短仿真时间,参数设为1,即为一个时钟周期.clk_in (clk),.rst_n (rst_n),.clk_out (clk_10ms)); always@(posedge clk, negedge rst_n) beginif(!rst_n) beginclk_10ms_reg1 <= 1'b0;clk_10ms_reg2 <= 1'b0;end else beginclk_10ms_reg1 <= clk_10ms;clk_10ms_reg2 <= clk_10ms_reg1;end end assign pos_10ms = clk_10ms_reg1 & (~clk_10ms_reg2);
//-------------------------------------- 10ms_计数 -------------------------------------------------------------------------------- reg [3:0] cnt_ms0;reg [3:0] cnt_ms1; reg [3:0] cnt_s0;reg [3:0] cnt_s1;reg [3:0] cnt_m0;reg [3:0] cnt_m1;always@(posedge clk, negedge rst_n) beginif(!rst_n) begin //异步复位cnt_ms0 <= 4'd0;cnt_ms1 <= 4'd0; cnt_s0 <= 4'd0;cnt_s1 <= 4'd0;cnt_m0 <= 4'd0;cnt_m1 <= 4'd0;end else if(clear) begin //同步清零cnt_ms0 <= 4'd0;cnt_ms1 <= 4'd0; cnt_s0 <= 4'd0;cnt_s1 <= 4'd0;cnt_m0 <= 4'd0;cnt_m1 <= 4'd0; end else if(pos_10ms) begin if(model == 2'b10) begin //秒表模式if(pause) begin //开始cnt_ms0 <= cnt_ms0 + 1'b1;if(cnt_ms0 == 4'd9) begincnt_ms0 <= 4'd0;cnt_ms1 <= cnt_ms1 + 1'b1;if(cnt_ms1 == 4'd9) begin //99, 1s时间到cnt_ms1 <= 4'd0;cnt_s0 <= cnt_s0 + 1'b1;if(cnt_s0 == 4'd9) begincnt_s0 <= 4'd0;cnt_s1 <= cnt_s1 + 1'b1;if(cnt_s1 == 4'd5) begin //59scnt_s1 <= 4'd0;cnt_m0 <= cnt_m0 + 1'b1;if(cnt_m0 == 4'd9) begincnt_m0 <= 4'd0;cnt_m1 <= cnt_m1 + 1'b1; end end end end end end end end else begincnt_ms0 <= cnt_ms0;cnt_ms1 <= cnt_ms1; cnt_s0 <= cnt_s0;cnt_s1 <= cnt_s1;cnt_m0 <= cnt_m0;cnt_m1 <= cnt_m1; end end
//------------------------------------------------------ 输出 --------------------------------------------------------------------assign stop_watch_num = {cnt_m1, cnt_m0, cnt_s1, cnt_s0, cnt_ms1, cnt_ms0};endmodule
以10ms为最小计数,则只输出分钟、秒、ms。按键控制启停和清零。这里使用 posedge clk_10ms会清爽很多,但是前面都同步了,这里就不改了。
闹钟音乐模块
module alarm_music(input clk,input rst_n,input [15:0] adjust_clock_num, //闹钟调整值,只有小时和分钟input [23:0] time_num,output beep
);
//------------------------------------- 闹钟使能逻辑 ---------------------------------------------------------------------------------------reg flag_en;reg [7:0] state;always@(posedge clk, negedge rst_n) beginif(!rst_n)flag_en <= 1'b0;else if({adjust_clock_num, 8'd0} == time_num) //闹钟时间到flag_en <= 1'b1;else if(state == 8'd63) //一首音乐结束flag_en <= 1'b0;elseflag_en <= flag_en;end //------------------------------------ 音乐 ---------------------------------------------------------------------------------reg beep_r; reg [16:0] count, count_end;reg [23:0] count1;localparam L_3 = 17'd75850,L_5 = 17'd63776,L_6 = 17'd56818,L_7 = 17'd50618,M_1 = 17'd47774,M_2 = 17'd42568,M_3 = 17'd37919,M_5 = 17'd31888,M_6 = 17'd28409,H_1 = 17'd23889;localparam TIME = 12000000; //每个音的长短(250ms) assign beep = beep_r;always@(posedge clk, negedge rst_n) beginif(!rst_n) begincount <= 17'h0;beep_r <= 1'b1;end else if(flag_en) begincount <= count + 1'b1;if(count == count_end) begincount <= 17'h0;beep_r <= !beep_r;end endelse begincount <= 17'h0;beep_r <= 1'b1; end end always@(posedge clk, negedge rst_n) beginif(!rst_n) begincount1 <= 24'd0;state <= 8'd0;endelse if(flag_en) beginif(count1 < TIME) count1 <= count1 + 1'b1;else begincount1 <= 24'd0;if(state == 8'd63)state <= 8'd0;elsestate <= state + 1'b1; end end else begincount1 <= 24'd0;state <= 8'd0;end end always@(state) begincase(state)8'd0, 8'd1, 8'd2, 8'd3: count_end = L_3;8'd4, 8'd5, 8'd6: count_end = L_5;8'd7: count_end = L_6;8'd8, 8'd9, 8'd10: count_end = M_1;8'd11: count_end = M_2;8'd12: count_end = L_6;8'd13: count_end = M_1;8'd14, 8'd15: count_end = L_5;8'd16, 8'd17, 8'd18: count_end = M_5;8'd19: count_end = H_1;8'd20: count_end = M_6;8'd21: count_end = M_5;8'd22: count_end = M_3;8'd23: count_end = M_5;8'd24, 8'd25, 8'd26, 8'd27, 8'd28, 8'd29, 8'd30, 8'd31: count_end = M_2;8'd32, 8'd33, 8'd34: count_end = M_2;8'd35: count_end = M_3;8'd36, 8'd37: count_end = L_7;8'd38, 8'd39: count_end = L_6;8'd40, 8'd41, 8'd42: count_end = L_5;8'd43: count_end = L_6;8'd44, 8'd45: count_end = M_1;8'd46, 8'd47: count_end = M_2;8'd48, 8'd49: count_end = L_3;8'd50, 8'd51: count_end = M_1;8'd52: count_end = L_6;8'd53: count_end = L_5;8'd54: count_end = L_5;8'd55: count_end = M_1;8'd56, 8'd57, 8'd58, 8'd59, 8'd60, 8'd61, 8'd62, 8'd63: count_end = L_5; default: count_end = 17'hxxxxx; endcase end
endmodule
音乐的内容是梁祝,基本原理是:不同频率的pwm能让交流蜂鸣器发出不同调的音,具体原理后面会附文件。
数码管显示
采用六个七段数码管,分别显示小时、分钟、秒(或者秒表模式的分钟、秒、毫秒)。
//----------------------------数码管显示-------------------------------------------------------------
module led_seg7_display( input clk,input rst_n,input [1:0] model,input date_time_ch, // 时钟/日期 input [23:0] time_num, // 时钟数据input [23:0] data_num, // 日期数据input [23:0] adjust_time_num, // 调整时钟数据input [23:0] adjust_date_num, // 调整日期数据 input [15:0] adjust_clock_num,// 调整闹钟数据input [23:0] stop_watch_num, // 秒表output [5:0] sel, // 数码管位选(选择当前要显示的数码管)output reg [6:0] seg // 数码管段选(当前要显示的内容)
);//--------------------------------------------- 显示数据选择 ------------------------------------------------------------------------------- reg [23:0] show_num;always@(posedge clk, negedge rst_n) beginif(!rst_n)show_num <= 24'h0;else beginif(model == 2'b00) begin //常规显示if(!date_time_ch) //显示时间show_num <= time_num;else //日期显示show_num <= data_num;end else if(model == 2'b01) //闹钟显示show_num <= {adjust_clock_num, 8'b00000000}; //闹钟只有小时和分钟,这里默认秒位else if(model == 2'b10) //秒表显示show_num <= stop_watch_num;else begin //调整时间if(!date_time_ch) //调整时间显示show_num <= adjust_time_num;else //调整日期显示show_num <= adjust_date_num;endend end
//--------------------------------------------- 数码管显示逻辑 ---------------------------------------------------------------------------------- wire clk_1ms; //数码管扫描周朱msclk_div #(.CNT_MAX(1)) clk_div_1ms( //为了缩短仿真时间,参数设丯¼Œ即为一个时钟周期.clk_in (clk),.rst_n (rst_n),.clk_out (clk_1ms));reg [5:0]sel_r; //位选缓存reg [3:0]data_tmp; //数据缓存always@(posedge clk_1ms or negedge rst_n)if(!rst_n)sel_r <= 6'b0000_01;else if(sel_r == 6'b1000_00)sel_r <= 6'b0000_01;elsesel_r <= sel_r << 1;always@(*)case(sel_r)8'b0000_01:data_tmp = show_num[3:0];8'b0000_10:data_tmp = show_num[7:4];8'b0001_00:data_tmp = show_num[11:8];8'b0010_00:data_tmp = show_num[15:12];8'b0100_00:data_tmp = show_num[19:16];8'b1000_00:data_tmp = show_num[23:20];default: data_tmp = 4'b0000;endcasealways@(*)case(data_tmp)4'h0:seg = 7'b1000000;4'h1:seg = 7'b1111001;4'h2:seg = 7'b0100100;4'h3:seg = 7'b0110000;4'h4:seg = 7'b0011001;4'h5:seg = 7'b0010010;4'h6:seg = 7'b0000010;4'h7:seg = 7'b1111000;4'h8:seg = 7'b0000000;4'h9:seg = 7'b0010000;4'ha:seg = 7'b0001000;4'hb:seg = 7'b0000011;4'hc:seg = 7'b1000110;4'hd:seg = 7'b0100001;4'he:seg = 7'b0000110;4'hf:seg = 7'b0001110;default:seg = 7'b1000000;endcaseassign sel = sel_r;endmodule
功能应该都是好的,仿真做了一点,没有很仔细的去仿真,如果功能不对或者缺失,可以看看是不是哪段代码被弄到注释里面了(可能是乱码搞得,quartus的版本太低,用notepad写的,编码格式一直对不上,11版本以下的好像得用ansi不能用utf-8)
***[完整代码]***https://download.csdn.net/download/weixin_39520719/12523952
FPGA实现多功能数字钟(Verilog)相关推荐
- EDA实验(Quartus Ⅱ+fpga) (五)---多功能数字钟设计
前言: 本文主要介绍了EDA原理与应用这门课程的相关实验及代码.使用的软件是Quartus Ⅱ,该实验使用fpga芯片为cycloneⅤ 5CSEMA5F31C6. (一)实验目的 (1)了解数字钟的 ...
- 【verilog】多功能数字钟的设计
实验目的 掌握数字钟的工作原理. 掌握计数器级联构成更大模值计数器的方法. 能用verilog描述简单的时序逻辑电路. 实验原理 多功能数字钟应该具有的基本功能有:显示时-分-秒.整点报时.小时和分 ...
- 数字系统设计(FPGA)课程设计: 多功能数字钟
一.目的: 实现多功能数字钟,具备下列功能: 1.数字钟:能计时,实现小时.分钟.秒的显示: 2.数字跑表:精度至0.01秒 比如显示12.97秒: 3.闹钟: 可以设定闹钟,用试验箱上的蜂鸣器作为闹 ...
- 基于Quartus II软件的FPGA综合实验——多功能数字钟
有很多自制元器件,内部电路附在文章中 文章目录 前言 一.设计要求 二.设计原理 三.设计过程 1.数码管扫描模块 2.计时模块 3.闹钟模块 4.闹钟响铃模块 5.数码管显示模块 6.整点报时功能 ...
- VHDL编写多功能数字钟,spartan3 FPGA开发板硬件实现-学习笔记
VHDL编写多功能数字钟,spartan3 FPGA开发板硬件实现-学习笔记 多功能数字钟硬件测试视频: https://www.bilibili.com/video/av62501230 1.数字钟 ...
- 多功能数字钟c语言单片机PPT,基于STC89C52单片机的多功能数字钟的设计
2010年第35期.本刊重稿.科技信囊 基于STC89C52单片机的多功能数字钟的设计 张开碧王浩曾勇斌 (重庆邮电大学自动化学院中国重庆400065) I摘要]本文主要介绍了数字钟的功能以及相应的硬 ...
- eda多功能数字钟课程设计_《多功能数字钟》EDA实验报告
<EDA课程设计> 1.摘要 实验报告 多功能数字钟 姓 名: 学 号: 联系方式: 成 绩: 在当代,随着人类社会进入到高度发达的信息化社会.信息技术的发展起着越来越大的作用,它几乎涉及 ...
- 多功能数字钟c语言单片机PPT,基于51单片机多功能数字钟的设计
多功能数字钟设计 摘要 本设计以AT89C52单片机.DS1302时钟芯片和DS18B20温度传感器为核心,采用LCD1602液晶显示,辅以必要的电路,共同构成一个具有多功能的数字钟.该系统能够准确的 ...
- 多功能数字钟软件C语言,多功能数字时钟
内容介绍 原文档由会员 你的样子 发布 多功能数字时钟 ①页数 19 ②字数 6932 ③摘要 摘 要: 随着电子技术的发展,在诸如计时.控制等领域,设计出应用具有时间设置(小时和分钟),闹钟时间设置 ...
最新文章
- SpringBoot中使用POI实现Excel导入到数据库(图文教程已实践)
- 数字图像处理与机器视觉——Visual C++与Matlab实现书中代码勘误
- NOI2013矩阵游戏
- verilog设计简易正弦波信号发生器_信号发生器工作原理是什么
- Apache Cassandra 在 Facebook 的应用
- (45)npm全局安装包的位置
- mybatis plus使用雪花算法_11.雪花算法与精度丢失
- 二叉树的先序、中序、后序遍历超详解
- opencv3.4.1 + vs 2017 + cmake 3.11.3 + win10 配置. 终章
- maven文件报错(pom.xml或者jar包缺失)解决方法
- Qt5 for linux离线安装工具下载地址
- 华为鸿蒙系统首发设备,鸿蒙首发设备包装曝光:安卓已成过去式,鸿蒙正式走上舞台...
- 数据源Display方法
- mysql with rollup_MySQL-with rollup函数运用 _20160930
- 基于STM8的TM1640驱动程序(附八段数码管配置工具)
- Android中TextToSpeech的使用
- XML文档类型定义---XML Schema结构
- 第15课:如何用RPA循环填写表单?(练兵场二)
- 十六进制转八进制(C语言版)
- STM32——快速识别芯片引脚数