美文网首页
FPGA-数字闹钟

FPGA-数字闹钟

作者: 十字街86号 | 来源:发表于2021-11-13 14:00 被阅读0次

    用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
    

    </div>

    时钟模块(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 闹钟分钟低位

    细节讲解

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

    代码展示

    <button class="testButton" onclick="document.all.child3.style.display=(document.all.child3.style.display =='none')?'':'none'">点击显\隐代码</button>
    <div id="child3" style="display:">

    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
    
    

    </div>

    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-数字闹钟

          本文链接:https://www.haomeiwen.com/subject/utfzzltx.html