从零到一:用Verilog在DE2-115上构建你的第一个数字钟

从零到一:用Verilog在DE2-115上构建你的第一个数字钟

还记得第一次看到七段数码管亮起时的兴奋吗?那跳动的数字不仅仅是电流的舞蹈,更是逻辑与时间的完美交融。如果你刚接触FPGA开发,想要找一个既有趣又实用的项目来练手,数字钟绝对是你的不二之选。它涵盖了数字电路设计的核心概念:时钟分频、状态控制、计数器设计、显示驱动,甚至还包括了一些高级功能如闹钟和时间校准。本文将带你从零开始,一步步在DE2-115开发板上实现一个功能完整的数字钟,无论你是电子工程的学生,还是对硬件设计感兴趣的开发者,都能从中获得扎实的实践技能。

我们将使用Verilog这一硬件描述语言,搭配Intel的Quartus Prime开发环境,从最基本的时钟分频开始,逐步构建起时、分、秒的计数逻辑,添加时间设置和闹钟功能,最终驱动开发板上的数码管显示。过程中,我会分享一些实际开发中容易踩坑的细节,比如按键消抖的处理、多时钟域的设计注意事项,以及如何高效地进行仿真测试。不用担心你是初学者,只要具备基本的数字电路知识,就能跟上这个项目的节奏。

1. 开发环境搭建与项目初始化

在开始编写代码之前,我们需要准备好开发环境。DE2-115开发板搭载了Cyclone IV EP4CE115F29C7 FPGA芯片,这意味着我们需要安装与之兼容的Quartus Prime版本。建议使用Quartus Prime Lite Edition 18.1或更高版本,这个版本对教育用途免费,并且支持DE2-115开发板的所有特性。

安装完成后,第一步是创建一个新的Quartus项目。打开Quartus Prime,选择File > New Project Wizard,按照向导步骤操作。在设备选择页面,务必正确选择芯片型号:Cyclone IV E系列,EP4CE115F29C7。这一步很重要,因为不同的FPGA芯片其资源规模和引脚定义都不相同,选错了会导致后续编译和烧录失败。

项目创建好后,我们需要添加Verilog源文件。数字钟项目通常采用模块化设计,建议为每个主要功能创建独立的.v文件:

// 项目主要模块
module digital_clock(
    input clk_50M,        // 50MHz主时钟
    input reset_n,        // 复位信号(低有效)
    input [1:0] sw_mode,  // 模式选择开关
    input key_hour,       // 小时调整按键
    input key_minute,     // 分钟调整按键
    output [6:0] hex0,    // 数码管段选信号
    output [6:0] hex1,
    output [6:0] hex2,
    output [6:0] hex3,
    output [6:0] hex4,
    output [6:0] hex5,
    output alarm_led      // 闹钟提示LED
);
// 模块内部信号和实例化将在这里实现
endmodule

在开始编码前,建议先规划好项目的目录结构。一个良好的结构可以大大提高开发效率:

project_root/
│
├── src/           # Verilog源代码
│   ├── digital_clock_top.v
│   ├── clock_divider.v
│   ├── time_counter.v
│   ├── alarm_module.v
│   ├── display_driver.v
│   └── debounce.v
│
├── sim/           # 仿真文件
│   └── tb_digital_clock.v
│
├── quartus/       # Quartus项目文件
│   ├── digital_clock.qpf
│   └── digital_clock.qsf
│
└── doc/           # 文档和笔记
    └── pin_assignment.md

提示:在项目初期就建立良好的版本控制习惯。使用Git管理你的代码,定期提交并撰写清晰的commit message,这会在后期调试和功能扩展时节省大量时间。

2. 时钟分频与按键消抖处理

DE2-115开发板提供的50MHz时钟信号对于数字钟来说太快了,我们需要将其分频到1Hz(每秒一个脉冲)来驱动秒计数器。时钟分频是FPGA设计中的基础操作,但实现方式有多种选择,每种都有其适用场景。

最简单的分频方法是使用计数器进行整数分频:

module clock_divider(
    input clk_50M,
    input reset_n,
    output reg clk_1Hz
);
    
    // 50MHz到1Hz需要分频50,000,000倍
    reg [25:0] counter;  // 26位计数器,最大计数值67,108,863
    
    always @(posedge clk_50M or negedge reset_n) begin
        if (!reset_n) begin
            counter <= 0;
            clk_1Hz <= 0;
        end
        else if (counter == 26'd49_999_999) begin
            counter <= 0;
            clk_1Hz <= ~clk_1Hz;
        end
        else begin
            counter <= counter + 1;
        end
    end
endmodule

这种直接计数的方法简单直观,但需要注意计数器的位宽要足够大,否则会导致分频错误。26位计数器最大可计数67,108,863,刚好满足50,000,000的分频需求。

在实际应用中,机械按键会产生抖动现象,即按下或释放时信号会快速跳变多次后才稳定。如果不处理这种抖动,一次按键操作可能会被误识别为多次操作。我们需要添加消抖模块:

module debounce(
    input clk,
    input button_in,
    output reg button_out
);
    
    parameter DEBOUNCE_LIMIT = 16'd50000;  // 50MHz时钟下约1ms消抖时间
    reg [15:0] count;
    reg button_state;
    
    always @(posedge clk) begin
        if (button_in != button_state) begin
            // 输入状态变化,重置计数器
            button_state <= button_in;
            count <= 0;
        end
        else if (count == DEBOUNCE_LIMIT) begin
            // 稳定时间达到阈值,输出稳定状态
            button_out <= button_state;
        end
        else begin
            // 继续计数
            count <= count + 1;
        end
    end
endmodule

消抖原理很简单:当检测到按键状态变化时,开始计时,只有状态保持一定时间(通常是1-20ms)后才认为变化有效。这样可以过滤掉机械抖动产生的高频噪声。

参数名称 推荐值 说明
DEBOUNCE_LIMIT 50,000 50MHz时钟下对应1ms消抖时间
计数器位宽 16位 足够计到65,535,满足消抖需求
采样频率 50MHz 使用系统主时钟即可
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值