在DE2-115板子上用 Verilog编程实现一个 分秒计数器

一、 引言

1、介绍计数器的概念及应用

计数器是一种数字电路,用于对事件或脉冲进行计数。它可以跟踪数量的增减,通常以二进制或十进制的形式表示。计数器在电子设备和计算机系统中具有广泛的应用,

2、DE2-115 开发板的简介

DE2-115 开发板是 Altera(现为英特尔)推出的一款功能强大的 FPGA 开发平台,专用于数字系统设计与开发。它适合教育、学习和工程原型开发,以下是该开发板的一些主要特点和功能:

主要特点

  1. FPGA 芯片

    • DE2-115 开发板搭载了 Altera 的 EP4CE115 FPGA 芯片,这是 Cyclone IV 系列的一部分,具有 115K 逻辑单元(LEs)。

    • 支持丰富的 I/O 引脚可用于各种接口和外设。

  2. 丰富的 I/O 接口

    • 提供多种数字和模拟 I/O 连接,例如 LED、开关、七段显示器、声音输出(DAC)、摄像头接口等。

    • 配备多个 USB 接口,方便与计算机进行数据传输和编程。

  3. 内存资源

    • 板载了 SDRAM(64MB)和 Flash 存储器(4MB),可用于存储数据和程序。

    • 支持 EEPROM 存储,用于保存配置信息。

  4. 时钟源

    • 内置时钟源(50 MHz)和多个可选时钟输入,供设计中使用。

    • 支持外部扩展时钟源。

  5. 模拟输入输出(ADC/DAC)

    • 板上集成了音频 DAC,用于音频处理应用。

    • 提供模拟输入输出接口,便于传感器和其他模拟设备连接。

3、项目的目的和目标

在DE2-115板子上用 Verilog编程实现一个 分秒计数器,并具备按键暂停、按键消抖功能

二、 项目准备

硬件需求

DE2-115 开发板连接线

外部按键模块 

软件需求

Quartus 软件

模拟工具(如 ModelSim)

三、分秒计数器的 Verilog 设计

设计模块结

构输入/输出端口定义

输入端口

clk:系统时钟信号 (50MHz)。

raw_reset:复位信号,低电平有效。

raw_start_pause:启动/暂停信号,低电平有效。

输出端口

hex3:分钟十位的七段显示输出。

hex2:分钟个位的七段显示输出,带小数点。

hex1:秒十位的七段显示输出。

hex0:秒个位的七段显示输出。

示例代码

module minute_second_counter (  
    input wire clk,             // 50MHz输入时钟  
    input wire raw_reset,       // 原始复位信号(高电平有效)  
    input wire raw_start_pause, // 启动/暂停按键(高电平有效)  
    output reg [7:0] hex3,      // 分钟十位(HEX3)  
    output reg [7:0] hex2,      // 分钟个位(带小数点,HEX2)  
    output reg [7:0] hex1,      // 秒十位(HEX1)  
    output reg [7:0] hex0       // 秒个位(HEX0)  
);  

计数器逻辑实现

秒钟计数器

实现秒钟计数器逻辑,当秒钟达到 59 时,自动归零并增加分钟计数

分钟计数器

分钟计数器的逻辑与秒钟计数相似,达到 59 分时归零

示例代码

reg [3:0] min_t; // 分钟十位  
reg [3:0] min_o; // 分钟个位  
reg [3:0] sec_t; // 秒十位  
reg [3:0] sec_o; // 秒个位  

always @(posedge clk or posedge raw_reset) begin  
    if (raw_reset) begin  
        // 复位所有计数器  
        min_t <= 0;  
        min_o <= 0;  
        sec_t <= 0;  
        sec_o <= 0;  
    end else if (one_second_enable) begin  
        if (sec_o == 9) begin  
            sec_o <= 0;  
            if (sec_t == 5) begin  
                sec_t <= 0;  
                if (min_o == 9) begin  
                    min_o <= 0;  
                    min_t <= (min_t == 5) ? 0 : min_t + 1; // 短时间防溢出  
                end else begin  
                    min_o <= min_o + 1;  
                end  
            end else begin  
                sec_t <= sec_t + 1;  
            end  
        end else begin  
            sec_o <= sec_o + 1;  
        end  
    end  
end  

时钟分频器的设计

从系统时钟分频到 1Hz 的方法

将输入的 50MHz 时钟分频至 1Hz,以便每秒产生一次脉冲信号供计时器使用

示例代码

reg [25:0] counter; // 用于计数的寄存器  
reg one_second_enable; // 1秒脉冲信号  

