基于 FPGA 的分秒计数器
文章目录
- 基于 FPGA 的分秒计数器
- 一、设计原理
- (一)分频器
- (二)按键控制
- (三)分秒计时器
- 二、设计实现
- (一)分频器模块(clock_divider.v)
- (二)按键控制模块
- (三)分秒计时器模块
- (四)顶层模块(top.v)
- 三、实现效果
- 四、总结
一、设计原理
(一)分频器
FPGA 开发板通常提供 50MHz 的时钟信号,但分秒计数器仅需 1Hz 的时钟来实现每秒计数一次的效果。分频器模块的作用就是将 50MHz 的高频时钟分频为 1Hz 的低频时钟。其核心原理是通过一个计数器对输入时钟进行计数,当计数值达到分频系数的一半(对于 50MHz 到 1Hz 的分频,分频系数为 50000000 ,即计数到 25000000)时,翻转输出时钟信号的电平,同时将计数器清零,重新开始计数。
(二)按键控制
- 按键消抖:机械按键在按下和松开时会产生抖动,导致按键信号不稳定,可能出现多次触发的情况。为解决这一问题,采用延时消抖的方法。当按键状态保持 20ms 以上时,才认为按键状态稳定,锁存该状态并产生一个脉冲信号。在 50MHz 时钟下,1ms 对应 50000 个时钟周期,因此使用一个 20 位的计数器,计数到 1000000(50000×20)即达到 20ms。
- 按键暂停计时:将按键消抖后产生的脉冲信号转换为一个稳定的切换信号。每当检测到有效的按键脉冲时,翻转暂停状态信号,从而实现暂停和继续计时的功能。
(三)分秒计时器
- 计时器逻辑:分秒计数器遵循特定的计数规则。秒个位从 0 计数到 9,满 9 后归零并向秒十位进位;秒十位从 0 计数到 5,满 59 秒后归零并向分个位进位;分个位从 0 计数到 9,满 9 后归零并向分十位进位;分十位从 0 计数到 5,满 59 分后归零,最大显示 59:59。
- 显示模块:七段数码管有共阳极和共阴极两种类型。共阳极数码管在笔段电极接低电平、公共阳极接高电平时发光;共阴极数码管则相反,驱动信号为高电平、公共阴极接低电平时发光。通过将计数器的输出值转换为对应的七段数码管显示代码,实现时间的可视化。
二、设计实现
(一)分频器模块(clock_divider.v)
module clock_divider #(parameter DIVISOR = 50000000
)(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;endendend
endmodule
(二)按键控制模块
- 按键消抖模块(debounce.v)
module debounce(input clk,input reset_n,input key_in,output reg key_pulse
);reg [19:0] count; 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 beginkey_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 count <= count + 1'b1;endelse beginkey_pulse <= key_in_sync2 & ~key_stable; endendend
endmodule
- 按键暂停计时模块(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;endend
endmodule
(三)分秒计时器模块
- 计时器逻辑模块(min_sec_counter.v)
module min_sec_counter(input clk,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;endendend
endmodule
- 显示模块(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; 4'h1: seg = 7'b1111001; 4'h2: seg = 7'b0100100; 4'h3: seg = 7'b0110000; 4'h4: seg = 7'b0011001; 4'h5: seg = 7'b0010010; 4'h6: seg = 7'b0000010; 4'h7: seg = 7'b1111000; 4'h8: seg = 7'b0000000; 4'h9: seg = 7'b0010000; default: seg = 7'b1111111; endcaseend
endmodule
(四)顶层模块(top.v)
module top(input clk_50m,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
);wire clk_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)) 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));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
三、实现效果
通过综合、编译和下载到 FPGA 开发板后,分秒计数器能够准确计时。按下复位按键,计数器归零;按下暂停按键,计时暂停,再次按下则继续计时。七段数码管实时显示当前的分钟和秒数,范围为 00:00 至 59:59,实现了预期的计时功能。
四、总结
本设计成功利用 Verilog 语言在 FPGA 上实现了分秒计数器,通过多个功能模块的协同工作,完成了时钟分频、按键控制、计时和显示等功能。在设计过程中,各模块之间的信号连接和逻辑关系是关键,需要仔细规划和调试。同时,这种模块化的设计方法提高了代码的可读性和可维护性,为后续功能扩展和优化奠定了基础。