FPGA-数字闹钟

用FPGA设计一个数字闹钟应该说是一个比较综合的小系统,包含了按键、数码
管、状态机等内容,本文主要是讲述三键输入的数字闹钟总体的设计,整个作品和小时候两三块一个的电子手表十分类似

功能描述

1 用四个数码管正常显示时、分高低位,实现正常显示时间。
2 具备调分调时功能
3 闹钟功能

功能并不复杂,我们现在来分析一下需要哪些模块。
首先是时钟功能,正常显示时间肯定是必须的,为实现这一可以设计一个60进制计数器和一个24进制计数器,当然也可以根据逻辑直接书写出来,但无论是什么办法,这肯定需要一个模块来实现它。

第二就是调分调时了,说白了就是置数,要置数,那么就必须有一个区域去控制数据,也需要一个地方存储数据,然后将置数的数据传给时钟,所以它应该与键盘的联系,内部有存储器。

第三是闹钟,闹钟不难想,比较器,我设定闹钟时间,然后与时钟的时间比较,如果两者相同,输出闹钟信号,就是如此。

最后的便是显示电路,主要是数码管的显示原理,驱动数码管显示时间。

就是这几样部分,貌似这么一说,确实没有什么东西,一个系统就是需要输入和输出相互协调好,这里面的逻辑必须是相互对应的,不出矛盾,个人认为,这是设计的难度所在。

整体设计图

image

模块讲解

键盘模块(key)

输入 功能说明 输出 功能说明
add_in c_add 时钟加
aub_in c_sub 时钟减
model_in 模式控制端 a_hour 调闹钟小时
clk 时钟 a_minute 调闹钟分钟
rst_n 复位 cnt_control 计数器开关
Display_Model 显示控制
Time_model 调时钟转换信号

细节讲解

model:模式的选择寄存器

整个闹钟系统我设置为五个模式,所以model需要是三位的[2:0]
00:时钟正常走时
01:时钟调分模式,该模式下时钟的计数器停止,时钟是不走的,同时显示模式也会转到调时钟模式。
10:时钟调时模式,与调分模式类似。
11:闹钟调分设置模式,此时时钟走时,显示模式为闹钟模式。

100:闹钟调时模式,与调分时类似。

cnt_control:计数器开关

正常走时和调闹钟模式下,计数器开,cnt_control = 0;
当进入调分和调时模式,计数器关闭,cnt_control = 1。

Time_Model:调时钟转换信号

这个是连接时钟模块(clock)的,是调分模式和调时模式的切换信号。

Display_Model:显示控制

正常走时,进入调分和调时模式时,停止走时,整个过程我设置为同一种显示模式;
闹钟模式下,显示模式转换;
所以一共是两种模式,一根线足以。

代码展示

module key(
    input clk,
    input rst_n,
    input add_in,//增加按钮
    input sub_in,//减去按钮
    input model_in,//模式选择
                
    output reg Display_Model, 
      //时钟与闹钟显示选择,0 代表时钟,1代表闹钟
    
    output reg cnt_control, //控制定时器
    output reg [2:0]Time_model,    
    output reg c_add,       //控制时钟 加
    output reg c_sub,       //控制时钟 减

    output reg a_add,       //控制闹钟 加
    output reg a_sub       //控制闹钟  减
            );
            
            
/************************************/
parameter T40MS = 20'd40_000;
parameter T1S = 30'd1_000_000;
/************************************/

///         add_in 按键      /////
reg add;
reg [19:0]cnt_a;
reg [1:0]a;
 always @(posedge clk or negedge rst_n)       
   begin
       if(!rst_n)
           begin
               cnt_a <= 0;
               a <= 0;
               add <= 1; 
           end
       else
       begin
           case(a) 
               0:begin
                   if(cnt_a < T40MS)
                     // 按下时间大于40MS 认为有效
                       begin
                           if(!add_in) 
                               cnt_a = cnt_a+1'b1;
                           else
                               cnt_a = 0;
                         end
                   else //counter> 40MS ,说明确实按键是按下了
                         begin 
                               add = 0; 
                           // 给冲击信号 ,0~1 是上升沿
                               a = 1;  
                           //确定按键按下,转到状态 1
                               cnt_a = 0;  //计数器清零
                         end
                     end
                1:begin
                       add = 1;    //产生尖脉冲
                       if(cnt_a < T40MS) 
                         // 按下时间大于40MS 松开有效
                           begin
                               if(add_in) 
                                       cnt_a = cnt_a+1'b1;
                               else
                                       cnt_a = 0; 
                             end
                        else
                            begin
                               a = 0; 
                    // 若松开,回到状态 0 ,等待下次按键到来
                               cnt_a = 0;
                            end
                   end
                   default : a = 1; 
           endcase
       end
   end
