分秒计数器设计
本次实验内容为:DE2-115板子上用 Verilog编程实现一个 分秒计数器,并具备按键暂停、按键消抖功能
一、系统架构设计
-
顶层模块划分
顶层模块(top) ├── 按键消抖模块(key_debounce) ├── 边沿检测模块(edge_detect) ├── 时钟分频模块(clk_divider) ├── 分秒计数器(min_sec_counter) └── 七段译码器(seg7_decoder)
-
模块交互逻辑
plaintextCLK(50MHz) → 分频 → 1Hz时钟 → 计时模块 KEY → 消抖模块 → 暂停控制 → 计时模块 计时输出 → 数码管显示模块 → 物理数码管
二、核心模块实现
顶层模块
verilog
module top(
input CLOCK_50, // 50MHz时钟(PIN_Y2)
input KEY0, // 复位
input KEY1, // 暂停
output [0:6] HEX7, HEX6,// 分十位、分个位
output [0:6] HEX5, HEX4 // 秒十位、秒个位
);
wire clk_1hz; // 1Hz时钟信号
wire key_stable; // 消抖后按键信号
wire pause_trigger; // 边沿检测信号
reg pause_state = 1'b0; // 暂停状态寄存器
wire [3:0] min_tens, min_ones;
wire [3:0] sec_tens, sec_ones;
// 按键消抖模块(20ms消抖)
debounce #(.DEBOUNCE_MS(20)) debounce_inst(
.clk(CLOCK_50),
.button_in(~KEY1),
.button_out(key_stable)
);
// 边沿检测模块
edge_detect edge_inst(
.clk(CLOCK_50),
.signal_in(key_stable),
.edge_out(pause_trigger)
);
// 时钟分频模块
clk_divider clk_div_inst(
.clk(CLOCK_50),
.reset(~KEY0),
.pause(pause_state),
.clk_out(clk_1hz)
);
// 分秒计数器
min_sec_counter counter(
.clk(clk_1hz),
.reset(~KEY0),
.min_tens(min_tens),
.min_ones(min_ones),
.sec_tens(sec_tens),
.sec_ones(sec_ones)
);
// 显示译码模块
seg7_decoder hex7(.bcd(min_tens), .seg(HEX7));
seg7_decoder hex6(.bcd(min_ones), .seg(HEX6));
seg7_decoder hex5(.bcd(sec_tens), .seg(HEX5));
seg7_decoder hex4(.bcd(sec_ones), .seg(HEX4));
// 暂停状态机
always @(posedge CLOCK_50 or posedge ~KEY0) begin
if (~KEY0) pause_state <= 1'b0;
else if (pause_trigger) pause_state <= ~pause_state;
end
endmodule
时钟分频模块
上次实验提到过:DE2-115开发板配备了一个固定的时钟源。该开发板内置了一个50MHz的晶振,因此对于50MHz的时钟频率,时钟周期为20ns。
verilog
module clk_divider(
input clk,
input reset,
input pause,
output reg clk_out
);
parameter DIV_FACTOR = 26'd49_999_999; // 50MHz→1Hz
reg [25:0] cnt;
always @(posedge clk or posedge reset) begin
if (reset) begin
cnt <= 0;
clk_out <= 0;
end else if (!pause) begin
cnt <= (cnt == DIV_FACTOR) ? 0 : cnt + 1;
if (cnt == DIV_FACTOR) clk_out <= ~clk_out;
end
end
endmodule
按键模块
我选择KEY0做为复位按钮,KEY1作为控制分秒计数器的暂停按钮

