基于FPGA的数字时钟的设计课设(HUAT)


前言

学校黄老师的FPGA的设计课设,最后的课设为数字时钟,实现分时的计数功能,带有整点报时,按键调节的功能,供电子类学生学习和参考。


一、数字时钟课设目标

l 基本要求

1、能进行正常的时、分、秒计时功能,分别由6个数码管显示24小时、60分钟、60秒钟的计数器显示。

2、能利用实验系统上的按键实现“校时”“校分”功能:

⑴按下“SA”键时,计时器迅速递增,并按24小时循环,计满23小时后回“00”

⑵按下“SB”键时,计分器迅速递增,并按59分钟循环,计满59分钟后回“00”,但不向“时”进位;

⑷要求按下“SA”、“SB”或“SC”时均不产生数字跳变(SA”、“SB”、“SC”按键是有抖动的,必须对其消除抖动处理)。

3、能利用扬声器做整点报时:

⑴当计时到达5950秒时开始报时,在5950秒、52秒、54秒、56秒、58秒鸣叫,鸣叫声频率可定为512Hz

⑵到达5960秒时为最后一声整点报时,整点报时频率可定为1024Hz

4、用层次化设计方法设计该电路,用Verilog语言编写各个功能模块。

5、完成电路设计后,用实验系统下载验证。

l 发挥部分

   要求实现闹铃功能,准确到分钟。用功能仿真的方法验证,可通过观察有关波形确认电路设计是否正确。

二、部分代码

1.clock.v代码的编写

     秒计数器
 

reg  [25:0]  cnt2;
wire     add_cnt2;
wire     end_cnt2;
always@(posedge clk or negedge rst_n)begin //1s计数,可以通过按键更改

if(rst_n == 0)
cnt2 <= 0;
else if (add_cnt2)begin
     if(end_cnt2)
        cnt2 <= 0;
     else
        cnt2 <= cnt2 +1;
 end
end

assign add_cnt2 = 1;
assign end_cnt2 = add_cnt2 && cnt2 == y-1;//y可以通过按键更改,实现按键调节时钟频率

us计数器
 

reg [8:0]  cnt0;

always@(posedge clk or negedge rst_n) //数码管显示

  if(rst_n == 0)
  cnt0 <= 0;
  else if (add_cnt0)
  begin
     if(end_cnt0 )
       cnt0 <= 0;
     else
       cnt0 <= cnt0 +1;
    end
    
assign add_cnt0 = 1;
assign end_cnt0 = add_cnt0 && cnt0== TIME_1US-1;//通过短时间的数码管显示,实现动态扫描

数码管显示   对4个数码管进行us级的循环点亮,达到视觉上的暂留
 

reg  [2:0]  cnt1;
wire     add_cnt1;
wire     end_cnt1;

always@(posedge clk or negedge rst_n)begin //4个数码管

if(rst_n == 0)
cnt1 <= 0;
else if (add_cnt1)begin
     if(end_cnt1)
        cnt1 <= 0;
     else
        cnt1 <= cnt1 +1;
 end
 
       
end

assign add_cnt1 = end_cnt0;
assign end_cnt1 = add_cnt1 && cnt1 == 4-1;

数码管动态扫描

