FPGA数字时钟系统-设计教程

1 post / 0 new
2303896998_680
2303896998_680的头像
FPGA数字时钟系统-设计教程

数字钟是一个常用的数字系统,其主要功能是计时和显示时间。这里通过一个数字钟表的模块化设计方法,说明自顶向下的模块化设计方法和实现一个项目的设计步骤。

这里实现的电子表具有显示和调时的基本功能,可以显示时、分、秒和毫秒,并通过按键进行工作模式选择,工作模式有4种,分别是正常计时模式、调时模式、调分模式、调秒模式。

数字钟

“”

构成电子表的基本基本模块有四个,分别是时钟调校及计时模块myclock、整数分频模块int_div、时钟信号选择模块clkgen和七段显示模块disp_dec。

1、时钟调校及计时模块

时钟调校及计时模块myclock实现的功能是根据当前的工作状态进行时、分、秒的调整或正常的计时。代码端口说明如下:

输入信号:
RSTn—复位信号;
CLK—100Hz时钟信号;
FLAG[1:0]—工作模式控制信号,模式定义为:00表示正常显示,01表示调时,10表示调分,11表示调秒;
UP—调校模式时以加1方式调节信号;
DN—调校模式时以减1方式调节信号。

输出信号:
H[7:0]—“时”数据(十六进制);
M[7:0]—“分”数据(十六进制);
S[7:0]——“秒”数据(十六进制);
MS[7:0]—“百分秒”数据(十六进制)。

该模块的设计思路是,当复位信号RSTn有效时,时、分、秒信号清零,否则工作模式控制信号FLAG的值决定当前的工作状态。

当FLAG=2’b00时,电子表工作在正常计时状态,对输入的100Hz的时钟信号clk进行计数,修改当前的百分秒(MS)、秒(S)、分(M)和时(H)的计数值;

当FLAG信号=2’b01时,电子表工作在“时”校正状态,若此时UP信号有效则H加1,若此时DN信号有效则H减1,;

当FLAG信号=1’b10时,电子表工作在“分” 校正状态,若此时UP信号有效则M加1,若此时DN信号有效则M减1;

当FLAG=2’b11时,电子表工作在“秒”校正状态,其UP和DN的控制过程与“时”、“分”类似。