DE2-115 提供了四个按钮开关,每个按钮开关都通过一个施 密特触发器进行了去抖动处理。四个施密特触发器的输出信 号,分别为KEY0、KEY1、KEY2、KEY3,直接连接到了Cyclone IV E FPGA。当按钮没有 被按下的时候,它的输出是高电平,按下去则给出一个低电平
verilog
module key_debounce #(
parameter DEBOUNCE_MS = 20 // 可配置消抖时间
)(
input clk,
input button_in,
output reg button_out
);
localparam MAX_COUNT = 50_000_000 * DEBOUNCE_MS / 1000;
reg [23:0] cnt;
always @(posedge clk) begin
if (button_in != button_out) begin
cnt <= (cnt == MAX_COUNT-1) ? 0 : cnt + 1;
if (cnt == MAX_COUNT-1) button_out <= button_in;
end else begin
cnt <= 0;
end
end
endmodule
边沿检测模块
verilog
module edge_detect(
input clk,
input signal_in,
output reg edge_out
);
reg signal_delay;
always @(posedge clk) begin
signal_delay <= signal_in;
edge_out <= signal_in & ~signal_delay; // 上升沿检测
end
endmodule
计数器模块
verilog
module min_sec_counter(
input clk,
input reset,
output reg [3:0] min_tens,
output reg [3:0] min_ones,
output reg [3:0] sec_tens,
output reg [3:0] sec_ones
);
// 秒计数器
always @(posedge clk or posedge reset) begin
if (reset) begin
sec_ones <= 4'd0;
sec_tens <= 4'd0;
end else begin
if (sec_ones == 4'd9) begin
sec_ones <= 4'd0;
sec_tens <= (sec_tens == 4'd5) ? 4'd0 : sec_tens + 1;
end else begin
sec_ones <= sec_ones + 1;
end
end
end
// 分钟计数器
always @(posedge clk or posedge reset) begin
if (reset) begin
min_ones <= 4'd0;
min_tens <= 4'd0;
end else if (sec_tens == 4'd5 && sec_ones == 4'd9) begin
if (min_ones == 4'd9) begin
min_ones <= 4'd0;
min_tens <= (min_tens == 4'd5) ? 4'd0 : min_tens + 1;
end else begin
min_ones <= min_ones + 1;
end
end
end
endmodule
数码管模块
之前提及到过,可以利用38译码器点亮7段数码管(其实只需要7段译码器即可),但是我们的输入情况有9种,所以七段译码器需要将4位BCD码转换为对应的段码;也说过数码管的点亮逻辑:FPGA输出低电压的时候,对应的字码段点亮,反之则熄灭
进行分秒展示,一共要用到4个数码管,为了更直观展示我选择:HEX7,HEX6作为分钟位,HEX5,HEX4做为秒位


数码管显示数字 | 需要亮起来的小段 | 对应的7位二进制数 |
---|---|---|
0 | HEX[0]~[5] | 0000001 |
1 | HEX[1]~[2] | 1001111 |
2 | HEX[0],[1],[3],[4],[6] | 0010010 |
3 | HEX[0],[1],[2],[3],[6] | 0000110 |
4 | HEX[1].[2].[5],[6] | 1001100 |
5 | HEX[0],[2],[3],[5],[6] | 0100100 |
6 | HEX[0],[2],[3],[4],[5],[6] | 0100000 |
7 | HEX[1]~[3] | 0001111 |
8 | HEX[0]~[6] | 0000000 |
9 | HEX[0],[1],[2],[3],[5],[6] | 0001000 |
verilog
module seg7_decoder(
input [3:0] bcd,
output reg [6:0] seg
);
always @(*) begin
case(bcd)
4'd0 : seg = 7'b0000_001; // 0
4'd1 : seg = 7'b1001_111; // 1
4'd2 : seg = 7'b0010_010; // 2
4'd3 : seg = 7'b0000_110; // 3
4'd4 : seg = 7'b1001_100; // 4
4'd5 : seg = 7'b0100_100; // 5
4'd6 : seg = 7'b0100_000; // 6
4'd7 : seg = 7'b0001_111; // 7
4'd8 : seg = 7'b0000_000; // 8
4'd9 : seg = 7'b0001_000; // 9
default: seg = 7'b1111_111; // 灭
endcase
end
endmodule
HEX4~7对应的引脚图如下:


引脚配置:


实现效果:
• 上电后数码管显示00:00并开始计时
• 按下KEY[1]后立即暂停,再次按下恢复
• 复位键KEY[0]可清零计数器
总结:按键边沿检测模块使用双寄存器级联技术捕获有效按键信号,配合FSM状态机实现暂停/恢复功能切换