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. SpringBoot中使用POI实现Excel导入到数据库(图文教程已实践)
  2. 数字图像处理与机器视觉——Visual C++与Matlab实现书中代码勘误
  3. NOI2013矩阵游戏
  4. verilog设计简易正弦波信号发生器_信号发生器工作原理是什么
  5. Apache Cassandra 在 Facebook 的应用
  6. (45)npm全局安装包的位置
  7. mybatis plus使用雪花算法_11.雪花算法与精度丢失
  8. 二叉树的先序、中序、后序遍历超详解
  9. opencv3.4.1 + vs 2017 + cmake 3.11.3 + win10 配置. 终章
  10. maven文件报错(pom.xml或者jar包缺失)解决方法
  11. Qt5 for linux离线安装工具下载地址
  12. 华为鸿蒙系统首发设备,鸿蒙首发设备包装曝光:安卓已成过去式,鸿蒙正式走上舞台...
  13. 数据源Display方法
  14. mysql with rollup_MySQL-with rollup函数运用 _20160930
  15. 基于STM8的TM1640驱动程序(附八段数码管配置工具)
  16. Android中TextToSpeech的使用
  17. XML文档类型定义---XML Schema结构
  18. 第15课:如何用RPA循环填写表单?(练兵场二)
  19. 十六进制转八进制(C语言版)
  20. STM32——快速识别芯片引脚数

热门文章

  1. 罗技F710无线手柄ROS中控制自己麦轮机器人运动
  2. 最适合手机的linux系统,10大主流Linux系统大盘点,总有一个适合你
  3. log4j打印彩色日志
  4. 流量精灵(P2P方式,刷真实流量)
  5. 用python做频数分析_Python统计如何进行DNA序列整理
  6. 在 LaTeX 中调整图片和表格的位置
  7. 国内免费开源CMS系统大全
  8. Nginx应用结业篇---3
  9. 翻译接口java_微软翻译api的java实现
  10. 【PP-YOLOE+】第18届全国大学生智能汽车竞赛百度完全模型组线上资格