目录
一、分频器
二、按键控制
1.按键消抖
2.按键暂停计时
三、分秒计时器
1.计时器逻辑
2.显示模块
3.顶层模块
总结
一、分频器
在实现计数器的算法之前我们先要实现分频器,对板子的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) beginif (!reset_n) begincounter <= 0;clk_out <= 0;endelse beginif (counter >= (DIVISOR/2 - 1)) begincounter <= 0;clk_out <= ~clk_out;endelse begincounter <= counter + 1;endend
endendmodule
二、按键控制
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) beginif (!reset_n) beginkey_in_sync1 <= 1'b1;key_in_sync2 <= 1'b1;key_stable <= 1'b1;count <= 20'd0;key_pulse <= 1'b0;endelse begin// 同步输入信号key_in_sync1 <= key_in;key_in_sync2 <= key_in_sync1;// 检测按键状态变化if (key_in_sync2 != key_stable) beginkey_stable <= key_in_sync2;count <= 20'd0;endelse if (count < 20'd1000000) begin // 20ms (50MHz * 0.02s)count <= count + 1'b1;endelse beginkey_pulse <= key_in_sync2 & ~key_stable; // 产生上升沿脉冲endend
endendmodule
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) beginif (!reset_n) beginpause_toggle <= 1'b0;endelse if (pause_pulse) beginpause_toggle <= ~pause_toggle;end
endendmodule
三、分秒计时器
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) beginif (!reset_n) beginsec_ones <= 4'd0;sec_tens <= 4'd0;min_ones <= 4'd0;min_tens <= 4'd0;endelse if (!pause) begin // 只有不在暂停状态时才计数// 秒个位计数if (sec_ones == 4'd9) beginsec_ones <= 4'd0;// 秒十位计数if (sec_tens == 4'd5) beginsec_tens <= 4'd0;// 分个位计数if (min_ones == 4'd9) beginmin_ones <= 4'd0;// 分十位计数if (min_tens == 4'd5) beginmin_tens <= 4'd0;endelse beginmin_tens <= min_tens + 4'd1;endendelse beginmin_ones <= min_ones + 4'd1;endendelse beginsec_tens <= sec_tens + 4'd1;endendelse beginsec_ones <= sec_ones + 4'd1;endend
endendmodule
2.显示模块
七段数码管分共阳极与共阴极两种。共阳极数码管其工作特点是,当笔段电极接低电平,公共阳极接高电平时,相应笔段可以发光。共阴极数码管则与之相反,它是将发光二极管的阴极短接后作为公共阴极,当驱动信号为高电平、公共阴极接低电平时,才能发光。
七段数码管显示模块 (seven_seg_display.v)
module seven_seg_display(input [3:0] digit,output reg [6:0] seg
);always @(*) begincase (digit)4'h0: seg = 7'b1000000; // 04'h1: seg = 7'b1111001; // 14'h2: seg = 7'b0100100; // 24'h3: seg = 7'b0110000; // 34'h4: seg = 7'b0011001; // 44'h5: seg = 7'b0010010; // 54'h6: seg = 7'b0000010; // 64'h7: seg = 7'b1111000; // 74'h8: seg = 7'b0000000; // 84'h9: seg = 7'b0010000; // 9default: seg = 7'b1111111; // 全灭endcase
endendmodule
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
实现效果:
分秒计时器
总结
代码分为了很多个模块,需要仔细思考各模块之间的关系