一、模块设计
如若不清楚怎么模块化,请看https://blog.csdn.net/szyugly/article/details/146379170?spm=1001.2014.3001.5501
1.1顶层模块
module top_counter(
input wire CLOCK_50, // 50MHz时钟
input wire KEY0, // 暂停/继续按键
output wire [6:0] HEX3, HEX2, HEX1, HEX0, // 4位七段数码管
output wire [3:0] DIGIT_EN // 数码管位选
);
wire clk_1hz; // 1Hz时钟
wire clk_scan; // 扫描时钟(1kHz)
wire pause_pulse; // 消抖后的暂停信号
// 模块实例化
clk_div u_clk_div(
.clk_50m(CLOCK_50),
.clk_1hz(clk_1hz),
.clk_1k(clk_scan)
);
debounce u_debounce(
.clk(CLOCK_50),
.key_in(KEY0),
.key_out(pause_pulse)
);
time_counter u_time_counter(
.clk(clk_1hz),
.pause(pause_pulse),
.min_ten(min_ten),
.min_unit(min_unit),
.sec_ten(sec_ten),
.sec_unit(sec_unit)
);
display u_display(
.clk(clk_scan),
.min_ten(min_ten),
.min_unit(min_unit),
.sec_ten(sec_ten),
.sec_unit(sec_unit),
.seg_data({HEX3, HEX2, HEX1, HEX0}),
.digit_en(DIGIT_EN)
);
endmodule
1.2计数器模块
module time_counter(
input wire clk, // 1Hz时钟
input wire pause, // 暂停信号
output reg [3:0] min_ten, // 分钟十位
output reg [3:0] min_unit, // 分钟个位
output reg [3:0] sec_ten, // 秒钟十位
output reg [3:0] sec_unit // 秒钟个位
);
reg pause_state; // 暂停状态寄存器
always @(posedge clk) begin
if(pause) pause_state <= ~pause_state; // 切换暂停状态
end
always @(posedge clk) begin
if(!pause_state) begin // 非暂停状态
// 秒个位计数
if(sec_unit == 4'd9) begin
sec_unit <= 0;
// 秒十位计数
if(sec_ten == 4'd5) begin
sec_ten <= 0;
// 分钟个位计数
if(min_unit == 4'd9) begin
min_unit <= 0;
// 分钟十位计数
if(min_ten == 4'd5) begin
min_ten <= 0;
end else begin
min_ten <= min_ten + 1;
end
end else begin
min_unit <= min_unit + 1;
end
end else begin
sec_ten <= sec_ten + 1;
end
end else begin
sec_unit <= sec_unit + 1;
end
end
end
endmodule
1.3按键消抖模块
module debounce(
input wire clk, // 50MHz时钟
input wire key_in, // 原始按键输入
output reg key_out // 消抖后输出
);
reg [19:0] counter; // 20ms计数器 (50MHz * 0.02s = 1,000,000)
reg key_reg;
always @(posedge clk) begin
key_reg <= key_in; // 同步输入
if(key_reg != key_out) begin // 状态变化
counter <= 20'd0;
end else if(counter < 20'd1_000_000) begin
counter <= counter + 1;
end else begin
key_out <= key_reg; // 稳定后更新输出
end
end
endmodule
1.4显示驱动模块
module display(
input wire clk, // 扫描时钟(1kHz)
input wire [3:0] min_ten,
input wire [3:0] min_unit,
input wire [3:0] sec_ten,
input wire [3:0] sec_unit,
output reg [6:0] seg_data, // 段选信号
output reg [3:0] digit_en // 位选使能
);
reg [1:0] scan_cnt; // 扫描计数器
reg [3:0] data_temp; // 当前显示数据
// 七段译码表(共阳极)
parameter [6:0] SEG_BCD [0:9] = {
7'b1000000, // 0
7'b1111001, // 1
7'b0100100, // 2
7'b0110000, // 3
7'b0011001, // 4
7'b0010010, // 5
7'b0000010, // 6
7'b1111000, // 7
7'b0000000, // 8
7'b0010000 // 9
};
always @(posedge clk) begin
scan_cnt <= scan_cnt + 1;
case(scan_cnt)
2'b00: begin
digit_en <= 4'b1110; // 最低位
data_temp <= sec_unit;
end
2'b01: begin
digit_en <= 4'b1101;
data_temp <= sec_ten;
end
2'b10: begin
digit_en <= 4'b1011;
data_temp <= min_unit;
end
2'b11: begin
digit_en <= 4'b0111; // 最高位
data_temp <= min_ten;
end
endcase
seg_data <= SEG_BCD[data_temp]; // 查表输出
end
endmodule
1.5分频模块
module clk_div(
input wire clk_50m, // 50MHz输入
output reg clk_1hz, // 1Hz输出
output reg clk_1k // 1kHz扫描时钟
);
// 1Hz分频计数器 (50,000,000分频)
reg [25:0] cnt_1hz;
always @(posedge clk_50m) begin
if(cnt_1hz == 26'd49_999_999) begin
cnt_1hz <= 0;
clk_1hz <= ~clk_1hz;
end else begin
cnt_1hz <= cnt_1hz + 1;
end
end
// 1kHz分频计数器 (50,000分频)
reg [15:0] cnt_1k;
always @(posedge clk_50m) begin
if(cnt_1k == 16'd49_999) begin
cnt_1k <= 0;
clk_1k <= ~clk_1k;
end else begin
cnt_1k <= cnt_1k + 1;
end
end
endmodule
二、效果展示
计数器