always@(posedge clk or negedge rst_n) //动态扫描
begin
    if(rst_n == 1'b0)
    seg_sel <= 4'b111_1;
        else if (cnt1 == 0)
    seg_sel <= 4'b111_0;
        else if (cnt1 == 1)
    seg_sel <= 4'b110_1;
        else if (cnt1 == 2)
    seg_sel <= 4'b101_1;
        else if (cnt1 == 3)
    seg_sel <= 4'b011_1;
    else
    seg_sel <= 4'b111_1;
    
end 

数码管选择显示
 

reg [3:0] sel_data;

always@(posedge clk or negedge rst_n)  // 选择显示
begin

  if(rst_n == 0)
  seg_ment <=8'hc0;
  else if (sel_data==0)
  seg_ment <= 8'hc0;
    else if (sel_data==1)
  seg_ment <= 8'hf9;
    else if (sel_data==2)
  seg_ment <= 8'ha4;
    else if (sel_data==3)
  seg_ment <= 8'hb0;
  else if (sel_data==4)
  seg_ment <= 8'h99;
  else if (sel_data==5)
  seg_ment <= 8'h92;
  else if (sel_data==6)
  seg_ment <= 8'h82;
  else if (sel_data==7)
  seg_ment <= 8'hf8;
  else if (sel_data==8)
  seg_ment <= 8'h80;
  else if (sel_data==9)
  seg_ment <= 8'h90;
    else
    seg_ment <= 8'hc0;
end

always@(*)
begin
    
    if (cnt1==0)
     sel_data <= m_g;
    else if (cnt1==1)
     sel_data <= m_s;
    else if (cnt1==2)
     sel_data <= s_g;
    else
     sel_data <= s_s;
     
end

分个位 分十位 时个位 时十位
 

reg  [3:0]  m_g;
wire     add_m_g;
wire     end_m_g;
always@(posedge clk or negedge rst_n)begin

if(rst_n == 0)
m_g <= 0;


else if (add_m_g)
   begin
     if(end_m_g)
        m_g <= 0;
    
     else
        m_g <= m_g +1;
   end
else
        m_g <= m_g;

end

assign add_m_g = end_cnt2;
assign end_m_g = add_m_g && m_g == 10-1;


reg  [2:0]  m_s;
wire     add_m_s;
wire     end_m_s;
always@(posedge clk or negedge rst_n)begin

if(rst_n == 0)
m_s <= 0;
else if (add_m_s)begin
     if(end_m_s)
        m_s <= 0;
        
     else
        m_s <= m_s +1;
 end
end

assign add_m_s = end_m_g;
assign end_m_s = add_m_s && m_s == 6-1;


reg  [3:0]  s_g;
wire     add_s_g;
wire     end_s_g;
always@(posedge clk or negedge rst_n)begin

if(rst_n == 0)
s_g <= 0;
else if (add_s_g)
begin
     if(end_s_g)
        s_g <= 0;
     else if (key_in1 == 1'b0)//保证按键按下时位不产生变化
        s_g <= s_g ;
     else
        s_g <= s_g +1;
 end

 else
     s_g <= s_g;
end

assign add_s_g = end_m_s;
assign end_s_g = add_s_g && s_g == x-1;

reg  [1:0]  s_s;
wire     add_s_s;
wire     end_s_s;
always@(posedge clk or negedge rst_n)begin

if(rst_n == 0)
s_s <= 0;
else if (add_s_s)begin
     if(end_s_s)
        s_s <= 0;
     else
        s_s <= s_s +1;
 end
end

assign add_s_s = end_s_g;
assign end_s_s = add_s_s && s_s == 3-1;   

always@(*)
begin

if(s_s==2)
x=4;
else
x=10;

end

上述基本可以达到数码管显示计数的效果

//按键更改频率
 

parameter  TIME_1S = 5000_000_0;
parameter  TIME_1MS = 5000_000;
parameter  TIME_1NS = 5000_00;


always@(posedge clk or negedge rst_n)
    if(rst_n == 1'b0)
       y <= TIME_1S;
     else if ( key_in1 == 1'b0) //按键1改时钟频率,同时s_g数码管不变
     begin
     
       y=TIME_1MS;
       
     end
     else if (key_in2 == 1'b0)//按键2 改时钟频率,改的更快
     y=TIME_1NS;
     else
     begin
        y <= TIME_1S;
        
        
     end

beep报时
 

//beep 计数
reg   [24:0]   cnt;
reg   [2:0]    cnt_500ms;
reg   [17:0]   freq_cnt;
reg   [17:0]   freq_data;
reg   [17:0]   freq_data2;
wire   [16:0]   duty_data1;
wire   [16:0]   duty_data2;

// beep 0.5s的计数
always@(posedge clk or negedge rst_n)
    if(rst_n == 1'b0)
       cnt <= 25'd0;
    else if (cnt == CNT_MAX)
        cnt <= 25'd0;
     else
       cnt <= cnt + 25'b1;

always@(posedge clk or negedge rst_n)
    if(rst_n == 1'b0)
       cnt_500ms <= 3'd0;
    else if ((cnt_500ms == 3'd6)&&(cnt == CNT_MAX))
       cnt_500ms <= 3'd0;
    else if  (cnt == CNT_MAX)
       cnt_500ms <= cnt_500ms + 3'd1;
    else
       cnt_500ms <= cnt_500ms;
 //频率计数      
always@(posedge clk or negedge rst_n)
    if(rst_n == 1'b0)
       freq_cnt <= 18'd0;
    else if ((freq_cnt == freq_data)||(cnt == CNT_MAX))
       freq_cnt <= 18'd0;
    else
    freq_cnt  <= freq_cnt + 18'd1;
    
always@(posedge clk or negedge rst_n)
    if(rst_n == 1'b0)
    freq_data <= DO;
    else    case(cnt_500ms)
        3'd0:
        begin
        freq_data <= DO;
        freq_data2<= XI;
        end

       
        
      default :
      begin
      freq_data <= DO;
      freq_data2<= XI;
      end
      
    endcase
    
 assign    duty_data1 = freq_data >> 1;
 assign    duty_data2 = freq_data2 >> 1;

  //报时
 always@(posedge clk or negedge rst_n)
    if(rst_n == 1'b0)
        beep <= 1'b0;
    else if ((freq_cnt >= duty_data1)&&(((m_s == 5)&&(m_g== 2))||((m_s == 5)&&
    (m_g ==0))||((m_s == 5)&&(m_g ==4))||((m_s == 5)&&(m_g ==6))||((m_s == 5)&&(m_g ==8))))// 50 52 54 56 58 报时
        beep <= 1'b1;
     else if ((freq_cnt >= duty_data2)&&(((m_s == 0)&&(m_g== 0))))// 正点报时
        beep <= 1'b1;
    else
        beep <= 1'b0;  

2.完整代码

module clock_k_b
(

     input   clk,
     input   rst_n,
     input wire key_in1,
     input wire key_in2,

     output  reg [3:0] seg_sel,
     output reg [7:0] seg_ment,
     output reg   beep

);

parameter  TIME_1US = 500;
parameter  TIME_1S = 5000_000_0;
parameter  TIME_1MS = 5000_000;
parameter  TIME_1NS = 5000_00;
parameter  CNT_MAX =25'd24_999_999;
parameter  DO = 18'd190839;
parameter  XI = 18'd101213;

//
reg [25:0] y;
reg [8:0]  cnt0;
reg [3:0]   x;
wire  add_cnt0;
wire  end_cnt0;

//beep 计数
reg   [24:0]   cnt;
reg   [2:0]    cnt_500ms;
reg   [17:0]   freq_cnt;
reg   [17:0]   freq_data;
reg   [17:0]   freq_data2;
wire   [16:0]   duty_data1;
wire   [16:0]   duty_data2;

// beep 0.5s的计数
always@(posedge clk or negedge rst_n)
    if(rst_n == 1'b0)
       cnt <= 25'd0;
    else if (cnt == CNT_MAX)
        cnt <= 25'd0;
     else
       cnt <= cnt + 25'b1;

always@(posedge clk or negedge rst_n)
    if(rst_n == 1'b0)
       cnt_500ms <= 3'd0;
    else if ((cnt_500ms == 3'd6)&&(cnt == CNT_MAX))
       cnt_500ms <= 3'd0;
    else if  (cnt == CNT_MAX)
       cnt_500ms <= cnt_500ms + 3'd1;
    else
       cnt_500ms <= cnt_500ms;
 //频率计数      
always@(posedge clk or negedge rst_n)
    if(rst_n == 1'b0)
       freq_cnt <= 18'd0;
    else if ((freq_cnt == freq_data)||(cnt == CNT_MAX))
       freq_cnt <= 18'd0;
    else
    freq_cnt  <= freq_cnt + 18'd1;
    
always@(posedge clk or negedge rst_n)
    if(rst_n == 1'b0)
    freq_data <= DO;
    else    case(cnt_500ms)
        3'd0:
        begin
        freq_data <= DO;
        freq_data2<= XI;
        end

       
        
      default :
      begin
      freq_data <= DO;
      freq_data2<= XI;
      end
      
    endcase
    
 assign    duty_data1 = freq_data >> 1;
 assign    duty_data2 = freq_data2 >> 1;

  //报时
 always@(posedge clk or negedge rst_n)
    if(rst_n == 1'b0)
        beep <= 1'b0;
    else if ((freq_cnt >= duty_data1)&&(((m_s == 5)&&(m_g== 2))||((m_s == 5)&&
    (m_g ==0))||((m_s == 5)&&(m_g ==4))||((m_s == 5)&&(m_g ==6))||((m_s == 5)&&(m_g ==8))))// 50 52 54 56 58 报时
        beep <= 1'b1;
     else if ((freq_cnt >= duty_data2)&&(((m_s == 0)&&(m_g== 0))))// 正点报时
        beep <= 1'b1;
    else
        beep <= 1'b0;   
always@(posedge clk or negedge rst_n)
    if(rst_n == 1'b0)
       y <= TIME_1S;
     else if ( key_in1 == 1'b0) //按键1改时钟频率,同时s_g数码管不变
     begin
     
       y=TIME_1MS;
       
     end
     else if (key_in2 == 1'b0)//按键2 改时钟频率,改的更快
     y=TIME_1NS;
     else
     begin
        y <= TIME_1S;
        
        
     end
       
/*  always@(posedge clk or negedge rst_n)
    if(rst_n == 1'b0)
    begin
        //beep <= 1'b0;
        led <= 1'b1;
    end
    else if (m_s>= 5)
    begin
        //beep <= 1'b1;
        led <= ~led;
    end
    else
        begin     
        //beep <= 1'b0;
        led <= led;
        end */
       
always@(posedge clk or negedge rst_n) //数码管显示

  if(rst_n == 0)
  cnt0 <= 0;
  else if (add_cnt0)
  begin
     if(end_cnt0 )
       cnt0 <= 0;
     else
       cnt0 <= cnt0 +1;
    end
    
assign add_cnt0 = 1;
assign end_cnt0 = add_cnt0 && cnt0== TIME_1US-1;

reg  [2:0]  cnt1;
wire     add_cnt1;
wire     end_cnt1;

always@(posedge clk or negedge rst_n)begin //4个数码管

if(rst_n == 0)
cnt1 <= 0;
else if (add_cnt1)begin
     if(end_cnt1)
        cnt1 <= 0;
     else
        cnt1 <= cnt1 +1;
 end
 
       
end

assign add_cnt1 = end_cnt0;
assign end_cnt1 = add_cnt1 && cnt1 == 4-1;

reg  [25:0]  cnt2;
wire     add_cnt2;
wire     end_cnt2;
always@(posedge clk or negedge rst_n)begin //1s计数,可以通过按键更改

if(rst_n == 0)
cnt2 <= 0;
else if (add_cnt2)begin
     if(end_cnt2)
        cnt2 <= 0;
     else
        cnt2 <= cnt2 +1;
 end
end

assign add_cnt2 = 1;
assign end_cnt2 = add_cnt2 && cnt2 == y-1;

reg [3:0] sel_data;

always@(posedge clk or negedge rst_n)  // 选择显示
begin

  if(rst_n == 0)
  seg_ment <=8'hc0;
  else if (sel_data==0)
  seg_ment <= 8'hc0;
    else if (sel_data==1)
  seg_ment <= 8'hf9;
    else if (sel_data==2)
  seg_ment <= 8'ha4;
    else if (sel_data==3)
  seg_ment <= 8'hb0;
  else if (sel_data==4)
  seg_ment <= 8'h99;
  else if (sel_data==5)
  seg_ment <= 8'h92;
  else if (sel_data==6)
  seg_ment <= 8'h82;
  else if (sel_data==7)
  seg_ment <= 8'hf8;
  else if (sel_data==8)
  seg_ment <= 8'h80;
  else if (sel_data==9)
  seg_ment <= 8'h90;
    else
    seg_ment <= 8'hc0;
end
 
 
 
always@(posedge clk or negedge rst_n) //动态扫描
begin
    if(rst_n == 1'b0)
    seg_sel <= 4'b111_1;
        else if (cnt1 == 0)
    seg_sel <= 4'b111_0;
        else if (cnt1 == 1)
    seg_sel <= 4'b110_1;
        else if (cnt1 == 2)
    seg_sel <= 4'b101_1;
        else if (cnt1 == 3)
    seg_sel <= 4'b011_1;
    else
    seg_sel <= 4'b111_1;
    
end    

reg  [3:0]  m_g;
wire     add_m_g;
wire     end_m_g;
always@(posedge clk or negedge rst_n)begin

if(rst_n == 0)
m_g <= 0;


else if (add_m_g)
   begin
     if(end_m_g)
        m_g <= 0;
    
     else
        m_g <= m_g +1;
   end
else
        m_g <= m_g;

end

assign add_m_g = end_cnt2;
assign end_m_g = add_m_g && m_g == 10-1;


reg  [2:0]  m_s;
wire     add_m_s;
wire     end_m_s;
always@(posedge clk or negedge rst_n)begin

if(rst_n == 0)
m_s <= 0;
else if (add_m_s)begin
     if(end_m_s)
        m_s <= 0;
        
     else
        m_s <= m_s +1;
 end
end

assign add_m_s = end_m_g;
assign end_m_s = add_m_s && m_s == 6-1;


reg  [3:0]  s_g;
wire     add_s_g;
wire     end_s_g;
always@(posedge clk or negedge rst_n)begin

if(rst_n == 0)
s_g <= 0;
else if (add_s_g)
begin
     if(end_s_g)
        s_g <= 0;
     else if (key_in1 == 1'b0)
        s_g <= s_g ;
     else
        s_g <= s_g +1;
 end

 else
     s_g <= s_g;
end

assign add_s_g = end_m_s;
assign end_s_g = add_s_g && s_g == x-1;

reg  [1:0]  s_s;
wire     add_s_s;
wire     end_s_s;
always@(posedge clk or negedge rst_n)begin

if(rst_n == 0)
s_s <= 0;
else if (add_s_s)begin
     if(end_s_s)
        s_s <= 0;
     else
        s_s <= s_s +1;
 end
end

assign add_s_s = end_s_g;
assign end_s_s = add_s_s && s_s == 3-1;   

always@(*)
begin

if(s_s==2)
x=4;
else
x=10;

end

/* always@(posedge clk or negedge rst_n)
begin
    if(rst_n == 1'b0)
     sel_data <= 0;
    else if (cnt1==0)
     sel_data <= m_g;
    else if (cnt1==1)
     sel_data <= m_s;
    else if (cnt1==2)
     sel_data <= s_g;
    else if (cnt1==3)
     sel_data <= s_s;
     
end  */
 
always@(*)
begin
    
    if (cnt1==0)
     sel_data <= m_g;
    else if (cnt1==1)
     sel_data <= m_s;
    else if (cnt1==2)
     sel_data <= s_g;
    else
     sel_data <= s_s;
     
end  


    
endmodule

3.仿真代码

`timescale 1ns/1ns 

module tb_clock();

parameter  CYCLE = 20;

reg clk;
reg rst_n;

wire [3:0] seg_sel;
wire [6:0] seg_ment;

clock 
#(
    .TIME_1US (100),
    .TIME_1S  (10)
)
 clock_inst
(
     .clk      (clk),
     .rst_n    (rst_n),
     .seg_sel  (seg_sel),
     .seg_ment (seg_ment)
);

initial 
    begin 
    clk = 0;
    forever#(CYCLE/2)begin  
     clk=~clk;
    end 
  end
  
initial 
    begin
    #1
    rst_n = 0;
    #(10*CYCLE);
    rst_n = 1;
    end
    
     

endmodule

总结

这是暑假自学搞得数字时钟的设计,虽然功能齐全,但是难免有点不完美的地方,也可以通过层次化的设计让代码变得没有这么长,可以通过这一份代码,要是期末课设弄不出来,参考一下也是可以的,免得挂了,功能反正都可以实现。若有更好的点子也可以一起交流学习一下。


  • 8
    点赞
  • 92
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
基于FPGA设计的万年历数字时钟课程设计Quartus工程源码+设计报告文档资料,可以做为你的学习设计参考。 系统总体设计 2.1.1 系统功能介绍 本次万年历实现的功能有: (1)年月日、时分秒的走时、设置及其显示。 (2)闹钟功能、闹钟设置及其闹钟设置显示。 2.1.2 系统硬件介绍 本次万年历设计使用的硬件资源有:6个数码管、三个微动开关、两个拨码开关和两个LED灯。 其中,6个数码管选择显示年月日、时分秒及其闹钟设置的时分秒。三个按键分别为翻页按键(选择数码管显示内容)、选择按键(选择需调整的内容)、加一按键(调整的内容加一),用于翻页和时间及其闹钟的设置。两个拨码开关分别为闹钟使能开关和复位开关,用于闹钟的打开及其关闭和将整个系统复位。两个LED灯分别为闹钟开关状态和闹钟标志(代替蜂鸣器)。 2.1.3 系统总体框图介绍 整个系统采用自顶向下的设计方法,从系统的总体功能出发,将整个系统划分为多个模块,然后再将各个模块划分为多个功能部分,编程完成后将各个部分联系起来组成整个系统。 module calendar(system_clk,reset,key_turn,key_switch,key_add,switch_alarm,led,beep,hex0,hex1,hex2,hex3,hex4,hex5); input system_clk,reset,key_turn,key_switch,key_add,switch_alarm;//系统时钟、复位、三个按键和一个拨码开关 output led,beep; //LED灯和蜂鸣器 output [6:0] hex0; //输出:数码管0 output [6:0] hex1; //输出:数码管1 output [6:0] hex2; //输出:数码管2 output [6:0] hex3; //输出:数码管3 output [6:0] hex4; //输出:数码管4 output [6:0] hex5; //输出:数码管5 wire second_add,minute_add,hour_add,day_add,month_add,year_add,alarm_second_add,alarm_minute_add,alarm_hour_add; //专属增一信号 wire [1:0] flag_turn; //翻页状态信号 wire [1:0] flag_switch; //闪烁数码管选择信号 wire select_sign; //选择状态 wire [5:0] second; //秒 wire [5:0] minute; //分 wire [4:0] hour; //小时 wire [4:0] day; //日 wire [3:0] month; //月 wire [6:0] year; //年 wire [5:0] alarm_second; //闹钟秒 wire [5:0] alarm_minute; //闹钟分 wire [4:0] alarm_hour; //闹钟小时 //例化按键模块 key_drive_module use_key_drive_module( .system_clk (system_clk), .reset (reset), .key_turn (key_turn), .key_switch (key_switch), .key_add (key_add), .flag_switch (flag_switch), .flag_turn (flag_turn), .second_add (second_add), .minute_add (minute_add), .hour_add (hour_add), .day_add (day_add), .month_add (month_add), .year_add (year_add), .alarm_second_add (alarm_second_add), .alarm_minute_add (alarm_minute_add), .alarm_hour_add (alarm_hour_add), .select_sign (select_sign) ); //例化时钟模块 clock

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值