//////////////////////////////////////////////////////////

///        sub_in 按键       ///
reg sub;
reg [19:0]cnt_s;
reg [1:0]s;
 always @(posedge clk or negedge rst_n)       
   begin
       if(!rst_n)
           begin
               cnt_s <= 0;
               s <= 0;
               sub <= 1; 
           end
       else
       begin
           case(s) 
               0:begin
                   if(cnt_s < T40MS)
                     // 按下时间大于40MS 认为有效
                       begin
                           if(!sub_in) 
                               cnt_s = cnt_s+1'b1;
                           else
                               cnt_s = 0;
                         end
                   else //counter> 40MS ,说明确实按键是按下了
                         begin 
                               sub = 0; 
                           // 给冲击信号 ,0~1 是上升沿
                               s = 1; 
                           //确定按键按下,转到状态 1
                               cnt_s = 0;  //计数器清零
                         end
                     end
                1:begin
                       sub = 1;    //产生尖脉冲
                       if(cnt_s < T40MS) 
                         // 按下时间大于40MS 松开有效
                           begin
                               if(sub_in) 
                                       cnt_s = cnt_s+1'b1;
                               else
                                       cnt_s = 0; 
                             end
                        else
                            begin
                               s = 0; 
                         // 若松开,回到状态 0 ,等待下次按键到来
                               cnt_s = 0;
                            end
                   end
                   default : s = 1; 
           endcase
       end
   end
////////////////////////////////////////////////////////////
      
///        model_in 按键       ///
reg model;
reg [19:0]cnt_m;
reg [1:0]m;
 always @(posedge clk or negedge rst_n)       
   begin
       if(!rst_n)
           begin
               cnt_m <= 0;
               m <= 0;
               model <= 1; 
           end
       else
       begin
           case(m) 
               0:begin
                   if(cnt_m < T40MS) 
                     // 按下时间大于40MS 认为有效
                       begin
                           if(!model_in) 
                               cnt_m = cnt_m+1'b1;
                           else
                               cnt_m = 0;
                         end
                   else //counter> 40MS ,说明确实按键是按下了
                         begin 
                               model = 0; 
                           // 给冲击信号 ,0~1 是上升沿
                               m = 1;   
                           //确定按键按下,转到状态 1
                               cnt_m = 0;  //计数器清零
                         end
                     end
                1:begin
                       model = 1;    //产生尖脉冲
                       if(cnt_m < T40MS)
                         // 按下时间大于40MS 松开有效
                           begin
                               if(model_in) 
                                       cnt_m = cnt_m+1'b1;
                               else
                                       cnt_m = 0; 
                             end
                        else
                            begin
                               m = 0; 
                        // 若松开,回到状态 0 ,等待下次按键到来
                               cnt_m = 0;
                            end
                   end
                   default : m = 1; 
           endcase
       end
   end
////////////////////////////////////////////////////////////


/************************************************/
reg [2:0]type;
//00:时钟正常跑模式
//01:时钟调分模式,在该模式时间计数器停止计数
//10: 时钟调时模式,在该模式时间计数器停止计数
//11:闹钟调分模式,在该模式时间计数器正常计数
//100:闹钟调时模式,在该模式时间计数器正常计数
/************************************************/

