在DE2-115板子上用 Verilog编程实现一个 分秒计数器,并具备按键暂停、按键消抖功能
功能描述
1.分秒计数器功能
- 计数器需要显示分钟和秒。
- 每秒钟秒计数器递增一次。
- 每60秒分钟计数器递增一次。
- 分钟和秒的计数范围分别为0到59。
实现思路
- 使用一个1Hz的时钟信号来驱动计数器。
- 每次1Hz时钟信号上升沿到来时,秒计数器递增。
- 当秒计数器达到59时,清零秒计数器,并递增分钟计数器。
- 当分钟计数器达到59时,清零分钟计数器。
2.按键暂停功能
- 使用一个按键来控制计数器的启动和暂停。
- 按键按下时,计数器暂停;再次按下时,计数器继续运行。
实现思路
- 使用一个寄存器来记录计数器的状态(运行或暂停)。
- 每次按键信号上升沿到来时,切换计数器的状态。
3.按键消抖功能
- 按键信号通常带有噪声,需要通过消抖模块来稳定按键信号。
实现思路
- 使用一个计数器来检测按键信号的稳定性。
- 如果按键信号在一段时间内保持不变,则认为按键信号稳定。
- 这段时间称为消抖时间,通常设置为20ms左右。
4.七段显示功能
- 使用七段显示器显示分钟和秒的值。
实现思路
- 将分钟和秒的值分别转换为四位二进制数。
- 使用七段显示器的驱动模块将四位二进制数转换为七段显示器的控制信号。
模块划分
1.顶层模块(TopModule.v)
- 负责连接各个子模块,实现整体功能。
- 接收输入时钟、复位信号和按键信号,输出七段显示器的控制信号。
module TopModule (
input wire clk, // 输入时钟信号
input wire rst_n, // 复位信号(低电平有效)
input wire button, // 按键信号
output wire [6:0] segment1, // 第一个七段显示器
output wire [6:0] segment2, // 第二个七段显示器
output wire [6:0] segment3, // 第三个七段显示器
output wire [6:0] segment4 // 第四个七段显示器
);
wire clk_1Hz; // 分频后的1Hz时钟信号
wire button_debounced; // 消抖后的按键信号
reg [5:0] minutes; // 分钟计数
reg [5:0] seconds; // 秒计数
// 分频模块
ClockDivider u_ClockDivider (
.clk_in(clk),
.rst_n(rst_n),
.clk_out(clk_1Hz)
);
// 按键消抖模块
Debouncer u_Debouncer (
.clk(clk),
.rst_n(rst_n),
.button_in(button),
.button_out(button_debounced)
);
// 计数器模块
Counter u_Counter (
.clk(clk_1Hz),
.rst_n(rst_n),
.pause(button_debounced),
.minutes(minutes),
.seconds(seconds)
);
// 七段显示模块
SevenSegmentDisplay u_Segment1 (
.digit(minutes[3:0]),
.segment(segment1)
);
SevenSegmentDisplay u_Segment2 (
.digit(minutes[5:4]),
.segment(segment2)
);
SevenSegmentDisplay u_Segment3 (
.digit(seconds[3:0]),
.segment(segment3)
);
SevenSegmentDisplay u
2.分频模块(ClockDivider.v)
- 将输入的50MHz时钟分频为1Hz时钟。
- 提供秒计数器所需的时钟信号。
module ClockDivider #(
parameter DIVIDE = 50_000_000 // 分频系数
)(
input wire clk_in, // 输入时钟信号
input wire rst_n, // 复位信号(低电平有效)
output reg clk_out // 输出分频后的时钟信号
);
reg [31:0] counter; // 计数器,用于分频
always @(posedge clk_in or negedge rst_n) begin
if (!rst_n) begin
counter <= 0;
clk_out <= 0;
end
else if (counter == DIVIDE - 1) begin
counter <= 0;
clk_out <= ~clk_out; // 切换输出时钟状态
end
else begin
counter <= counter + 1;
end
end
endmodule
3.按键消抖模块(Debouncer.v)
- 消除按键信号的抖动。
- 提供稳定的按键信号给暂停控制逻辑。
module Debouncer #(
parameter DEBOUNCE_TIME = 20'd10000 // 消抖时间(20ms)
)(
input wire clk, // 输入时钟信号
input wire rst_n, // 复位信号(低电平有效)
input wire button_in, // 输入按键信号
output reg button_out // 输出稳定的按键信号
);
reg [19:0] debounce_counter; // 消抖计数器
reg button_sync; // 同步寄存器
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
debounce_counter <= 0;
button_sync <= 0;
button_out <= 0;
end
else begin
button_sync <= button_in; // 同步按键信号
if (button_sync == button_out) begin
debounce_counter <= 0; // 如果信号稳定,计数器清零
end
else if (debounce_counter < DEBOUNCE_TIME) begin
debounce_counter <= debounce_counter + 1; // 计数器递增
end
else begin
button_out <= button_sync; // 更新稳定的按键信号
debounce_counter <= 0;
end
end
end
endmodule
4.计数器模块
- 实现分秒计数逻辑。
- 根据1Hz时钟信号递增秒计数器和分钟计数器。
- 根据按键信号控制计数器的运行和暂停。
module Counter #(
parameter MAX_MINUTES = 60 // 最大分钟数
)(
input wire clk, // 输入时钟信号
input wire rst_n, // 复位信号(低电平有效)
input wire pause, // 暂停信号
output reg [5:0] minutes, // 分钟计数
output reg [5:0] seconds // 秒计数
);
reg [5:0] second_counter; // 秒计数器
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
minutes <= 0;
seconds <= 0;
second_counter <= 0;
end
else if (!pause) begin
if (second_counter == 59) begin
second_counter <= 0;
seconds <= seconds + 1;
if (seconds == 59) begin
seconds <= 0;
minutes <= minutes + 1;
if (minutes == MAX_MINUTES - 1) begin
minutes <= 0;
end
end
end
else begin
second_counter <= second_counter + 1;
end
end
end
endmodule
5.七段显示模块(SevenSegmentDisplay.v)
- 将分钟和秒的值转换为七段显示器的控制信号。
- 显示分钟和秒的值。
module SevenSegmentDisplay #(
parameter SEGMENT_COUNT = 4 // 显示段数
)(
input wire [3:0] digit, // 输入数字
output reg [6:0] segment // 七段显示信号
);
always @(*) begin
case (digit)
4'd0: segment = 7'b1000000; // 0
4'd1: segment = 7'b1111001; // 1
4'd2: segment = 7'b0100100; // 2
4'd3: segment = 7'b0110000; // 3
4'd4: segment = 7'b0011001; // 4
4'd5: segment = 7'b0010010; // 5
4'd6: segment = 7'b0000010; // 6
4'd7: segment = 7'b1111000; // 7
4'd8: segment = 7'b0000000; // 8
4'd9: segment = 7'b0010000; // 9
default: segment = 7'b1111111; // 空白
endcase
end
endmodule
实验结果
视频在后续帖子中发布
心得
再次学习了verilog的编写方法,学习了如何使用vscode编写verilog并且编译运行。对de2-115开发板更加熟悉了,学习到了更多有关于它的知识。