[FGPA基础学习]分秒计数器的制作

分秒计数器设计

​ 本次实验内容为:DE2-115板子上用 Verilog编程实现一个 分秒计数器,并具备按键暂停、按键消抖功能

一、系统架构设计

  1. 顶层模块划分

    复制代码
    顶层模块(top)
    ├── 按键消抖模块(key_debounce)
    ├── 边沿检测模块(edge_detect)
    ├── 时钟分频模块(clk_divider)
    ├── 分秒计数器(min_sec_counter)
    └── 七段译码器(seg7_decoder)
  2. 模块交互逻辑

    plaintext 复制代码
    CLK(50MHz) → 分频 → 1Hz时钟 → 计时模块
    KEY → 消抖模块 → 暂停控制 → 计时模块
    计时输出 → 数码管显示模块 → 物理数码管

二、核心模块实现

顶层模块

verilog 复制代码
module top(
    input CLOCK_50,         // 50MHz时钟(PIN_Y2)
    input KEY0,             // 复位
    input KEY1,             // 暂停
    output [0:6] HEX7, HEX6,// 分十位、分个位
    output [0:6] HEX5, HEX4 // 秒十位、秒个位
);

wire clk_1hz;               // 1Hz时钟信号
wire key_stable;            // 消抖后按键信号
wire pause_trigger;         // 边沿检测信号
reg pause_state = 1'b0;     // 暂停状态寄存器

wire [3:0] min_tens, min_ones;
wire [3:0] sec_tens, sec_ones;

// 按键消抖模块(20ms消抖)
debounce #(.DEBOUNCE_MS(20)) debounce_inst(
    .clk(CLOCK_50),
    .button_in(~KEY1),
    .button_out(key_stable)
);

// 边沿检测模块
edge_detect edge_inst(
    .clk(CLOCK_50),
    .signal_in(key_stable),
    .edge_out(pause_trigger)
);

// 时钟分频模块
clk_divider clk_div_inst(
    .clk(CLOCK_50),
    .reset(~KEY0),
    .pause(pause_state),
    .clk_out(clk_1hz)
);

// 分秒计数器
min_sec_counter counter(
    .clk(clk_1hz),
    .reset(~KEY0),
    .min_tens(min_tens),
    .min_ones(min_ones),
    .sec_tens(sec_tens),
    .sec_ones(sec_ones)
);

// 显示译码模块
seg7_decoder hex7(.bcd(min_tens), .seg(HEX7));
seg7_decoder hex6(.bcd(min_ones), .seg(HEX6));
seg7_decoder hex5(.bcd(sec_tens), .seg(HEX5));
seg7_decoder hex4(.bcd(sec_ones), .seg(HEX4));

// 暂停状态机
always @(posedge CLOCK_50 or posedge ~KEY0) begin
    if (~KEY0) pause_state <= 1'b0;
    else if (pause_trigger) pause_state <= ~pause_state;
end

endmodule

时钟分频模块

上次实验提到过:DE2-115开发板配备了一个固定的时钟源。该开发板内置了一个50MHz的晶振,因此对于50MHz的时钟频率,时钟周期为20ns。

verilog 复制代码
module clk_divider(
    input clk,
    input reset,
    input pause,
    output reg clk_out
);
    parameter DIV_FACTOR = 26'd49_999_999; // 50MHz→1Hz
    reg [25:0] cnt;
    
    always @(posedge clk or posedge reset) begin
        if (reset) begin
            cnt <= 0;
            clk_out <= 0;
        end else if (!pause) begin
            cnt <= (cnt == DIV_FACTOR) ? 0 : cnt + 1;
            if (cnt == DIV_FACTOR) clk_out <= ~clk_out;
        end
    end
endmodule

按键模块

我选择KEY0做为复位按钮,KEY1作为控制分秒计数器的暂停按钮

DE2-115 提供了四个按钮开关,每个按钮开关都通过一个施 密特触发器进行了去抖动处理。四个施密特触发器的输出信 号,分别为KEY0、KEY1、KEY2、KEY3,直接连接到了Cyclone IV E FPGA。当按钮没有 被按下的时候,它的输出是高电平,按下去则给出一个低电平

verilog 复制代码
module key_debounce #(
    parameter DEBOUNCE_MS = 20  // 可配置消抖时间
)(
    input clk,
    input button_in,
    output reg button_out
);
    localparam MAX_COUNT = 50_000_000 * DEBOUNCE_MS / 1000;
    reg [23:0] cnt;
    
    always @(posedge clk) begin
        if (button_in != button_out) begin
            cnt <= (cnt == MAX_COUNT-1) ? 0 : cnt + 1;
            if (cnt == MAX_COUNT-1) button_out <= button_in;
        end else begin
            cnt <= 0;
        end
    end
endmodule

边沿检测模块

