目录
一、实现一个分秒计数器
1. 任务说明
在DE2-115板子上用 Verilog编程实现一个 分秒计数器,并具备按键暂停、按键消抖功能。
2. 消抖模块介绍
2.1 消抖模块的作用
按键在物理接触时会产生机械抖动(约10-20ms),导致FPGA误判多次触发。消抖模块通过延时检测消除抖动,确保每次按键动作只产生单次稳定信号。
2.2 模块接口定义
module debounce #(
parameter DEBOUNCE_MS = 20, // 消抖时间(默认20ms)
parameter CLK_FREQ = 50_000_000 // 时钟频率(默认50MHz)
)
(
input clk, // 时钟输入(50MHz)
input button_in, // 原始按键输入(需预同步)
output reg button_out // 消抖后输出(稳定信号)
);
2.3 核心实现原理
(1) 同步器
reg button_sync;
always @(posedge clk) begin
button_sync <= button_in; // 用D触发器同步异步信号
end
(2) 消抖状态机
localparam COUNTER_MAX = (CLK_FREQ / 1000) * DEBOUNCE_MS;
reg [COUNTER_BITS-1:0] counter;
always @(posedge clk) begin
if (button_sync != button_out) begin // 检测到输入变化
if (counter == COUNTER_MAX-1) begin
button_out <= button_sync; // 稳定后更新输出
counter <= 0;
end else begin
counter <= counter + 1; // 计数等待稳定
end
end else begin
counter <= 0; // 状态未变化时复位计数器
end
end
消抖逻辑:
-
当检测到按键状态变化时,启动计数器。
-
持续计数直到达到
DEBOUNCE_MS
时间(如20ms)。 -
只有信号稳定超过设定时间,才更新输出。
2.4 硬件行为测试
时间 | 按键物理状态 | button_in | button_out | 说明 |
---|---|---|---|---|
0ms | 松开(高) | 1 | 1 | 初始状态 |
1ms | 按下(抖动) | 0→1→0 | 1 | 抖动期,输出不变 |
21ms | 稳定按下 | 0 | 0 | 消抖完成,输出更新 |
22ms | 松开(抖动) | 1→0→1 | 0 | 抖动期,输出不变 |
42ms | 稳定松开 | 1 | 1 | 消抖完成,输出更新 |
3. 项目实例
3.1 新建项目
建立项目工程名
然后一直点击NEXT进入开发板选型界面
开发板信息如下,点击finish创建
3.2 创建Verilog文件添加至顶层实体
编译代码如下:
module Counter (
input clk, // 50MHz时钟(PIN_Y2)
input raw_reset, // 原始复位信号(KEY0,PIN_M23,低有效)
input raw_start_pause,// 原始启动/暂停信号(KEY1,PIN_M21,低有效)
output [6:0] hex3, // 分钟十位(HEX3,无小数点)
output [6:0] hex2, // 分钟个位(HEX2,无小数点)
output [6:0] hex1, // 秒十位(HEX1,无小数点)
output [6:0] hex0 // 秒个位(HEX0,无小数点)
);
// ================= 消抖模块 ==================
wire debounced_reset; // 消抖后的复位(高有效)
wire debounced_start_pause;// 消抖后的启停(高有效)
debounce #(
.DEBOUNCE_MS(20) // 20ms消抖时间
) deb_reset_inst (
.clk(clk),
.button_in(~raw_reset), // 物理按键低有效,取反
.button_out(debounced_reset)
);
debounce deb_start_inst (
.clk(clk),
.button_in(~raw_start_pause),
.button_out(debounced_start_pause)
);
// ================= 核心计数 ==================
core_counter counter_core (
.clk(clk),
.reset(debounced_reset),
.start_pause(debounced_start_pause),
.hex3(hex3),
.hex2(hex2),
.hex1(hex1),
.hex0(hex0)
);
endmodule
// ============== 消抖模块实现 ================
module debounce #(
parameter DEBOUNCE_MS = 20, // 消抖时间(毫秒)
parameter CLK_FREQ = 50_000_000 // 时钟频率(Hz)
)(
input clk,
input button_in, // 原始按键输入
output reg button_out// 消抖后输出
);
localparam COUNTER_MAX = (CLK_FREQ / 1000) * DEBOUNCE_MS;
localparam COUNTER_BITS = $clog2(COUNTER_MAX);
reg [COUNTER_BITS-1:0] counter;
reg button_sync;
// 同步器防亚稳态
always @(posedge clk) begin
button_sync <= button_in;
end
// 消抖核心逻辑
always @(posedge clk) begin
if (button_sync != button_out) begin
if (counter == COUNTER_MAX-1) begin
button_out <= button_sync;
counter <= 0;
end else begin
counter <= counter + 1;
end
end else begin
counter <= 0;
end
end
endmodule
// ============== 核心计数逻辑 ================
module core_counter (
input clk,
input reset, // 高有效复位
input start_pause, // 高有效启停(1=暂停,0=运行)
output [6:0] hex3, // 改为7位输出
output [6:0] hex2,
output [6:0] hex1,
output [6:0] hex0
);
// 分频器(50MHz→1Hz)
reg [25:0] counter;
wire one_sec_enable = (counter == 26'd49_999_999);
// BCD计数器
reg [3:0] min_tens; // 0-5
reg [3:0] min_ones; // 0-9
reg [3:0] sec_tens; // 0-5
reg [3:0] sec_ones; // 0-9
// 七段译码器(共阳极,g,f,e,d,c,b,a)
function [6:0] seg7;
input [3:0] bcd;
begin
case (bcd)
4'd0: seg7 = 7'b1000000; // 0
4'd1: seg7 = 7'b1111001; // 1
4'd2: seg7 = 7'b0100100; // 2
4'd3: seg7 = 7'b0110000; // 3
4'd4: seg7 = 7'b0011001; // 4
4'd5: seg7 = 7'b0010010; // 5
4'd6: seg7 = 7'b0000010; // 6
4'd7: seg7 = 7'b1111000; // 7
4'd8: seg7 = 7'b0000000; // 8
4'd9: seg7 = 7'b0010000; // 9
default: seg7 = 7'b1111111; // 灭
endcase
end
endfunction
// 分频逻辑
always @(posedge clk or posedge reset) begin
if (reset) begin
counter <= 0;
end else begin
if (one_sec_enable)
counter <= 0;
else
counter <= counter + 1;
end
end
// 计数逻辑(高电平暂停)
always @(posedge clk or posedge reset) begin
if (reset) begin
{sec_ones, sec_tens, min_ones, min_tens} <= 16'd0;
end else if (!start_pause) begin // 低电平运行
if (one_sec_enable) begin
if (sec_ones == 9) begin
sec_ones <= 0;
if (sec_tens == 5) begin
sec_tens <= 0;
if (min_ones == 9) begin
min_ones <= 0;
min_tens <= (min_tens == 5) ? 0 : min_tens + 1;
end else begin
min_ones <= min_ones + 1;
end
end else begin
sec_tens <= sec_tens + 1;
end
end else begin
sec_ones <= sec_ones + 1;
end
end
end
end
// 数码管输出(无小数点)
assign hex3 = seg7(min_tens); // 分钟十位
assign hex2 = seg7(min_ones); // 分钟个位
assign hex1 = seg7(sec_tens); // 秒十位
assign hex0 = seg7(sec_ones); // 秒个位
endmodule
另存为.v文件(名称与工程名一致)
选择Files,显示Counter.v文件
右键点击文件,将其设置为顶层实体
3.3 引脚分配
进行引脚分配:
3.4 选择烧录器
点击选择烧录器
选择USB-Blaster [USB-0]
PS:如果遇到无法识别USB接口的情况,即只显示No-Hardware,可参考一下博客进行解决:使用Altera综合工具Quartus II下载到FPGA时无法识别USB-Blaster问题_fpga usb blaster 禁止-优快云博客文章浏览阅读1.7w次,点赞25次,收藏48次。问题描述:当使用Quartus II综合好的文件下载到FPGA时发现,没有识别到下载接口USB_Blaster,如下图所示。图片中高亮的部分只有显示No Hardware,并没有显示我们需要的USB-Blaster[USB-0](上面截图显示出来USB-Blaster[USB-0]是因为这张图是我解决这个问题后截取的),那么遇到这种情况该如何解决呢?解决方法: 首先检查你得设备..._fpga usb blaster 禁止
https://blog.youkuaiyun.com/l2563898960/article/details/80309089
3.5 添加烧录文件&下载
进入output_files,选择.sof文件,点击open
点击添加编程与配置,然后点击start开始下载
3.6 烧录结果
点击KEY0,分秒计数器重置归零;长按KEY1可以暂停计数,松开可继续计时