FPGA——分秒计数器

发布于:2025-04-03 ⋅ 阅读:(22) ⋅ 点赞:(0)

一、实验任务

在DE2-115板子上用 Verilog编程实现一个分秒计数器,并具备按键暂停、按键消抖功能。

二、系统模块

  • 分频模块
    高频时钟(如50MHz)分频得到低频时钟(如1Hz),用于驱动计数器模块,使计时每秒更新一次。用户代码中的分频模块可能通过计数器的累加和翻转来实现分频。
  • 消抖模块
    处理机械按键的抖动问题,确保每次按键按下只产生一个稳定的脉冲信号。用户的消抖模块可能使用同步器和计数器来消除按键的抖动,确保复位和暂停信号的可靠性。
  • 计数模块
    负责实际的计时功能,如秒和分的计数,并在达到59秒或59分时进位。用户代码中的该模块可能在1Hz时钟的驱动下递增秒和分,并在复位信号下清零。
  • 数码管显示模块
    将二进制数转换为七段数码管的显示编码,驱动数码管显示当前的秒和分。用户的显示模块可能将秒和分的个位和十位分别解码为对应的七段码。
  • 顶层模块
    通常负责将各个子模块实例化并连接起来,处理全局信号如时钟、按键输入,以及输出显示。用户代码中的顶层模块可能负责协调所有子模块的工作,比如将分频后的时钟传递给计数器,处理按键消抖后的信号,并将计数结果传递给显示模块。

三、工程源码

1. 分频模块代码

clock_divider.v

module clock_divider #(
    parameter DIVIDER = 24_999_999 
)(
    input  clk,
    input  reset,
    output reg clk_out
);

reg [31:0] counter;

always @(posedge clk or posedge reset) begin
    if (reset) begin
        counter <= 0;
        clk_out <= 0;
    end else begin
        counter <= (counter == DIVIDER) ? 0 : counter + 1;
        clk_out <= (counter == DIVIDER) ? ~clk_out : clk_out;
    end
end

endmodule

2. 消抖模块代码

key_debounce.v

module key_debounce #(
    parameter DEBOUNCE_MS = 20,
    parameter CLK_FREQ = 50_000_000
)(
    input  clk,
    input  key_n,
    output reg key_pulse
);

localparam CNT_MAX = (DEBOUNCE_MS * CLK_FREQ) / 1000;

reg [1:0] sync_chain = 2'b11;
reg [19:0] count = 0;
reg stable_state = 1'b1;
reg prev_state = 1'b1;

always @(posedge clk) begin
    // 同步器链
    sync_chain <= {sync_chain[0], key_n};
    
    // 消抖计数器
    if (sync_chain[1] != stable_state) begin
        count <= (count < CNT_MAX) ? count + 1 : 0;
        if (count == CNT_MAX) stable_state <= sync_chain[1];
    end else begin
        count <= 0;
    end
    
    // 边沿检测
    prev_state <= stable_state;
    key_pulse <= prev_state & ~stable_state;
end

endmodule

3. 计数模块代码

time_counter.v

module time_counter(
    input  clk,        // 1Hz时钟
    input  reset,      // 扩展复位
    input  enable,     // 运行使能
    output reg [5:0] seconds=0,
    output reg [5:0] minutes=0
);

// 60进制计数逻辑
always @(posedge clk or posedge reset) begin
    if (reset) begin
        seconds <= 6'd0;
        minutes <= 6'd0;
    end else if (enable) begin
        if (seconds == 6'd59) begin
            seconds <= 6'd0;
            minutes <= (minutes == 6'd59) ? 6'd0 : minutes + 1;
        end else begin
            seconds <= seconds + 1;
        end
    end
end

endmodule

4. 数码管显示模块代码

seg7_decoder.v

module seg7_decoder(
    input [3:0] num,
    output reg [6:0] seg
);

// 共阳极数码管编码
always @(*) begin
    case(num)
        4'd0: seg = 7'b1000000; // 0
        4'd1: seg = 7'b1111001; // 1
        4'd2: seg = 7'b0100100; // 2
        4'd3: seg = 7'b0110000; // 3
        4'd4: seg = 7'b0011001; // 4
        4'd5: seg = 7'b0010010; // 5
        4'd6: seg = 7'b0000010; // 6
        4'd7: seg = 7'b1111000; // 7
        4'd8: seg = 7'b0000000; // 8
        4'd9: seg = 7'b0010000; // 9
        default: seg = 7'b1111111; // 灭
    endcase
end

endmodule

5. 顶层模块代码

counter.v

module counter(
    input  CLOCK_50,
    input  KEY0,
    input  KEY1,
    output [6:0] hex0,
    output [6:0] hex1,
    output [6:0] hex2,
    output [6:0] hex3
);

// 时钟与信号定义
wire clk_1hz;                   
wire reset_pulse;               // 消抖后的复位脉冲
wire pause_pulse;               // 消抖后的暂停脉冲
reg running=1'b1;               // 运行状态信号
wire [5:0] seconds, minutes;    // 时间信号

// 时钟分频模块
clock_divider #(.DIVIDER(24_999_999)) clk_div (
    .clk(CLOCK_50),
    .reset(reset_pulse),
    .clk_out(clk_1hz)
);

// 按键消抖模块
key_debounce #(.DEBOUNCE_MS(20)) key_reset (
    .clk(CLOCK_50),
    .key_n(KEY0),
    .key_pulse(reset_pulse)
);

key_debounce #(.DEBOUNCE_MS(20)) key_pause (
    .clk(CLOCK_50),
    .key_n(KEY1),
    .key_pulse(pause_pulse)
);

// 复位信号展宽(保持顶层时序控制)
reg [19:0] reset_hold = 20'hFFFFF; // 20-bit for ~21ms reset
always @(posedge CLOCK_50) begin
    if (reset_pulse) reset_hold <= 20'hFFFFF;
    else if (reset_hold > 0) reset_hold <= reset_hold - 1;
end
wire reset_extended = (reset_hold > 0);


always @(posedge CLOCK_50) begin
    case(1'b1)
        reset_pulse:  running <= 1'b1;    // 复位优先
        pause_pulse: running <= ~running; // 状态切换
        default:    running <= running; // 保持状态
    endcase
end

// 核心计数模块
time_counter timer_core (
    .clk(clk_1hz),
    .reset(reset_extended),
    .enable(running),
    .seconds(seconds),
    .minutes(minutes)
);

// 显示模块
seg7_decoder seg0(.num(seconds%10), .seg(hex0));
seg7_decoder seg1(.num(seconds/10), .seg(hex1));
seg7_decoder seg2(.num(minutes%10), .seg(hex2));
seg7_decoder seg3(.num(minutes/10), .seg(hex3));

endmodule

四、管脚信息

在这里插入图片描述
在这里插入图片描述

五、运行结果

将以上文件加载到项目中后,将counter.v设置为置顶文件
在这里插入图片描述

分秒计数器

参考资料

https://blog.csdn.net/weixin_43828944/article/details/122360794
https://blog.csdn.net/weixin_43828944/article/details/122699533
并辅以AI软件DeepSeek完成此项实验。

总结

本次实验实现了一个简单的分秒计数器,但由于首次做该实验也遇到了很多问题,例如只能计数,复位键和暂停键按下时无反应,但最后通过DeepSeek的辅助解决了该问题。