基于fpga的数字时钟开发

1、题目要求

在这里插入图片描述

2、程序代码

module clock(
    input clk,    //时钟
    input rst,    //复位键
    input  wire  month_adj ,
    input  wire  day_adj  ,
    input [0:4] key,    //按键输入
    input [0:0] qiehuan,
    output [0:7] seg_cs,   //数码管位选
    output [0:7] seg_data0,    //前四个数码管
    output [0:7] led,
    output [0:7] seg_data1    //后四个数码管
    );
    reg [3:0] day_l = 0;
    reg [3:0] day_h = 0;
    reg [3:0] month_l = 0 ;
    reg [3:0] month_h = 0 ;
    reg [0:26] cnt_1s = 0;    //计时1s
    reg [0:20] cnt_xd = 0;    //消抖计时
    reg [0:25] cnt_s = 0;     //闪烁计时
    reg [0:1] key_out;    //输出按下哪一位按键
    reg [0:17] cnt;     //扫描计时
    reg [0:3] count = 0;   //用于移位的计数
    reg [0:7] wz;    //保存当前数码管位置
    reg [0:2] cnt_wz;     //决定哪一位数码管亮
    reg [0:3] num = 0;   //用于段码输送
    reg [0:7] smg;    //保存输送的段码
    reg [0:1] who = 0;    //决定调整哪一个时间单元
    reg [0:7] light;    //保存LED的值
    reg [7:0] s = 0;   //记录小时
    reg [7:0] f = 0;   //记录分钟
    reg [7:0] m = 0;   //记录秒
    reg [7:0] cd = 0;  //传递变量
    reg [3:0] gw = 0;  //个位
    reg [3:0] sw = 0;  //十位
    reg [17:0] shift_reg = 0;    //移位寄存器
    reg DIR = 1'b0;    //分频计
    reg state = 1'b0;  //时钟状态
    reg shine = 1'b0;  //暗灭状态
    parameter xd = 21'd2000000;    //计时_20ms
    parameter   CNT_1S_MAX = 26'd50_000_000      ;//The frequency is 1Hz,and the period is 1s.
    reg [25:0]  cnt_11s;
    wire   cnt_1s_flag ;
    reg     clk_1hz  ;
    assign cnt_1s_flag = (cnt_11s==(CNT_1S_MAX-1'b1));
    
    always @ (posedge clk) begin
    if(cnt_1s_flag) begin
        cnt_11s<=26'd0;
        clk_1hz<=~clk_1hz;
    end
    else begin
        cnt_11s<=cnt_11s+1'b1;
        clk_1hz<=clk_1hz;//It's best not to omit.
    end
end

    always @ (posedge clk_1hz or negedge rst ) begin
    if(!rst) begin
        day_h<=4'd0;
        day_l<=4'd0;
        month_h<=4'd0;
        month_l<=4'd0;
    end
    else begin
        if(month_adj) begin//While month_adj=1,adjust month.
            if(month_l == 4'd9) begin
                month_l<=4'd0;
                month_h<=month_h+1'b1;
            end
            else if((month_h==4'd1) && (month_l==4'd2))begin
                month_l<=4'd0;
                month_h<=4'd0;
            end
            else begin
                month_l<=month_l+1'b1;
                month_h<=month_h;//It's best not to omit.
            end
        end
        else if(!day_adj) begin //While day_adj=1,adjust day.
            if(day_l==4'd9) begin
                day_l<=4'd0;
                if(day_h==4'd2)
                    day_h<=4'd0;
                else
                    day_h<=day_h+1'b1;
            end
            else begin
                day_l<=day_l+1'b1;
                day_h<=day_h;
                day_l <= day_l;
            end
        end
        else if(day_l==4'd9) begin
                    day_l<=4'd0;
                    if(day_h==4'd3) begin
                        day_h<=4'd0;
                        if(month_l==4'd9)begin
                            month_l<=4'd0;
                            month_h<=month_h+1'b1;
                        end
                        else if((month_h==4'd1) && (month_l==4'd2)) begin
                            month_l<=4'd0;
                            month_h<=4'd0;
//                            if(day==4'd7)
//                                day<=4'd1;
                        end
                       // else
                            month_l <= month_l + 1;
                    end
                    else
                        day_h<=day_h+1'b1;
                     end
                else
                    day_l<=day_l+1'b1;
            end
        end 


    always@(posedge clk) begin    //消抖计时
    if(key == 5'b00000)    //抖动即重新开始
        cnt_xd <= 0;
    else if(cnt_xd == xd)
        cnt_xd <= xd;
    else
        cnt_xd <= cnt_xd + 1;
    end

    always@(posedge clk) begin
    if(cnt_xd == 0)
        key_out <= 0;
    else if(cnt_xd == (xd - 21'b1))    //产生1个时间单位的按键信号
        case(key)     //根据键入得到对应的值
        5'b10000: key_out <= 1;
        5'b00100: key_out <= 2;
        5'b00010: key_out <= 3;
        endcase
    else
        key_out <= 0;   //0表示无按键按下
    end

    always@(posedge clk) begin
    if(key_out == 1)
        state <= ~state;    //状态变换,0表示时钟运行,1表示时间调整
    else
        state <= state;   
    end

    always@(posedge clk) begin
    if(state == 1)
    begin
        if(key_out == 2)
        begin
            who = who + 1;
            if(who == 3)
                who = 0;
            else
                who = who;
        end
    end
    else
        who = 0;   
    end


    always@(posedge clk) begin   //数码管扫描计时
    if(cnt == 18'd200000)  //500Hz
    begin
        cnt <= 0;
        DIR <= 1'b1;   //产生一个上升沿
    end
    else
    begin
        cnt <= cnt +1;
        DIR <= 1'b0;
    end   
    end

    always@(posedge DIR) begin
    if(cnt_wz == 7)
        cnt_wz <= 0;
    else
        cnt_wz <= cnt_wz + 1;  //cnt_wz表示不同数码管的状态
    end

    always@(posedge clk) begin   //根据cd的值来计算不同类型的时间
    case(cnt_wz)
    3'd0: cd <= s;
    3'd3: cd <= f;
    3'd6: cd <= m;
    default: cd <= cd;
    endcase      
    end

    always@(posedge clk) begin    //计时1s
    if(state == 0)
    begin
        if(cnt_1s == 27'd100000000)   //若感觉计时非1s,可以修改这里的数值
            cnt_1s <= 0;
        else
            cnt_1s <= cnt_1s + 1;
    end
    else
        cnt_1s <= 0;
    end

    always@(posedge clk) begin    //控制数码管的闪烁
    if(state == 1)
    begin
        if(cnt_s == 26'd50000000)   //0.5s变换
        begin
            cnt_s <= 0;
            shine <= ~shine;     //暗灭转换
        end
        else
            cnt_s <= cnt_s + 1;
    end
    else
        cnt_s <= 0;  
    end

    always@(posedge clk) begin   //时间计算逻辑单元
    if(!rst)
        m = 0;
    else if(state == 0)
        if(cnt_1s == 27'd100000000)
            m = m +1;   //秒计时
        else if(m == 60)   //若秒计时到60
        begin
            f = f + 1;   //分+1
            m = 0;
            if(f == 60)   //若分到60
            begin
                s = s + 1;   //小时+1
                f = 0;
                if(s == 24)   //若时到24
                    s = 0;    //小时归零
                else
                    s = s;
            end
            else
                f = f;
        end
        else
            m = m;
    else
    begin
        if(key_out == 3)    //进入时间调制状态
            case(who)
            2'd0: begin
                s = s + 1;
                if(s == 24)
                    s = 0; 
                else
                    s = s;
                end
            2'd1: begin
                f = f + 1;
                if(f == 60)
                    f = 0; 
                else
                    f = f;
                end
            2'd2: begin
                m = m + 1;
                if(m == 60)
                    m = 0; 
                else
                    m = m;
                end
            default: m = m;
            endcase
        else
        begin
            s = s;
            f = f;
            m = m;
        end
    end
    end

    always@(posedge clk)   //用来控制移位
    begin
        if(count == 9)    //由于这里原码为8bit,故只需要移位10位就可以得到结果
            count <= 0;
        else
            count <= count + 1;                 
    end

    always@(posedge clk) begin   //下面程序完成二进制码到BCD码的变换
    if(count == 0)
        shift_reg={10'b0000000000,cd};
    else if (count<=8) 
    begin
        if(shift_reg[11:8] >= 5)   //大于等于5就加3,下面同理
        begin
            if(shift_reg[15:12] >= 5)
                begin
                shift_reg[15:12]=shift_reg[15:12]+2'b11;    
                shift_reg[11:8]=shift_reg[11:8]+2'b11;
                shift_reg = shift_reg<<1;
                end 
             else
             begin
                shift_reg[15:12]=shift_reg[15:12];
                shift_reg[11:8]=shift_reg[11:8]+2'b11;
                shift_reg=shift_reg<<1;
             end
         end
         else
         begin
              if(shift_reg[15:12] >= 5)
                  begin
                  shift_reg[15:12]=shift_reg[15:12]+2'b11;
                  shift_reg[11:8]=shift_reg[11:8];
                  shift_reg=shift_reg<<1;
                  end
              else
                begin
                 shift_reg[15:12]=shift_reg[15:12];
                 shift_reg[11:8]=shift_reg[11:8];
                 shift_reg=shift_reg<<1;
                 end
          end
       end
    end

    always@(posedge clk) begin
    if(count==9)  //移位结束 
    begin
        gw <= shift_reg[11:8];    //个位
        sw <= shift_reg[15:12];   //十位
    end
    else
    begin
        gw <= gw;
        sw <= sw;
    end 
    end

    always@(posedge clk) begin
    case(cnt_wz)   //决定哪一位数码管亮
        3'd0: begin
        if(shine == 0)   //shine为0表示亮
            wz <= 8'b10000000;
        else if(who == 0)   //shine为1且为当前时间类型,灭
            wz <= 8'b00000000;
        else    //不为当前时间类型,仍保持亮
            wz <= 8'b10000000;
        end
        3'd1: begin
        if(shine == 0)
            wz <= 8'b01000000;
        else if(who == 0)
            wz <= 8'b00000000;
        else
            wz <= 8'b01000000;
        end
        3'd2: wz <= 8'b00100000;
        3'd3: begin
        if(shine == 0)
            wz <= 8'b00010000;
        else if(who == 1)
            wz <= 8'b00000000;
        else
            wz <= 8'b00010000;
        end
        3'd4: begin
        if(shine == 0)
            wz <= 8'b00001000;
        else if(who == 1)
            wz <= 8'b00000000;
        else
            wz <= 8'b00001000;
        end
        3'd5: wz <= 8'b00000100;
        3'd6: begin
        if(shine == 0)
            wz <= 8'b00000010;
        else if(who == 2)
            wz <= 8'b00000000;
        else
            wz <= 8'b00000010;
        end
        3'd7: begin
        if(shine == 0)
            wz <= 8'b00000001;
        else if(who == 2)
            wz <= 8'b00000000;
        else
            wz <= 8'b00000001;
        end
        default: wz <= 8'b00000000;
    endcase        
    end

    always@(posedge clk) begin
    if(qiehuan == 1) 
    begin
    case(cnt_wz)    //数码管要要显示的数值
    4'd0: num <= sw;   //输送十位 
    4'd1: num <= gw;   //输送个位
    4'd2: num <= 12;  //12无意义,仅用于下面的判断
    4'd3: num <= sw;
    4'd4: num <= gw;
    4'd5: num <= 12;   //本12同理上面
    4'd6: num <= sw;
    4'd7: num <= gw;
    default: num <= 0;
    endcase
    end 
    else if(qiehuan == 0) 
    begin
    case(cnt_wz)    //数码管要要显示的数值
    4'd0: num <= 2;   //输送十位 
    4'd1: num <= 0;   //输送个位
    4'd2: num <= 2;  //12无意义,仅用于下面的判断
    4'd3: num <= 3;
    4'd4: num <= month_h;
    4'd5: num <= month_l;   //本12同理上面
    4'd6: num <= day_h;
    4'd7: num <= day_l;
    default: num <= 0;
    endcase
    end
    end
 
     
    always @(posedge clk) begin
    if(num <= 9)
        case(num)   //根据num的值决定输出的段码
        4'd0: smg <= 8'b11111100;
        4'd1: smg <= 8'b01100000;
        4'd2: smg <= 8'b11011010;
        4'd3: smg <= 8'b11110010;
        4'd4: smg <= 8'b01100110;
        4'd5: smg <= 8'b10110110;
        4'd6: smg <= 8'b10111110;
        4'd7: smg <= 8'b11100000;
        4'd8: smg <= 8'b11111110;
        4'd9: smg <= 8'b11110110;
        default: smg <= 8'b00000000;
        endcase
    else
        smg <= 8'b00000010;    //不同类型间的分隔符
    end
    
    always@(posedge clk)   //LED仅用于状态的判断,若不需要可以去掉
    case(who)
    2'd0: light <= 8'b10000000;
    2'd1: light <= 8'b11000000;
    2'd2: light <= 8'b11100000;
    endcase

    assign seg_data0 = smg;    //段码输送
    assign seg_data1 = smg;
    assign seg_cs = wz;     //位选输送
    assign led = light;    //LED输送
endmodule

3、约束文件

set_property -dict {PACKAGE_PIN P17 IOSTANDARD LVCMOS33} [get_ports clk]
set_property -dict {PACKAGE_PIN P15 IOSTANDARD LVCMOS33} [get_ports rst]
#数码管位选
set_property -dict {PACKAGE_PIN G2 IOSTANDARD LVCMOS33} [get_ports {seg_cs[0]}]
set_property -dict {PACKAGE_PIN C2 IOSTANDARD LVCMOS33} [get_ports {seg_cs[1]}]
set_property -dict {PACKAGE_PIN C1 IOSTANDARD LVCMOS33} [get_ports {seg_cs[2]}]
set_property -dict {PACKAGE_PIN H1 IOSTANDARD LVCMOS33} [get_ports {seg_cs[3]}]
set_property -dict {PACKAGE_PIN G1 IOSTANDARD LVCMOS33} [get_ports {seg_cs[4]}]
set_property -dict {PACKAGE_PIN F1 IOSTANDARD LVCMOS33} [get_ports {seg_cs[5]}]
set_property -dict {PACKAGE_PIN E1 IOSTANDARD LVCMOS33} [get_ports {seg_cs[6]}]
set_property -dict {PACKAGE_PIN G6 IOSTANDARD LVCMOS33} [get_ports {seg_cs[7]}]
#数码管段选
set_property -dict {PACKAGE_PIN B4 IOSTANDARD LVCMOS33} [get_ports {seg_data0[0]}]
set_property -dict {PACKAGE_PIN A4 IOSTANDARD LVCMOS33} [get_ports {seg_data0[1]}]
set_property -dict {PACKAGE_PIN A3 IOSTANDARD LVCMOS33} [get_ports {seg_data0[2]}]
set_property -dict {PACKAGE_PIN B1 IOSTANDARD LVCMOS33} [get_ports {seg_data0[3]}]
set_property -dict {PACKAGE_PIN A1 IOSTANDARD LVCMOS33} [get_ports {seg_data0[4]}]
set_property -dict {PACKAGE_PIN B3 IOSTANDARD LVCMOS33} [get_ports {seg_data0[5]}]
set_property -dict {PACKAGE_PIN B2 IOSTANDARD LVCMOS33} [get_ports {seg_data0[6]}]
set_property -dict {PACKAGE_PIN D5 IOSTANDARD LVCMOS33} [get_ports {seg_data0[7]}]
#另一组数码管段选
set_property -dict {PACKAGE_PIN D4 IOSTANDARD LVCMOS33} [get_ports {seg_data1[0]}]
set_property -dict {PACKAGE_PIN E3 IOSTANDARD LVCMOS33} [get_ports {seg_data1[1]}]
set_property -dict {PACKAGE_PIN D3 IOSTANDARD LVCMOS33} [get_ports {seg_data1[2]}]
set_property -dict {PACKAGE_PIN F4 IOSTANDARD LVCMOS33} [get_ports {seg_data1[3]}]
set_property -dict {PACKAGE_PIN F3 IOSTANDARD LVCMOS33} [get_ports {seg_data1[4]}]
set_property -dict {PACKAGE_PIN E2 IOSTANDARD LVCMOS33} [get_ports {seg_data1[5]}]
set_property -dict {PACKAGE_PIN D2 IOSTANDARD LVCMOS33} [get_ports {seg_data1[6]}]
set_property -dict {PACKAGE_PIN H2 IOSTANDARD LVCMOS33} [get_ports {seg_data1[7]}]
#八个LED
set_property -dict {PACKAGE_PIN F6 IOSTANDARD LVCMOS33} [get_ports {led[0]}]
set_property -dict {PACKAGE_PIN G4 IOSTANDARD LVCMOS33} [get_ports {led[1]}]
set_property -dict {PACKAGE_PIN G3 IOSTANDARD LVCMOS33} [get_ports {led[2]}]
set_property -dict {PACKAGE_PIN J4 IOSTANDARD LVCMOS33} [get_ports {led[3]}]
set_property -dict {PACKAGE_PIN H4 IOSTANDARD LVCMOS33} [get_ports {led[4]}]
set_property -dict {PACKAGE_PIN J3 IOSTANDARD LVCMOS33} [get_ports {led[5]}]
set_property -dict {PACKAGE_PIN J2 IOSTANDARD LVCMOS33} [get_ports {led[6]}]
set_property -dict {PACKAGE_PIN K2 IOSTANDARD LVCMOS33} [get_ports {led[7]}]
#五个按键
set_property -dict {PACKAGE_PIN R11 IOSTANDARD LVCMOS33} [get_ports {key[0]}]
set_property -dict {PACKAGE_PIN R17 IOSTANDARD LVCMOS33} [get_ports {key[1]}]
set_property -dict {PACKAGE_PIN R15 IOSTANDARD LVCMOS33} [get_ports {key[2]}]
set_property -dict {PACKAGE_PIN V1 IOSTANDARD LVCMOS33} [get_ports {key[3]}]
set_property -dict {PACKAGE_PIN U4 IOSTANDARD LVCMOS33} [get_ports {key[4]}]

set_property PACKAGE_PIN P5 [get_ports {qiehuan[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {qiehuan[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports day_adj]
set_property IOSTANDARD LVCMOS33 [get_ports month_adj]
set_property PACKAGE_PIN N4 [get_ports month_adj]
set_property PACKAGE_PIN R1 [get_ports day_adj]

4、实验现象

如图,最左边的一个拨码开关可以切换时间与日期的界面,最右边的两个拨码开关可以调整日期,3个按键可以调整日期。

基于fpga的数字时钟开发_第1张图片

你可能感兴趣的:(fpga开发)