always @(posedge clk or posedge raw_reset) begin  
    if (raw_reset) begin  
        counter <= 0;  
        one_second_enable <= 0;  
    end else begin  
        if (counter == 26'd49_999_999) begin  
            counter <= 0;  
            one_second_enable <= 1; // 每秒产生一个脉冲  
        end else begin  
            counter <= counter + 1;  
            one_second_enable <= 0;  
        end  
    end  
end  

四、按键暂停功能的实现

实现逻辑

增加一个称为start_pause的输入信号,用于控制计数的启动和暂停状态。该信号为低电平有效。

在计数器逻辑中,增加对start_pause信号的检测。

start_pause信号为高电平时,计数器将保持当前值,不再增加。当start_pause为低电平时,计数器会继续正常计数。

示例代码

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 // 根据start_pause状态进行暂停  
        // 不进行计数更新  
    end else 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  

五、按键消抖功能

实现逻辑

定义消抖模块,输入为原按键信号,输出为经过处理后的稳定信号。

创建一个计数器,当检测到按键状态变化时,开始计时。如果计时器在一定时间内(如20ms)没有再次变化,更新输出为当前状态。如果变化,重置计数器。

示例代码

module debounce (  
    input clk,  
    input button_in, // 原始按键输入  
    output reg button_out // 消抖后输出  
);  
    reg [19:0] count; // 20ms的计数器(50MHz时钟)  

    always @(posedge clk) begin  
        button_sync <= button_in; // 添加同步器避免亚稳态  
        
        if (button_sync != button_out) begin  
            count <= count + 1;  
            if (count == 20'hFFFFF) begin // 达到20ms  
                button_out <= button_sync; // 更新输出  
                count <= 0; // 重置计数器  
            end  
        end else begin  
            count <= 0; // 状态未变化时复位计数器  
        end  
    end  
endmodule  

六、完整代码示例

module minute_second_counter (  
    input clk,                 // 50MHz时钟(PIN_R8)  
    input raw_reset,           // 原始复位信号(连接按键KEY0,PIN_R22,低电平有效)  
    input raw_start_pause,     // 原始启动/暂停信号(连接按键KEY1,PIN_R21,低电平有效)  
    output [7:0] hex3,         // 分钟十位(HEX3)  
    output [7:0] hex2,         // 分钟个位(带小数点)(HEX2)  
    output [7:0] hex1,         // 秒十位(HEX1)  
    output [7:0] hex0          // 秒个位(HEX0)  
);  

// 消抖模块接口  
wire debounced_reset;          // 消抖后的复位信号(高有效)  
wire debounced_start_pause;    // 消抖后的启动/暂停信号(高有效)  

// 实例化两个消抖模块(分别处理复位和启动/暂停按键)  
debounce deb_reset(  
    .clk(clk),  
    .button_in(~raw_reset),      // 输入信号取反(适配低有效物理按键)  
    .button_out(debounced_reset)  
);  

debounce deb_start(  
    .clk(clk),  
    .button_in(~raw_start_pause),  
    .button_out(debounced_start_pause)  
);  

// 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  

// 分频逻辑:将50MHz时钟分频到1Hz  
reg [25:0] counter;  
reg one_sec_enable;  

always @(posedge clk or posedge debounced_reset) begin  
    if (debounced_reset) begin  
        counter <= 0;  
        one_sec_enable <= 0;  
    end else begin  
        if (counter == 26'd49_999_999) begin // 50MHz -> 1Hz  
            counter <= 0;  
            one_sec_enable <= 1;  
        end else begin  
            counter <= counter + 1;  
            one_sec_enable <= 0;  
        end  
    end  
end  

// 状态机定义  
typedef enum logic [1:0] {  
    IDLE,      // 暂停状态  
    RUNNING    // 计时状态  
} state_t;  

state_t current_state, next_state;  

// 状态寄存器  
always @(posedge clk or posedge debounced_reset) begin  
    if (debounced_reset) begin  
        current_state <= IDLE;  
        {sec_ones, sec_tens, min_ones, min_tens} <= 16'h0; // 全部清零  
    end else begin  
        current_state <= next_state;  
    end  
end  

// 状态转移逻辑  
always @(*) begin  
    case (current_state)  
        IDLE: begin  
            next_state = debounced_start_pause ? RUNNING : IDLE;  
        end  
        RUNNING: begin  
            next_state = debounced_start_pause ? IDLE : RUNNING;  
        end  
        default: next_state = IDLE; // 默认返回初始状态  
    endcase  
end  

// 计数逻辑  
always @(posedge clk) begin  
    if (current_state == RUNNING) 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;  
                        // 分钟十位递增(0-5)  
                        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  

// 七段译码  
function [7:0] seg7;  
    input [3:0] bcd;  
    begin  
        case (bcd)  
            4'd0: seg7 = 8'hC0;  // 0  
            4'd1: seg7 = 8'hF9;  // 1  
            4'd2: seg7 = 8'hA4;  // 2  
            4'd3: seg7 = 8'hB0;  // 3  
            4'd4: seg7 = 8'h99;  // 4  
            4'd5: seg7 = 8'h92;  // 5  
            4'd6: seg7 = 8'h82;  // 6  
            4'd7: seg7 = 8'hF8;  // 7  
            4'd8: seg7 = 8'h80;  // 8  
            4'd9: seg7 = 8'h90;  // 9  
            default: seg7 = 8'hFF; // 灭  
        endcase  
    end  
endfunction  

// 显示输出(HEX2显示分钟个位并带小数点)  
assign hex3 = seg7(min_tens);         // 分钟十位  
assign hex2 = {1'b0, seg7(min_ones)[6:0]}; // 秒个位带小数点  
assign hex1 = seg7(sec_tens);          // 秒十位  
assign hex0 = seg7(sec_ones);          // 秒个位  

endmodule  

// 消抖模块(参考设计)  
module debounce (  
    input clk,  
    input button_in,  
    output reg button_out  
);  
    // 消抖计数器  
    reg [19:0] count;  
    reg button_sync;  

    always @(posedge clk) begin  
        button_sync <= button_in; // 同步  
        if (button_sync != button_out) begin  
            count <= count + 1;  
            if (count == 20'hFFFFF) begin // 达到20ms效果  
                button_out <= button_sync;  
                count <= 0;  
            end  
        end else begin  
            count <= 0;  
        end  
    end  
endmodule  

七、总结

在 DE2-115 开发板上用 Verilog 实现分秒计数器的过程可以让我们深入理解了 FPGA 硬件架构与 Verilog 编程的关系,特别是在设计时钟分频和计数逻辑中学会了如何将软件映射到硬件电路。

通过模块化设计,提高了代码的可读性和可维护性,并在调试过程中掌握了使用仿真工具和逻辑分析仪的技巧。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值