目录
一、分频器
在实现计数器的算法之前我们先要实现分频器,对板子的50MHz频率进行分频,从而输出一个1Hz的时钟信号。也就是让最终呈现的数字实现一秒变一下,从而达到计时器的效果。
(clock_divider.v)代码如下:
module clock_divider #(
parameter DIVISOR = 50000000 // 默认50MHz->1Hz
)(
input clk_in,
input reset_n,
output reg clk_out
);
reg [31:0] counter;
always @(posedge clk_in or negedge reset_n) begin
if (!reset_n) begin
counter <= 0;
clk_out <= 0;
end
else begin
if (counter >= (DIVISOR/2 - 1)) begin
counter <= 0;
clk_out <= ~clk_out;
end
else begin
counter <= counter + 1;
end
end
end
endmodule
二、按键控制
1.按键消抖
由于每次按下按键时存在抖动,容易引起按键的多次触发,因此按键消抖模块就可以解决这个问题。
按键消抖主要通过延时来实现,即当按键的一个状态保持20ms以上,即锁存按键的状态,这样既保证按键消抖的稳定又保证了一定的灵敏性。
按键消抖模块 (debounce.v)
module debounce(
input clk,
input reset_n,
input key_in,
output reg key_pulse
);
reg [19:0] count; // 20ms 消抖计数器 (50MHz时钟下,1ms=50000个周期)
reg key_in_sync1, key_in_sync2;
reg key_stable;
always @(posedge clk or negedge reset_n) begin
if (!reset_n) begin
key_in_sync1 <= 1'b1;
key_in_sync2 <= 1'b1;
key_stable <= 1'b1;
count <= 20'd0;
key_pulse <= 1'b0;
end
else begin
// 同步输入信号
key_in_sync1 <= key_in;
key_in_sync2 <= key_in_sync1;
// 检测按键状态变化
if (key_in_sync2 != key_stable) begin
key_stable <= key_in_sync2;
count <= 20'd0;
end
else if (count < 20'd1000000) begin // 20ms (50MHz * 0.02s)
count <= count + 1'b1;
end
else begin
key_pulse <= key_in_sync2 & ~key_stable; // 产生上升沿脉冲
end
end
end
endmodule
2.按键暂停计时
将按键的脉冲信号 转换为一个稳定的 切换信号 。每当检测到一个有效的 pause_pulse 时,就翻转 pause_toggle 的状态(从 0 变 1,或从 1 变 0)。
暂停控制模块 (pause_control.v)
module pause_control(
input clk,
input reset_n,
input pause_pulse,
output reg pause_toggle
);
always @(posedge clk or negedge reset_n) begin
if (!reset_n) begin
pause_toggle <= 1'b0;
end
else if (pause_pulse) begin
pause_toggle <= ~pause_toggle;
end
end
endmodule
三、分秒计时器
1.计时器逻辑
计数规则
秒个位 :从0 计数到 9,然后归零并触发秒十位进位。
秒十位 :从 0 计数到 5(即 59 秒),然后归零并触发分个位进位。
分个位 :从 0 计数到 9,然后归零并触发分十位进位。
分十位 :从 0 计数到 5(即 59 分),然后归零(最大显示 59:59)
分秒计数器模块 (min_sec_counter.v)
module min_sec_counter(
input clk, // 1Hz 时钟
input reset_n, // 复位信号
input pause, // 暂停信号
output reg [3:0] sec_ones, // 秒个位
output reg [3:0] sec_tens, // 秒十位
output reg [3:0] min_ones, // 分个位
output reg [3:0] min_tens // 分十位
);
always @(posedge clk or negedge reset_n) begin
if (!reset_n) begin
sec_ones <= 4'd0;
sec_tens <= 4'd0;
min_ones <= 4'd0;
min_tens <= 4'd0;
end
else if (!pause) begin // 只有不在暂停状态时才计数
// 秒个位计数
if (sec_ones == 4'd9) begin
sec_ones <= 4'd0;
// 秒十位计数
if (sec_tens == 4'd5) begin
sec_tens <= 4'd0;
// 分个位计数
if (min_ones == 4'd9) begin
min_ones <= 4'd0;
// 分十位计数
if (min_tens == 4'd5) begin
min_tens <= 4'd0;
end
else begin
min_tens <= min_tens + 4'd1;
end
end
else begin
min_ones <= min_ones + 4'd1;
end
end
else begin
sec_tens <= sec_tens + 4'd1;
end
end
else begin
sec_ones <= sec_ones + 4'd1;
end
end
end
endmodule
2.显示模块
七段数码管分共阳极与共阴极两种。共阳极数码管其工作特点是,当笔段电极接低电平,公共阳极接高电平时,相应笔段可以发光。共阴极数码管则与之相反,它是将发光二极管的阴极短接后作为公共阴极,当驱动信号为高电平、公共阴极接低电平时,才能发光。
七段数码管显示模块 (seven_seg_display.v)
module seven_seg_display(
input [3:0] digit,
output reg [6:0] seg
);
always @(*) begin
case (digit)
4'h0: seg = 7'b1000000; // 0
4'h1: seg = 7'b1111001; // 1
4'h2: seg = 7'b0100100; // 2
4'h3: seg = 7'b0110000; // 3
4'h4: seg = 7'b0011001; // 4
4'h5: seg = 7'b0010010; // 5
4'h6: seg = 7'b0000010; // 6
4'h7: seg = 7'b1111000; // 7
4'h8: seg = 7'b0000000; // 8
4'h9: seg = 7'b0010000; // 9
default: seg = 7'b1111111; // 全灭
endcase
end
endmodule
3.顶层模块
module top(
input clk_50m, // 50MHz 时钟输入
input reset_n, // 复位按键 (低电平有效)
input pause_key, // 暂停/继续按键
output [6:0] hex0, // 秒个位数码管
output [6:0] hex1, // 秒十位数码管
output [6:0] hex2, // 分个位数码管
output [6:0] hex3, // 分十位数码管
output [7:0] ledg // LED 显示秒数 (可选)
);
// 内部信号定义
wire clk_1hz; // 1Hz 时钟
wire pause_pulse; // 消抖后的暂停脉冲信号
wire pause_toggle; // 暂停状态信号
wire [3:0] sec_ones; // 秒个位
wire [3:0] sec_tens; // 秒十位
wire [3:0] min_ones; // 分个位
wire [3:0] min_tens; // 分十位
// 时钟分频模块
clock_divider #(
.DIVISOR(50000000) // 50MHz -> 1Hz
) clk_div (
.clk_in(clk_50m),
.reset_n(reset_n),
.clk_out(clk_1hz)
);
// 按键消抖模块
debounce debounce_pause (
.clk(clk_50m),
.reset_n(reset_n),
.key_in(pause_key),
.key_pulse(pause_pulse)
);
// 暂停控制模块 (将脉冲信号转换为 toggle 信号)
pause_control pause_ctrl (
.clk(clk_50m),
.reset_n(reset_n),
.pause_pulse(pause_pulse),
.pause_toggle(pause_toggle)
);
// 分秒计数器模块
min_sec_counter counter (
.clk(clk_1hz),
.reset_n(reset_n),
.pause(pause_toggle),
.sec_ones(sec_ones),
.sec_tens(sec_tens),
.min_ones(min_ones),
.min_tens(min_tens)
);
// 数码管显示模块
seven_seg_display seg0 (.digit(sec_ones), .seg(hex0));
seven_seg_display seg1 (.digit(sec_tens), .seg(hex1));
seven_seg_display seg2 (.digit(min_ones), .seg(hex2));
seven_seg_display seg3 (.digit(min_tens), .seg(hex3));
endmodule
实现效果:
分秒计时器
总结
代码分为了很多个模块,需要仔细思考各模块之间的关系