verilog 复制代码
module edge_detect(
    input clk,
    input signal_in,
    output reg edge_out
);
    reg signal_delay;
    always @(posedge clk) begin
        signal_delay <= signal_in;
        edge_out <= signal_in & ~signal_delay; // 上升沿检测
    end
endmodule

计数器模块

verilog 复制代码
module min_sec_counter(
    input clk,
    input reset,
    output reg [3:0] min_tens,
    output reg [3:0] min_ones,
    output reg [3:0] sec_tens,
    output reg [3:0] sec_ones
);
    // 秒计数器
    always @(posedge clk or posedge reset) begin
        if (reset) begin
            sec_ones <= 4'd0;
            sec_tens <= 4'd0;
        end else begin
            if (sec_ones == 4'd9) begin
                sec_ones <= 4'd0;
                sec_tens <= (sec_tens == 4'd5) ? 4'd0 : sec_tens + 1;
            end else begin
                sec_ones <= sec_ones + 1;
            end
        end
    end
    
    // 分钟计数器
    always @(posedge clk or posedge reset) begin
        if (reset) begin
            min_ones <= 4'd0;
            min_tens <= 4'd0;
        end else if (sec_tens == 4'd5 && sec_ones == 4'd9) begin
            if (min_ones == 4'd9) begin
                min_ones <= 4'd0;
                min_tens <= (min_tens == 4'd5) ? 4'd0 : min_tens + 1;
            end else begin
                min_ones <= min_ones + 1;
            end
        end
    end
endmodule

数码管模块

之前提及到过,可以利用38译码器点亮7段数码管(其实只需要7段译码器即可),但是我们的输入情况有9种,所以七段译码器需要将4位BCD码转换为对应的段码;也说过数码管的点亮逻辑:FPGA输出低电压的时候,对应的字码段点亮,反之则熄灭

进行分秒展示,一共要用到4个数码管,为了更直观展示我选择:HEX7,HEX6作为分钟位,HEX5,HEX4做为秒位

数码管显示数字 需要亮起来的小段 对应的7位二进制数
0 HEX0~5 0000001
1 HEX1~2 1001111
2 HEX0,1,3,4,6 0010010
3 HEX0,1,2,3,6 0000110
4 HEX1.2.5,6 1001100
5 HEX0,2,3,5,6 0100100
6 HEX0,2,3,4,5,6 0100000
7 HEX1~3 0001111
8 HEX0~6 0000000
9 HEX0,1,2,3,5,6 0001000
verilog 复制代码
module seg7_decoder(
    input [3:0] bcd,
    output reg [6:0] seg
);
    always @(*) begin
        case(bcd)
            4'd0 : seg = 7'b0000_001; // 0
            4'd1 : seg = 7'b1001_111; // 1
            4'd2 : seg = 7'b0010_010; // 2
            4'd3 : seg = 7'b0000_110; // 3
            4'd4 : seg = 7'b1001_100; // 4
            4'd5 : seg = 7'b0100_100; // 5
            4'd6 : seg = 7'b0100_000; // 6
            4'd7 : seg = 7'b0001_111; // 7
            4'd8 : seg = 7'b0000_000; // 8
            4'd9 : seg = 7'b0001_000; // 9
        default: seg = 7'b1111_111; // 灭
        endcase
    end
endmodule

HEX4~7对应的引脚图如下:

引脚配置:

实现效果

• 上电后数码管显示00:00并开始计时

• 按下KEY1后立即暂停,再次按下恢复

• 复位键KEY0可清零计数器

总结:按键边沿检测模块使用双寄存器级联技术捕获有效按键信号,配合FSM状态机实现暂停/恢复功能切换

相关推荐
通信小呆呆20 小时前
当算法有了“五感”:多模态数据融合如何向人体感官协同学习?
人工智能·学习·算法·机器学习·机器人
H__Rick20 小时前
自动对焦学习-3
人工智能·学习·计算机视觉
Daisy Lee20 小时前
量化学习-第1章-什么是量化金融
学习·金融·datawhale
坏孩子的诺亚方舟21 小时前
FPGA系统架构设计实践15_高云Arora V系列时钟体系
fpga开发·系统架构
Alsn8621 小时前
等待学习-学习目录:Docker 容器安全攻防
学习·安全·docker
YM52e21 小时前
买菜计算器小应用 - HarmonyOS ArkUI 开发实战-PC版本
学习·华为·harmonyos·鸿蒙·鸿蒙系统
小雨下雨的雨21 小时前
HarmonyOS ArkUI训练营入门-组件掌握系列-Animation 动画效果实现-PC版本
学习·华为·harmonyos·鸿蒙
cqbzcsq1 天前
CellFlow虚拟细胞论文阅读
论文阅读·人工智能·笔记·学习·生物信息
YangYang9YangYan1 天前
2026初入职场学习数据分析的价值
学习·数据挖掘·数据分析
guslegend1 天前
理论学习:什么是 Coding Agent?
学习