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)相关推荐

  1. EDA实验(Quartus Ⅱ+fpga) (五)---多功能数字钟设计

    前言: 本文主要介绍了EDA原理与应用这门课程的相关实验及代码.使用的软件是Quartus Ⅱ,该实验使用fpga芯片为cycloneⅤ 5CSEMA5F31C6. (一)实验目的 (1)了解数字钟的 ...

  2. 【verilog】多功能数字钟的设计

    实验目的 掌握数字钟的工作原理. 掌握计数器级联构成更大模值计数器的方法.  能用verilog描述简单的时序逻辑电路. 实验原理 多功能数字钟应该具有的基本功能有:显示时-分-秒.整点报时.小时和分 ...

  3. 数字系统设计(FPGA)课程设计: 多功能数字钟

    一.目的: 实现多功能数字钟,具备下列功能: 1.数字钟:能计时,实现小时.分钟.秒的显示: 2.数字跑表:精度至0.01秒 比如显示12.97秒: 3.闹钟: 可以设定闹钟,用试验箱上的蜂鸣器作为闹 ...

  4. 基于Quartus II软件的FPGA综合实验——多功能数字钟

    有很多自制元器件,内部电路附在文章中 文章目录 前言 一.设计要求 二.设计原理 三.设计过程 1.数码管扫描模块 2.计时模块 3.闹钟模块 4.闹钟响铃模块 5.数码管显示模块 6.整点报时功能 ...

  5. VHDL编写多功能数字钟,spartan3 FPGA开发板硬件实现-学习笔记

    VHDL编写多功能数字钟,spartan3 FPGA开发板硬件实现-学习笔记 多功能数字钟硬件测试视频: https://www.bilibili.com/video/av62501230 1.数字钟 ...

  6. 多功能数字钟c语言单片机PPT,基于STC89C52单片机的多功能数字钟的设计

    2010年第35期.本刊重稿.科技信囊 基于STC89C52单片机的多功能数字钟的设计 张开碧王浩曾勇斌 (重庆邮电大学自动化学院中国重庆400065) I摘要]本文主要介绍了数字钟的功能以及相应的硬 ...

  7. eda多功能数字钟课程设计_《多功能数字钟》EDA实验报告

    <EDA课程设计> 1.摘要 实验报告 多功能数字钟 姓 名: 学 号: 联系方式: 成 绩: 在当代,随着人类社会进入到高度发达的信息化社会.信息技术的发展起着越来越大的作用,它几乎涉及 ...

  8. 多功能数字钟c语言单片机PPT,基于51单片机多功能数字钟的设计

    多功能数字钟设计 摘要 本设计以AT89C52单片机.DS1302时钟芯片和DS18B20温度传感器为核心,采用LCD1602液晶显示,辅以必要的电路,共同构成一个具有多功能的数字钟.该系统能够准确的 ...

  9. 多功能数字钟软件C语言,多功能数字时钟

    内容介绍 原文档由会员 你的样子 发布 多功能数字时钟 ①页数 19 ②字数 6932 ③摘要 摘 要: 随着电子技术的发展,在诸如计时.控制等领域,设计出应用具有时间设置(小时和分钟),闹钟时间设置 ...

最新文章

  1. 关于Exception类
  2. Linux kernel crypto的介绍
  3. VirtualBox通过命令方式批量创建并配置虚拟机
  4. window server 安装与卸载
  5. Java Thread 总结
  6. Introducing the ClearGLASS App on ClearOS
  7. 对于Neural ODE的小研究
  8. 知识图谱嵌入的应用场景
  9. 今天的阿里云,不应该只是这张图
  10. linux 脚本加密工具下载,linux truecrypt 加密工具
  11. C# Win10缩放导致Winform字体模糊的解决方法
  12. iphone如何显示实时网速和内存空间
  13. Goolgle knowledge graph API使用
  14. [JS]HTML中把图片划分成多个作用区域的区域标记
  15. 1000句英语经典口语(9)
  16. 回忆篇,那些抹不去的童年记忆
  17. Docker学习四--Harbor私有仓库搭建
  18. 【转】看板和Scrum相得益彰
  19. mysql 2038年问题_关于PHP转换超过2038年日期出错的问题解决
  20. C++ 对象模型 第二章 构造函数语意学

热门文章

  1. 如何安装keepalived、keepalived配置文件讲解
  2. 微信小程序css单位,微信小程序 rpx 尺寸单位详细介绍
  3. 低延迟流媒体协议SRT、WebRTC、LL-HLS、UDP、TCP、RTMP详解
  4. 银联 php hex2bin,php 实现银联商务H5支付
  5. 办公娱乐一站式服务 威联通TS-251 NAS评测
  6. 2016Android某公司面试题
  7. 计算机工程与应用最新录用,2022.8.30
  8. 计算机编程怎样打符号,在CAD中如何输入一些特殊符号?比如乘除、符号.
  9. Windows Server 2008 R2文件系统管理(NTFS)视频课程-深博-专题视频课程
  10. 广东海洋大学matlab试卷,专注广东海洋大学