基于FPGA的数字时钟系统设计

本文详细描述了如何使用FPGA设计一个包含时钟分频、秒钟和分钟计时以及数码管显示的数字时钟系统,涉及Verilog代码实现及其模块设计。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、设计任务

设计一个能进行分钟计时和秒钟计时的时钟系统,并将计时结果用数码管显示

二、时钟分频模块

模块设计思路:

  • 使用计数的方法,由50MHz系统时钟产生1Hz的分频时钟clk_div。
  • 使用延时打拍的方法,采集分频时钟的上升沿,得到1s的控制信号,该信号用来控制秒计时周期。

V e r i l o g 代码 : Verilog代码: Verilog代码:


//时钟分频模块
module clk_div(
    input           clk        ,
    input           rstn       ,
    output          clk_up
    );
   parameter N=26'd2500_0000 ;//1s/20ns/2

reg [25:0] cnt;
reg clk_r;
reg clk_div;
assign clk_up=~clk_r&&clk_div;
//计数分频
always@(posedge clk or negedge rstn)begin
 if(!rstn)begin
     cnt<='b0;
     clk_div<='b0;
 end
 else if(cnt<N)begin
     cnt<=cnt+1'b1;         
 end
 else begin
     cnt<='b0;
     clk_div=~clk_div;
 end
end

//捕获上升沿生成控制信号
always@(posedge clk or negedge rstn)begin
    if(!rstn)
        clk_r<='b0;
    else 
        clk_r<=clk_div;
end    
endmodule

三、秒钟计时模块

模块设计思路:

  • 秒计时的个位,在1Hz控制信号下自增,计到9后向十位进1。
  • 整体计到59后,产生进位co,来控制分计时计数。

V e r i l o g 代码 : Verilog代码: Verilog代码:


//秒钟计时模块
module cnt60_s(
    input            clk             ,
    input            cin             ,
    input            rstn            ,
    output reg       co              ,//进位
    output reg [3:0] s_single        ,
    output reg [3:0] s_ten
    );