module myclock(RSTn,CLK,FLAG,UP,DN,H,M,S,MS);
  inputRSTn,CLK,UP,DN;
  output[7:0] H,M,S;
  output[7:0] MS;
  input[1:0]FLAG;
  
  reg[5:0] m_H,m_M,m_S;
  reg[6:0] m_MS;
  
  assignH = m_H;
  assignM = m_M;
  assignS = m_S;
  assignMS = m_MS;
  
  always@(posedge CLK)
    if(~RSTn) begin
        m_H<= 8'd23;
        m_M<= 8'd52;
        m_S<= 8'b0;
        m_MS<= 8'b0;
    end else if(FLAG==2'b01)begin//调时状态
        if(UP)begin
          if(m_H==8'd23) m_H <=8'd0;
          else m_H = m_H + 1'b1;
        end else if(DN) begin
          if(m_H==8'h00) m_H <=8'd23;
          else  m_H <= m_H - 1'b1;
        end
    end elseif(FLAG==2'b10) begin //调分状态
        if(UP)
          if(m_M==8'd59)  m_M <=8'd0;
          else  m_M <= m_M + 1'b1;
        elseif(DN)
          if(m_M==8'h00) m_M <=8'd59;
          else  m_M <= m_M - 1'b1;
    end else if(FLAG==2'b11)begin //调秒状态
        if(UP)
           if(m_S==8'd59) m_S <=8'b0;
           else m_S <= m_S+ 1'b1;
        else if(DN)
            if(m_S==8'h00) m_S <=8'd59;
            else m_S <= m_S- 1'b1;
    end else begin          //正常计时状态
        if(m_MS==8'd99) begin
          m_MS <=8'd0;
             if(m_S==8'd59) begin
                 m_S <= 8'd0;
                 if(m_M==8'd59) begin
                     m_M<=8'd0;
                     if(m_H==8'd23) m_H <= 0;
                     else m_H <= m_H+ 1'b1;
                 end else
                       m_M<= m_M + 8'd1;
              end else
                 m_S<=m_S+1'b1;
          end else
            m_MS<=m_MS+1'b1;
      end
endmodule

( 文中代码框,均可横向滑动 )

2、整数分频模块

由于数字系统提供的基准时钟信号频率往往较高,因此需要分频模块产生所需频率的失踪信号,例如上面时钟校正及计时模块所需的100Hz的时钟信号。

整数分频模块int_div可以实现对输入时钟clock进行F_DIV分频后输出clk_out。F_DIV分频系数范围为1~2^n(n=F_DIV_WIDTH),若要改变分频系数,改变参数F_DIV或F_DIV_WIDTH到相应范围即可。

若分频系数为偶数,则输出时钟占空比为50%;若分频系数为奇数,则输出的时钟占空比取决于输入的时钟占空比和分频系数(当输入为50%时,输出也是50%)。

module int_div(clock,clk_out);

  parameter F_DIV=48000000;    //分频系数
  parameter F_DIV_WIDTH=32;  

  //分频计数器宽度
  input clock;                //输入时钟
  output clk_out;          //输出时钟

  reg clk_p_r;
  reg clk_n_r;
  reg[F_DIV_WIDTH-1:0] count_p;
  reg[F_DIV_WIDTH-1:0] count_n;

  wire full_div_p;          //上升沿计数满标志
  wire half_div_p;         //上升沿计数半满标志
  wire full_div_n;          //下降沿计数满标志
  wire half_div_n;         //下降沿计数半满标志
 
  //判断计数标志位置位与否
  assign full_div_p=(count_p<F_DIV-1);
  assign half_div_p=(count_p<(F_DIV>>1)-1);
  assign full_div_n=(count_n<F_DIV-1);
  assign half_div_n=(count_n<(F_DIV>>1)-1);
 
  //时钟输出
  assignclk_out=(F_DIV==1)?clock:(F_DIV[0]?(clk_p_r&clk_n_r):clk_p_r);
  
  //上升沿脉冲计数
  always @(posedge clock) begin    
      if(full_div_p) begin
          count_p<=count_p+1'b1;
          if(half_div_p) clk_p_r<=1'b0;
          else clk_p_r<=1'b1;
      end else begin
          count_p<=0;
          clk_p_r<=1'b0;
      end
  end

  //下降沿脉冲计数
  always @(negedge clock)  begin
     if(full_div_n)begin
          count_n<=count_n+1'b1;
          if(half_div_n) clk_n_r<=1'b0;
          else clk_n_r=1'b1;
      end else begin
          count_n<=0;
          clk_n_r<=1'b0;
       end
    end

endmodule

3、时钟信号选择模块

时钟信号选择模块clkgen实际上时一个二选一电路,用于提供时钟调校及计时时模块所需的时钟脉冲。

当电子表工作在正常计时状态时选择100Hz时钟信号;当电子表工作在调时、调分、调秒是那种设置模式时,如果采用100Hz时钟信号,那么手动一次按键可能引起设置数据的一串跳变,因此为了方便按键时动作对时间的设置,这里采用2Hz的时钟信号。

其端口说明如下:
flag—时钟选择输入信号;
clk_100Hz—输入100Hz的时钟信号;
clk_2Hz—输入2Hz的时钟信号;
Clkout—输出时钟信号。

module clkgen(flag,clk_100hz,clk_2hz,clkout);
  input[1:0] flag;    
  //若flag=0则clkout=100Hz,否则clkout=2Hz
  input clk_100hz,clk_2hz;
  output clkout;
  assign clkout=(flag==2'b00)?clk_100hz:clk_2hz;
endmodule

4、七段显示设置

为了对时钟时、分、秒和毫秒数据输出显示,需要将时、分、秒和毫秒的二进制转换为十进制数。由于时、分、秒最大到60,毫秒最大到99,所以十进制数选择2位就能满足要求。为了在七段数码管输出时间数据,还需要将显示的十进制数转化为七段段码。以上功能分别由BCD码显示模块和七段译码管模块来实现。

4.1 BCD码显示模块

BCD码显示模块的功能是将8位二进制数转化为2位十进制数后,进行七段段译码显示。为了实现显示功能,在其内部调用了dual_hex 2位七段显示模块。

其端口说明如下:
输入信号:hex—2位8421BCD码输入。
输出信号:dispout—2位8421码对应的七段数码管段码。

module disp_dec(hex,dispout);
  input[7:0] hex;                      
    //八位二进制输入数据
  output[15:0] dispout;                
    //2位十进制的七段段码显示数据
  reg[7:0] dec;
    //8位二进制数转化为2位BCD码
  always @(hex) begin 
      dec[7:4]=hex/4'd10;
      dec[3:0]=hex%4'd10;
    end
  //调用2位共阳极七段显示模块
  dual_hex u1(1'b0,dec,dispout);         
endmodule

4.2 二位七段显示模块

二位七段显示模块的功能是将2进制或十六进制数转化为对应的七段段码,内部调用了一位七段译码模块seg_decoder。

module dual_hex(iflag,datain,dispout);
  input iflag;                         
  //共阴或共阳输出选择
  input[7:0] datain;                   
  //2位的十进制或十六进制数据
  output[15:0] dispout;                
  //2个七段段码数据
  seg_decoderu1(iflag,datain[7:4],dispout[15:8]);
  seg_decoder u2(iflag,datain[3:0],dispout[7:0]);
endmodule

4.3 一位七段译码模块

一位七段译码模块的功能是将4位二进制数转化为对应的共阴或共阳七段段码。

module seg_decoder(iflag,iA,oY);
  input iflag;   //共阴或共阳输出选择
  input[3:0] iA;   //4位二进制数据
  output reg[7:0] oY;  //七段段码显示数据
  
  always @(iflag,iA)
    begin
      case(iA)    //共阴级七段输出
        4'b0000:oY=8'h3f;
        4'b0001:oY=8'h06;
        4'b0010:oY=8'h5b;
        4'b0011:oY=8'h4f;
        4'b0100:oY=8'h66;
        4'b0101:oY=8'h6d;
        4'b0110:oY=8'h7d;
        4'b0111:oY=8'h27;
        4'b1000:oY=8'h7f;
        4'b1001:oY=8'h6f;
        4'b1010:oY=8'h77;
        4'b1011:oY=8'h7c;
        4'b1100:oY=8'h58;
        4'b1101:oY=8'h5e;
        4'b1110:oY=8'h79;
        4'b1111:oY=8'h71;
      endcase
      if(!iflag)
        oY=~oY;   //共阳极七段输出
   end
endmodule

5、顶层模块的实现

顶层模块是将各功能模块连接起来,实现电子表的完整功能。其端口信号说明如下:

输入信号:
iCLK—50—50MHz时钟信号;
RSTn—复位信号;
FLAG—工作模式控制信号,模式定义为:00表示正常显示,01表示调时,10表示调分,11调秒;
UP—调校模式时以加1方式调节信号;
DN—调校模式时以减1方式调节信号。

输出信号:
H_dis—“小时”数据的七段数码管数据;
M_dis—“分钟”数据的七段数码管数据;
S_dis—“秒”数据的七段译码管数据;
MS_dis—“百分秒”数据的七段译码管数据;
Mode—工作模式输出;
H—“时”数据(十六进制);
M—“分”数据(十六进制);
S—“秒”数据(十六进制);
MS—“百分秒”数据(十六进制)。

moduleclock(iCLK_50,RSTn,FLAG,UP,DN,H_dis,M_dis,S_dis,MS_dis,Mode,H,M,S);
  inputiCLK_50;
  inputRSTn,UP,DN;
  input[1:0] FLAG;
  output[1:0] Mode;
  output[15:0] H_dis,M_dis,S_dis,MS_dis;
  output[7:0] H,M,S;
  
  wire[7:0] MS;
  wireclk_100hz,clk_2hz;
  wireclk;
  assignMode=FLAG;
         
  int_div#(500000,32) nclk100(iCLK_50,clk_100hz);
  int_div#(50000000,32) nclk2(iCLK_50,clk_2hz);
  clkgenu0(FLAG,clk_100ha,clk_2hz,clk);
  myclocku1(RSTn,clk,FLAG,UP,DN,H,M,S,MS);
  disp_decHour(H,H_dis);
  disp_decMinute(M,M_dis);
  disp_decSecond(S,S_dis);
  disp_dechour(MS,MS_dis);
 
endmodule

本文转载自:电子开发圈(作者:C老师)
免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理。