always @(posedge clk or negedge rst_n)
    if(!rst_n) 
    begin
        Display_Model <= 1'b0;
        a_add <= 1'b0;
        a_sub <= 1'b0;
        c_add <= 1'b0;
        c_sub <= 1'b0;
        Time_model <= 3'b000;
        type <= 3'b000;
        cnt_control <= 1'b1;//启动计数
    end
    else
    begin
         if(!model) 
            begin
                if(type == 3'b100)
                    type = 3'b000;
                else
                    begin
                        type = type + 1'b1;
                    end
            end
        case(type)
        //时钟正常开始跑
            3'b000:
            begin
                Time_model <= 3'b000; 
                cnt_control <= 1'b1;//启动计数
                Display_Model <= 1'b0;
                a_add <= 1'b0;
                a_sub <= 1'b0;
                c_add <= 1'b0;
                c_sub <= 1'b0;
            end
            //调分模式            
            3'b001:
            begin 
            cnt_control <= 1'b0; //关闭计数
            Time_model <= 3'b001;
            Display_Model <= 1'b0;
                if(!add)//加
                    begin
                        c_add <=1'b1 ;
                    end
                else
                    begin
                        c_add <= 1'b0;
                    end
                    
                 if(!sub)//减
                    begin
                        c_sub <= 1'b1;
                    end
                 else
                    begin
                        c_sub <= 1'b0;
                    end
               end
            //调时模式
            3'b010:
            begin 
             cnt_control <= 1'b0;//关闭计数
             Time_model <= 2'b010;
             Display_Model <= 1'b0;
                if(!add)//加
                    begin
                        c_add <=1'b1 ;
                    end
                else
                    begin
                        c_add <= 1'b0;
                    end
                 if(!sub)//减
                    begin
                        c_sub <= 1'b1;
                    end
                 else
                    begin
                        c_sub <= 1'b0;
                    end
               end

           //调分模式            
                       3'b011:
                       begin 
                       cnt_control <= 1'b1; //kaijishu
                       Time_model <= 3'b011;
                       Display_Model <= 1'b1;
                           if(!add)//加
                               begin
                                   a_add <=1'b1 ;
                               end
                           else
                               begin
                                   a_add <= 1'b0;
                               end
                            if(!sub)//减
                               begin
                                   a_sub <= 1'b1;
                               end
                            else
                               begin
                                   a_sub <= 1'b0;
                               end
                          end
           
                       //调时模式
                       3'b100:
                       begin 
                        cnt_control <= 1'b1;//关闭计数
                        Time_model <= 3'b100;
                        Display_Model <= 1'b1;
                        
                           if(!add)//加
                               begin
                                   a_add <=1'b1 ;
                               end
                           else
                               begin
                                   a_add <= 1'b0;
                               end
                               
                            if(!sub)//减
                               begin
                                   a_sub <= 1'b1;
                               end
                            else
                               begin
                                   a_sub <= 1'b0;
                               end
                          end           
         
           default:type <= 3'b000;
        endcase
   end
endmodule

时钟模块(clock)

输入 功能说明 输出 功能说明
c_add hour_h 小时高位
c_sub hour_l 小时低位
Time_model 模式控制端 minute_h 分钟高位
cot_control 计数器开关 minute_l 分钟低位
clk 时钟
rst_n 复位

细节讲解

时钟模块的输入都是来自键盘模块,所以输入不多说了,提一点,我的板子上的输入的时钟(clk),用的是50MHz晶振,而时钟是1Hz,也就是一秒走一下,所以内部必须分频,代码里注意看一下,我为了方便调试,分频并不是分频到1Hz,如果你有需要用到,修改里面的参数就好。

模块输出

模块的输出就是时钟的数据,是传给显示模块去驱动数码管显示的。
小时高位,0~2,三种情况,两根线[1:0]
小时低位,0~0,十种情况,四根线[3:0]
分钟高位,0~5,六种情况,三根线[2:0]
分钟低位,0~9,十种情况,四根线[3:0]

代码展示

module clock(
        input  clk, 
        input  rst_n,
        input  cnt_control, //控制定时器 ,1启动 ,0停止
        input  c_add,       //控制 加
        input  c_sub,       //控制 减
        input  [2:0]Time_model,
        
        output  [1:0]hour_h,     //小时 十位
        output  [3:0]hour_l,     //小时 个位
        output  [2:0]minute_h,   //分钟 十位
        output  [3:0]minute_l   //分钟 个位
    );
    parameter  S= 100000000;
    parameter  M=60;
    /*********************************************************/
    //1S计数器
    reg [31:0] cnt1s;
    always @(posedge clk or negedge rst_n)
        if(!rst_n) 
            cnt1s <= 15'd0;
        else if((cnt1s == S) || (!cnt_control)) 
                cnt1s <= 15'd0;
            else if(cnt_control)
                cnt1s <= cnt1s + 1'b1; 
                
    /*********************************************************/
    
    ///////////////////////////////////////////////////////////
 //                        功能控制                        //
 //////////////////////////////////////////////////////////
    reg [1:0]flag;//用来标志reg1是否到了2,到了2,reg2只能加到4
    reg [1:0] reg1;//时的第一位:0~2
    reg [3:0] reg2;
  //时的第二位:当第一位为0和1时,可以是0~9,当第一位为2时,只能是0~9,
    reg [2:0] reg3;//分的第一位:只能是0~5
    reg [3:0] reg4;//分的第二位:是0~9
    
    always @(posedge clk or negedge rst_n)
    begin
        if(!rst_n)
         begin
            reg1 <= 2'd0; //小时 高位
            reg2 <= 4'd0; //小时 低位
            reg3 <= 3'd0; //分钟 高位
            reg4 <= 4'd0; //分钟 低位
            flag <= 2'd0;
        end
        else
            case(Time_model)
            //时钟正常开始跑
                3'b000:
                begin    
                    if(cnt1s == S)  //一分钟到了
                    begin
                        reg4 <= reg4 + 1'b1;    
                        if(reg4 == 4'd9)        
                        begin
                            reg4 <= 4'd0;
                            reg3 <= reg3 + 1'b1;
                            
                            if(reg3 == 3'd5)    
                            begin
                                reg3 <= 3'd0;        
                                if(reg1 == 2'd2)    
                                    begin
                                        reg2 <= reg2 + 1'b1;
                                        if(reg2 == 4'd3)
                                        begin
                                            reg2 <= 4'd0;                
                                            reg1 <= 2'd0;                            
                                        end
                                    end 
                                else 
                                    begin
                                        reg2 <= reg2 + 1'b1;
                                        if(reg2 == 4'd9)
                                        begin
                                        reg2 <= 4'd0;
                                        reg1 <= reg1 + 1'b1;
                                        end
                                    end
                             end
                            
                        end
                    end
                end
                
                //调分模式            
                3'b001:
                begin 
                    if(c_add)//加
                    begin
                        reg4 <= reg4 + 1'b1;
                        if(reg4 == 4'd9)
                        begin
                            reg4 <= 4'd0;
                            reg3 <= reg3 + 1'b1;
                            if(reg3 > 3'd5)
                                reg3 <= 3'd0;
                        end
                    end
                    else if(c_sub)//减
                        begin
                            reg4 <= reg4 - 1'b1;
                            if(reg4 == 4'd0)
                                begin
                                    reg4 <= 4'd9;
                                    reg3 <= reg3 - 3'd1;
                                    if(reg3 == 3'd0)
                                        reg3 <= 3'd5;
                                end
                        end
                end  
                //调时模式
                3'b010: 
                begin
                    if(c_add)//加
                    begin
                        if(flag == 2'd2)
                        begin
                            reg2 <= reg2 + 1'b1;
                            if(reg2 >= 4'd3)
                            begin
                                reg2 <= 4'd0;
                                reg1 <= 2'd0;
                                flag <= 2'd0;
                            end
                        end
                        else
                        begin
                            reg2 <= reg2 + 1'b1;
                            if(reg2 == 4'd9)
                            begin
                                flag <= flag + 1'b1;
                                reg2 <= 4'd0;
                                reg1 <= reg1 + 1'b1;         
                            end
                        end
                    end
                    else if(c_sub)//减
                        begin
                             if(flag == 2'd0)
                             begin
                                 reg2 <= reg2 - 1'b1;
                                 if(reg2 == 4'd0)
                                 begin
                                     reg2 <= 4'd3;
                                     reg1 <= 2'd2;
                                     flag <= 2'd2;
                                 end
                             end
                             else
                             begin
                                  reg2 <= reg2 - 1'b1;
                                  if(reg2 == 4'd0)
                                  begin
                                      flag <= flag - 1'b1;
                                      reg1 <= reg1 - 1'b1;   
                                      reg2 <= 4'd9;        
                             end
                       end
                  end
                end    
            endcase
     end       
    /************************************************/
    assign hour_h = reg1;
    assign hour_l = reg2;
    assign minute_h = reg3;
    assign minute_l = reg4;
    /************************************************/
endmodule

闹钟模块(alarm)

输入 功能说明 输出 功能说明
a_hour 闹钟小时控制 a_hour_h 闹钟小时高位
a_minute 闹钟分钟控制 a_hour_l 闹钟小时低位
clk 时钟 a_minute_h 闹钟分钟高位
rst_n 复位 a_minute_l 闹钟分钟低位

细节讲解

闹钟模块与时钟模块的其实没什么区别,就是在调闹钟时,时钟正常运行,时钟调分,调时都会停止运行,也就是计数器不计数,其他的区别不大。

代码展示


module alarm(
        input  clk, 
        input  rst_n,
        input  c_add,       //控制 加
        input  c_sub,       //控制 减
        input  [2:0]Time_model,

        output  [1:0]a_hour_h,     //闹钟小时 十位
        output  [3:0]a_hour_l,     //闹钟小时 个位
        output  [2:0]a_minute_h,   //闹钟分钟 十位
        output  [3:0]a_minute_l    //闹钟分钟 个位
    );
    //////////////////////////////////////////////////////////
 //                        功能控制                       //
 ////////////////////////////////////////////////////////
    reg [1:0]flag;//用来标志reg1是否到了2,到了2,reg2只能加到4
    reg [1:0] reg1;//时的第一位:0~2
    reg [3:0] reg2;
  //时的第二位:当第一位为0和1时,可以是0~9,当第一位为2时,只能是0~9,
    reg [2:0] reg3;//分的第一位:只能是0~5
    reg [3:0] reg4;//分的第二位:是0~9
    
    always @(posedge clk or negedge rst_n)
    begin
        if(!rst_n)
         begin
            reg1 <= 2'd0; //闹钟小时 高位
            reg2 <= 4'd0; //闹钟小时 低位
            reg3 <= 3'd0; //闹钟分钟 高位
            reg4 <= 4'd0; //闹钟分钟 低位
            flag <= 2'd0;
        end
        else
            case(Time_model)
                //闹钟调分模式            
                3'b011:
                begin 
                    if(c_add)//加
                    begin
                        reg4 <= reg4 + 1'b1;
                        if(reg4 == 4'd9)
                        begin
                            reg4 <= 4'd0;
                            reg3 <= reg3 + 1'b1;
                            if(reg3 > 3'd4)
                                reg3 <= 3'd0;
                        end
                    end
                    else if(c_sub)//减
                        begin
                            reg4 <= reg4 - 1'b1;
                            if(reg4 == 4'd0)
                                begin
                                    reg4 <= 4'd9;
                                    reg3 <= reg3 - 3'd1;
                                    if(reg3 == 3'd0)
                                        reg3 <= 3'd5;
                                end
                        end
                end  
                //闹钟调时模式
                3'b100: 
                begin
                    if(c_add)//加
                    begin
                        if(flag == 2'd2)
                        begin
                            reg2 <= reg2 + 1'b1;
                            if(reg2 >= 4'd3)
                            begin
                                reg2 <= 4'd0;
                                reg1 <= 2'd0;
                                flag <= 2'd0;
                            end
                        end
                        else
                        begin
                            reg2 <= reg2 + 1'b1;
                            if(reg2 == 4'd9)
                            begin
                                flag <= flag + 1'b1;
                                reg2 <= 4'd0;
                                reg1 <= reg1 + 1'b1;
                                
                            end
                        end
                    end
                    else if(c_sub)//减
                        begin
                             if(flag == 2'd0)
                             begin
                                 reg2 <= reg2 - 1'b1;
                                 if(reg2 == 4'd0)
                                 begin
                                     reg2 <= 4'd3;
                                     reg1 <= 2'd2;
                                     flag <= 2'd2;
                                 end
                             end
                             else
                             begin
                                  reg2 <= reg2 - 1'b1;
                                  if(reg2 == 4'd0)
                                  begin
                                      flag <= flag - 1'b1;
                                      reg1 <= reg1 - 1'b1;                              
                                      reg2 <= 4'd9;                                    
                             end
                       end
                  end
                end    
            endcase
     end       
    /************************************************/
    
    assign a_hour_h = reg1;
    assign a_hour_l = reg2;
    assign a_minute_h = reg3;
    assign a_minute_l = reg4;
    
    /************************************************/

endmodule

显示模块(display)

输入 功能说明 输出 功能说明
a_hour_h 闹钟小时高位 data 段选信号
a_hour_l 闹钟小时低位 select_wei 位选信号
a_minute_h 闹钟分钟高位 beep 蜂鸣器
a_minute_l 闹钟分钟低位
hour_h 时钟小时高位
hour_l 时钟小时低位
minute_h 时钟分钟高位
minute_l 时钟分钟低位
Display_Model 显示模式控制
clk 时钟
rst_n 复位

显示模块的输入有三种类型:

  1. 闹钟的数据
  2. 时钟的数据
  3. 显示模式控制信号

输出也只有三个

  1. data是八段数码管的段选信号,由于我没用小数点,所以七根线
  2. select_wei数码管位选,四个数码管,四根线
  3. beep链接蜂鸣器的,一根线。

细节讲解

显示模块里用到的主要是数码管动态扫描的知识,扫描频率如何设置,这些问题,查一下数码管的是如何显示就知道了,抓住段选和位选,还有动态扫描的原理,然后分清楚你使用的数码管是共阴还是共阳,这个显示模块就很好理解了。

代码展示

module  display(
            input clk,
            input rst_n,
            
            //时钟模式 双段选数据
            input [1:0] hour_h,
            input [3:0] hour_l,
            input [2:0] minute_h,
            input [3:0] minute_l,
            
            //闹钟模式 段选数据
            input [1:0] a_hour_h,
            input [3:0] a_hour_l,
            input [2:0] a_minute_h,
            input [3:0] a_minute_l,
            
            input Display_Model,//0:时钟模式,1:秒表模式
            
            output [6:0] data,//数码管段选
            output reg[3:0] select_wei, //数码管位选
            output reg alarm_out
                        );

/***********************************/
parameter     
            SEG_0     = 7'h7e,//c0,
            SEG_1     = 7'h30,//f9,
            SEG_2     = 7'h6d,//a4,
            SEG_3     = 7'h79,//b0,
            SEG_4     = 7'h33,//99,
            SEG_5     = 7'h5b,//92,
            SEG_6     = 7'h5f,//82,
            SEG_7     = 7'h70,//F8,
            SEG_8     = 7'h7f,//80,
            SEG_9     = 7'h7b;//90,
        
            /***********************************/
                        //时钟数据编码        
            /***********************************/
wire [6:0]c_data1;
wire [6:0]c_data2;
wire [6:0]c_data3;
wire [6:0]c_data4;

//数码管一要显示的列表数据(0~2)
reg [6:0] data1_temp;
always @(posedge clk or negedge rst_n)
    if(!rst_n)
        data1_temp <= SEG_0;
    else
        case(hour_h)
            2'd0:    data1_temp <= SEG_0;
            2'd1:    data1_temp <= SEG_1;
            2'd2:    data1_temp <= SEG_2;
            default: data1_temp <= SEG_0;
        endcase
/***********************************/
//数码管二要显示的列表数据(0~9)
reg [6:0] data2_temp;
always @(posedge clk or negedge rst_n)
    if(!rst_n)
        data2_temp <= SEG_0;
    else
        case(hour_l)
            4'd0:    data2_temp <= SEG_0;
            4'd1:    data2_temp <= SEG_1;
            4'd2:    data2_temp <= SEG_2;
            4'd3:    data2_temp <= SEG_3;
            4'd4:    data2_temp <= SEG_4;
            4'd5:    data2_temp <= SEG_5;
            4'd6:    data2_temp <= SEG_6;
            4'd7:    data2_temp <= SEG_7;
            4'd8:    data2_temp <= SEG_8;
            4'd9:    data2_temp <= SEG_9;
            default: data2_temp <= SEG_0;
        endcase
/***********************************/
//数码管三要显示的列表数据 (0~5)
reg [6:0] data3_temp;
always @(posedge clk or negedge rst_n)
    if(!rst_n)
        data3_temp <= SEG_0;
    else
        case(minute_h)
            3'd0:    data3_temp <= SEG_0;
            3'd1:    data3_temp <= SEG_1;
            3'd2:    data3_temp <= SEG_2;
            3'd3:    data3_temp <= SEG_3;
            3'd4:    data3_temp <= SEG_4;
            3'd5:    data3_temp <= SEG_5;
            default: data3_temp <= SEG_0;
        endcase
/***********************************/
//数码管四要显示的列表数据(1~9)
reg [6:0] data4_temp;
always @(posedge clk or negedge rst_n)
    if(!rst_n)
        data4_temp <= SEG_0;
    else
        case(minute_l)
            4'd0:    data4_temp <= SEG_0;
            4'd1:    data4_temp <= SEG_1;
            4'd2:    data4_temp <= SEG_2;
            4'd3:    data4_temp <= SEG_3;
            4'd4:    data4_temp <= SEG_4;
            4'd5:    data4_temp <= SEG_5;
            4'd6:    data4_temp <= SEG_6;
            4'd7:    data4_temp <= SEG_7;
            4'd8:    data4_temp <= SEG_8;
            4'd9:    data4_temp <= SEG_9;
            default: data4_temp <= SEG_0;
        endcase
/*****************************************/
assign c_data1 = data1_temp;
assign c_data2 = data2_temp;
assign c_data3 = data3_temp;
assign c_data4 = data4_temp;
/*****************************************/
        
            /***********************************/
                        //闹钟数据编码        
            /***********************************/
wire [6:0]a_data1;
wire [6:0]a_data2;
wire [6:0]a_data3;
wire [6:0]a_data4;
//数码管一要显示的列表数据(0~5)
reg [6:0] a_data1_temp;
always @(posedge clk or negedge rst_n)
    if(!rst_n) 
        a_data1_temp <= SEG_0;
    else  
        case(a_hour_h)
            3'd0:    a_data1_temp <= SEG_0;
            3'd1:    a_data1_temp <= SEG_1;
            3'd2:    a_data1_temp <= SEG_2;
            3'd3:    a_data1_temp <= SEG_3;
            3'd4:    a_data1_temp <= SEG_4;
            3'd5:    a_data1_temp <= SEG_5;
            default: a_data1_temp <= SEG_0;
        endcase
/***********************************/
//数码管二要显示的列表数据(0~9)
reg [6:0] a_data2_temp;
always @(posedge clk or negedge rst_n)
    if(!rst_n) 
        a_data2_temp <= SEG_0;
    else
        case(a_hour_l)
            4'd0:    a_data2_temp <= SEG_0;
            4'd1:    a_data2_temp <= SEG_1;
            4'd2:    a_data2_temp <= SEG_2;
            4'd3:    a_data2_temp <= SEG_3;
            4'd4:    a_data2_temp <= SEG_4;
            4'd5:    a_data2_temp <= SEG_5;
            4'd6:    a_data2_temp <= SEG_6;
            4'd7:    a_data2_temp <= SEG_7;
            4'd8:    a_data2_temp <= SEG_8;
            4'd9:    a_data2_temp <= SEG_9;
            default: a_data2_temp <= SEG_0;
        endcase
/*****************************************/
//数码管三要显示的列表数据(0~9)
reg [6:0] a_data3_temp;        
always @(posedge clk or negedge rst_n)
    if(!rst_n) 
        a_data3_temp <= SEG_0;
    else 
        case(a_minute_h)
            4'd0:    a_data3_temp <= SEG_0;
            4'd1:    a_data3_temp <= SEG_1;
            4'd2:    a_data3_temp <= SEG_2;
            4'd3:    a_data3_temp <= SEG_3;
            4'd4:    a_data3_temp <= SEG_4;
            4'd5:    a_data3_temp <= SEG_5;
            4'd6:    a_data3_temp <= SEG_6;
            4'd7:    a_data3_temp <= SEG_7;
            4'd8:    a_data3_temp <= SEG_8;
            4'd9:    a_data3_temp <= SEG_9;
            default: a_data3_temp <= SEG_0;
        endcase
/***********************************/
//数码管四要显示的列表数据(0~9)
reg [6:0] a_data4_temp;
always @(posedge clk or negedge rst_n)
    if(!rst_n) 
        a_data4_temp <= SEG_0;
    else
        case(a_minute_l)
            4'd0:    a_data4_temp <= SEG_0;
            4'd1:    a_data4_temp <= SEG_1;
            4'd2:    a_data4_temp <= SEG_2;
            4'd3:    a_data4_temp <= SEG_3;
            4'd4:    a_data4_temp <= SEG_4;
            4'd5:    a_data4_temp <= SEG_5;
            4'd6:    a_data4_temp <= SEG_6;
            4'd7:    a_data4_temp <= SEG_7;
            4'd8:    a_data4_temp <= SEG_8;
            4'd9:    a_data4_temp <= SEG_9;
            default: a_data4_temp <= SEG_0;
        endcase
/*******************************************************/    
assign a_data1 = a_data1_temp;
assign a_data2 = a_data2_temp;
assign a_data3 = a_data3_temp;
assign a_data4 = a_data4_temp;
/***************************************************/

/******************************************/
parameter shuaxin = 17'h1ffff;
// 数码管扫描频率
reg [19:0] cnt;    
reg [1:0] num;//每隔5MS,num加1
always @(posedge clk or negedge rst_n)
    if(!rst_n) 
    begin
        select_wei <= 4'd8; // 1000
        cnt <= 18'd0;
        num <= 2'd0;
    end
    else if(cnt == shuaxin) 
            begin
                num <= num + 1'b1;
                cnt <= 18'd0;
                if(select_wei == 4'd1) //0001
                     select_wei <= 4'd8;
                 else
              select_wei <= {1'b0,select_wei[3:1]};   //右移
            end
         else
              cnt <= cnt + 1'b1;
/******************************************/
//通过Display_Model来确定是要送秒表数据还是闹钟的数据
reg [7:0] data_temp;
always @(posedge clk or negedge rst_n)
begin
    if(!rst_n)
        data_temp <= 7'h7e;
    else if(cnt == shuaxin) 
        case(num)
            2'd0:    data_temp <= Display_Model ? a_data1:c_data1;//给第一个数码管送数据
            2'd1:    data_temp <= Display_Model ? a_data2:c_data2;//给第二个数码管送数据
            2'd2:    data_temp <= Display_Model ? a_data3:c_data3;//给第二个数码管送数据
            2'd3:    data_temp <= Display_Model ? a_data4:c_data4;//给第二个数码管送数据
        endcase
end
assign data = data_temp;
/******************************************/   

  //////        比较器模块             //////
always @(posedge clk or negedge rst_n)
begin
    if(!rst_n)
        alarm_out <= 0 ;
    else if(a_data1 == c_data1 && a_data2 == c_data2 && a_data3 == c_data3 && a_data4 == c_data4)
            alarm_out <=1;
            else
            alarm_out<=0;
end
endmodule

top层

主要是链接,直接上代码吧。

代码展示


module top(
            input clk,
            input rst_n,
           
            input add_in,
            input sub_in,
            input model_in,
            
            output [6:0] data,//数码管段选数据
            output [3:0] select_wei, //数码管位选
            output  alarm_out
            
            
           );

wire [1:0] hour_h;
wire [3:0] hour_l;
wire [2:0] minute_h;
wire [3:0] minute_l;

wire [1:0] a_hour_h;
wire [3:0] a_hour_l;
wire [2:0] a_minute_h;
wire [3:0] a_minute_l;

wire Display_Model;
wire [2:0]Time_model;

wire c_add;
wire c_sub;

wire a_add; 
wire a_sub;
wire cnt_control; //控制定时器


//
key       U1(
        //top的input
          .clk(clk),
          .rst_n(rst_n), 
          
          .add_in(add_in),
          .sub_in(sub_in),
          .model_in(model_in),
        
        //连clock的线
          .cnt_control(cnt_control),
          .c_add(c_add),
          .c_sub(c_sub),
          .Time_model(Time_model),
          
        //连alarm的线
           .a_add(a_add),
           .a_sub(a_sub),
        
        //连display的线
           .Display_Model(Display_Model)        
           );
                   
clock       U2(
        //input
              .clk(clk),
              .rst_n(rst_n),
              
        //从key来的线
              .c_add(c_add),
              .c_sub(c_sub),
              .cnt_control(cnt_control),
              .Time_model(Time_model),
         
        //output     
        //连display的线
        
              .hour_h(hour_h),
              .hour_l(hour_l),
              .minute_h(minute_h),
              .minute_l(minute_l)
                      
            );
alarm           U3(
                    //input
                          .clk(clk),
                          .rst_n(rst_n),
                          
                    //从key来的线
                          .c_add(a_add),
                          .c_sub(a_sub),              
                          .Time_model(Time_model),
                     
                    //output     
                    //连display的线
                    
                          .a_hour_h(a_hour_h),
                          .a_hour_l(a_hour_l),
                          .a_minute_h(a_minute_h),
                          .a_minute_l(a_minute_l)
                                  
                        );
    

                    
display             U4(
                        //input 
                        .clk(clk),
                        .rst_n(rst_n),
                        
                        //从clock 来的线
                        .hour_h(hour_h),
                        .hour_l(hour_l),
                        .minute_h(minute_h),
                        .minute_l(minute_l),
                        
                        //从alarm 来的线
                        .a_hour_h(a_hour_h),
                        .a_hour_l(a_hour_l),
                        .a_minute_h(a_minute_h),
                        .a_minute_l(a_minute_l),
                       
                        //从key 来的线
                        .Display_Model(Display_Model),
                        
                        //output 
                        .data(data),
                        .select_wei(select_wei),
                        .alarm_out(alarm_out)
                        );
                 
endmodule

细节讲解

top层的作用是连接各个模块,列出系统的输入输出,然后分清楚中间节点,如果之前没有写过top层,可以先从试着写个小系统,比如:全加器(调用半加器实现),如果你已经是老司机,相信已经驾轻就熟了,不多说了。其实,我这个例化写得有点烦了,例化如果非要分写法可以分为两种,我这是比较烦的,不过我也不改了,╭(╯^╰)╮

写在后面的话

数字钟就讲诉到这里,整个系统不难理解,只是模块之间的联系必须弄清楚,我的观点是:把需要的模块列出来,分析模块之间的联系,再具体看各个模块的内容,单个验证,自顶向下设计,主要还是多琢磨吧,有耐心就好。

关于文章,如果有任何问题都可以在评论区和我交流,如果有错误,欢迎斧正,谢谢了~~

你可能感兴趣的:(FPGA-数字闹钟)