//秒计数 
 always@(posedge clk or negedge rstn)begin
     if(!rstn)begin
         co<=1'b0;
         s_single<='b0;
         s_ten<='b0;
     end
  else if(cin)begin
      if(s_single==4'd9)begin
          s_single<='b0;
          if(s_ten==4'd5)begin
              s_ten<='b0;  
              co<=1'b1;              
          end
          else begin
              s_ten<=s_ten+1'b1;                
              co<=1'b0;              
          end
      end
       else begin
          s_single<=s_single+1'b1;
           co<=1'b0; 
       end
   end
   else begin
      s_single<=s_single;
      s_ten<=s_ten;
   end
 end
endmodule

四、分钟计时模块

模块设计思路:

  • 计数原理同秒计时。
  • 延时打两拍采集秒计时进位信号的上升沿,来对分计时进行控制。采上升沿是因为秒计时的进位信号持续时间会很长,如果采用电平控制,那么在co持续的这段时间内,分计数器会一直计数,这与设计要求不符。

V e r i l o g 代码 : Verilog代码: Verilog代码:

//分钟计时模块
module cnt60_m(
    input             clk             ,
    input             rstn            ,
    input             cin             ,
    output reg [3:0]  m_single        ,
    output reg [3:0]  m_ten
    );
    //检测上升沿
    reg cin_r;
    reg cin_r_r;
    wire flag;
    assign flag=~cin_r_r&cin_r;
always@(posedge clk or negedge rstn)begin
    if(!rstn)begin
        cin_r<='b0;
        cin_r_r<='b0;
    end
    else begin
        cin_r<=cin;
        cin_r_r<=cin_r;
    end
end
//分计数
always@(posedge clk or negedge rstn)begin
      if(!rstn)begin
          m_single<='b0;
          m_ten<='b0;
      end
      else if(flag)begin
          if(m_single==4'd9)begin
              m_single<='b0;
              if(m_ten==4'd5)begin
                  m_single<='b0;
                  m_ten<='b0;
              end
              else begin
                  m_ten<=m_ten+1'b1;
              end
          end
          else begin
              m_single<=m_single+1'b1;
          end
      end
   else begin
      m_single<=m_single;
      m_ten<=m_ten;
   end   
  end
endmodule

五、数码管显示模块

  • 数码管是一种常用的显示器件,指定相应的段选位选即可控制数码管的显示。
  • 位选指定哪一个数码管点亮,而段选则指定数码管的哪一段点亮。
  • 数码管分为共阳极共阴极两种,共阳极数码管是指将数码管各段发光二极管的阳极连接到一起,赋低电平即可点亮共阴极数码管是将数码管各段发光二极管的阴极连接到一起,点亮则需要赋高电平,两种数码管如下图所示。
  • 本次实验使用的是共阳极数码管,需要赋各段低电平,同时段选信号也是低电平有效。
    在这里插入图片描述

模块设计思路:

  • 以一定的扫描周期赋值位选和相应的段选,从程序的角度来讲是将各个数码管逐个点亮。但在扫描速度很快时,人眼难以分辨,看起来就像同时点亮一样。经了解,代码中设置的扫描周期是较为合适的值,读者自行可尝试改变扫描周期,观察实验现象。
  • 使用一个函数来将要显示的数据译码成对应的段选。

V e r i l o g 代码 : Verilog代码: Verilog代码:


//数码管显示模块
module seg(
    input              clk             ,
    input              rstn            ,
    input        [3:0] s_single        ,
    input        [3:0] s_ten           ,
    input        [3:0] m_single        ,
    input        [3:0] m_ten           ,
    output reg   [3:0] csn             ,
    output reg   [6:0] seg_data
    );

parameter SCAN_FREQ=200;
parameter CLK_FREQ=50000000;
parameter SCAN_COUNT=CLK_FREQ/(SCAN_FREQ*6)-1;  //计算扫描计数器最大值  
    
reg [20:0] scan_cnt;
reg [2:0] csn_cnt;
//段选扫描计数
always@(posedge clk or negedge rstn)begin
    if(!rstn)begin
        scan_cnt<='b0;
        csn_cnt<='b0;
    end
    else if(scan_cnt==SCAN_COUNT)begin
        scan_cnt<='b0; 
        if(csn_cnt==3'd3)begin
            csn_cnt<=3'd0;
        end   
        else begin
            csn_cnt<=csn_cnt+1'b1;
        end
    end
    else begin
        scan_cnt<=scan_cnt+1'b1;
    end    
end 
//以扫描周期赋值段选和位选
always@(posedge clk or negedge rstn)begin
    if(!rstn)begin
        csn<=4'b1111;
        seg_data<=translate(0);
    end
    else begin
        case(csn_cnt)
            3'd0:begin
                csn<=4'b1110;
                seg_data<=translate(s_single);                
            end
            3'd1:begin
                csn<=4'b1101;
                seg_data<=translate(s_ten);
            end
            3'd2:begin
                csn<=4'b1011;
                seg_data<=translate(m_single);
            end
            3'd3:begin
                csn<=4'b0111;
                seg_data<=translate(m_ten);
            end
        endcase
    end

end
//译码函数       
function [6:0] translate;
    input [3:0] data;
    begin
        case(data)
            4'd0:translate=7'b000_0001;//高位到低位分别是数码管的a~g
            4'd1:translate=7'b100_1111;
            4'd2:translate=7'b001_0010;
            4'd3:translate=7'b000_0110;                 
            4'd4:translate=7'b100_1100;                 
            4'd5:translate=7'b010_0100;                 
            4'd6:translate=7'b010_0000;                 
            4'd7:translate=7'b000_1111;                 
            4'd8:translate=7'b000_0000;                 
            4'd9:translate=7'b000_0100;                 
        endcase       
    end
    endfunction
endmodule

六、顶层模块

V e r i l o g 代码 : Verilog代码: Verilog代码:

//顶层模块
module sys_top(
    input clk,
    input rstn,
    output [3:0] csn,
    output [6:0] seg_data
    );
    
wire clk_up; 
wire co_s;
wire [3:0] s_single;
wire [3:0] s_ten ;
wire [3:0] m_single;
wire [3:0] m_ten ;

clk_div clk_div_u(
    .clk      (clk)  ,
    .rstn     (rstn)  ,
    .clk_up   (clk_up)
    );

cnt60_s cnt60_s_u(
    . clk       (clk)      ,
    . cin       (clk_up)      ,
    . rstn      (rstn)      ,
    . co        (co_s)      ,//进位
    . s_single  (s_single)      ,
    . s_ten     (s_ten)
    );  


cnt60_m cnt60_m_u(
    . clk       (clk)      ,
    . rstn      (rstn)      ,
    . cin       (co_s)      ,
    . m_single  (m_single)      ,
    . m_ten     (m_ten)
    );

seg seg_u(
    . clk       (clk)      ,
    . rstn      (rstn)      ,
    . s_single  (s_single)      ,
    . s_ten     (s_ten)      ,
    . m_single  (m_single)      ,
    . m_ten     (m_ten)      ,
    . csn       (csn)      ,
    . seg_data  (seg_data)
    );

endmodule
 
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

hi小瑞同学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值