fpga:分秒计时器

news2025/4/20 6:29:12

任务目标

分秒计数器核心功能:实现从00:00到59:59的循环计数,通过四个七段数码管显示分钟和秒。

复位功能:支持硬件复位,将计数器归零并显示00:00。

启动/暂停控制:通过按键控制计时的启动和暂停。

消抖处理:消除按键抖动对计时逻辑的影响,确保按键响应稳定。

实现思路

核心功能

计数逻辑:

使用两个BCD(二进制编码的十进制)计数器分别存储分钟和秒:

秒计数器:个位(0-9)和十位(0-5)。

分钟计数器:个位(0-9)和十位(0-5)。

每秒递增秒计数器:

当秒的个位达到9时,十位加一,个位归零。

当秒的十位达到5且个位达到9时,分钟计数器加一,秒计数器归零。

当分钟的个位达到9且十位达到5时,计数器归零。

显示逻辑:

将BCD计数器的值转换为七段数码管的段码。

使用查找表(case语句)将每个数字映射到对应的段码。

确保数码管的段码格式正确(共阳极或共阴极)。

边界条件:

秒从59递增到0(即60)时,分钟加一。

分钟从59递增到0(即60)时,计数器归零。

七段数码管

数码管的一种是半导体发光器件,数码管可分为七段数码管和八段数码管,区别在于八段数码管比七段数码管多一个用于显示小数点的发光二极管单元DP(decimal point),其基本单元是发光二极管。

七段数码管分为共阳极及共阴极,共阳极的七段数码管的正极(或阳极)为八个发光二极管的共有正极,其他接点为独立发光二极管的负极(或阴极),使用者只需把正极接电,不同的负极接地就能控制七段数码管显示不同的数字。共阴极的七段数码管与共阳极的只是接驳方法相反而已。

如图是数码管显示编码表,包含了共阳和共阴两种不同的极性。

复位功能

复位信号:

使用一个硬件按键作为复位输入,按下时复位信号激活。

复位信号为低电平有效或高电平有效,需根据具体硬件设计确定。

复位逻辑:

当复位信号激活时,将所有计数器清零。

同时将状态机重置到初始状态(暂停状态)。

确保复位信号优先级高于其他操作,避免复位过程中计数器继续运行。

启动/暂停控制功能

状态管理:

使用状态机管理计时逻辑,定义两个状态:

IDLE(暂停状态)。

RUNNING(运行状态)。

按键按下时,状态在IDLE和RUNNING之间切换。

按键检测:

使用边沿检测识别按键的按下事件,避免误触发。

在状态机中,根据当前状态和按键输入更新下一个状态。

状态输出:

在RUNNING状态下,每秒递增计数器。

在IDLE状态下,计数器保持不变。

消抖功能

抖动特性:

按键抖动会导致信号短时间内多次变化,需要通过延时采样消除抖动。

消抖逻辑:

使用一个计时器检测按键状态的稳定性:

当检测到按键状态变化时,启动计时器。

如果在设定时间(如20ms)内状态保持稳定,则更新按键状态。

计时器的位宽需足够大,确保能够覆盖设定的延时。

稳定输出:

只有当按键状态稳定后,才输出有效的控制信号。

确保消抖后的信号与原始按键信号的极性一致。

思考

为什么要消抖?

机械按键在按下或释放的瞬间,由于物理触点的弹性作用,会产生一系列快速的开闭跳变,这种现象称为按键抖动。抖动信号表现为一系列短暂的高低电平变化,而不是理想的单一电平变化。

抖动信号可能导致电路误判按键状态,例如,一次按键可能被误认为多次按键。同时抖动信号会导致输出不稳定,影响后续逻辑电路的正常工作;频繁的信号变化可能增加电路的功耗和资源占用等。

因此消抖处理在数字电路设计中至关重要。

如下是按键原理图

状态机思想

状态机思想是一种用于设计和分析数字系统行为的模型,通过定义一系列状态、输入信号和状态转移规则来描述系统的动态行为。状态机的核心在于将复杂的行为分解为若干个简单的状态,并根据输入信号触发状态之间的转移,每个状态对应特定的输出或行为。状态机通常分为Moore型(输出仅取决于当前状态)和Mealy型(输出取决于当前状态和输入信号),具有模块化设计、清晰逻辑和高效资源利用的优点。

在本次任务中我们可以设置IDLE(暂停状态,计数器停止运行)RUNNING(运行状态,计数器每秒递增)两个状态。

代码实现

在这里我们采用分模块化编写代码,具体使用为:在Visual Studio Code中分模块化编写代码,然后编译(配置verilog语言编写环境,可以参考上期报告),编写无误后,使用Quartus 烧录下载,进行实现。

这里分为分频模块、消抖模块、暂停模块、计数器模块、显示模块、顶层模块

分频模块

module clock_divider #(

    parameter DIVISOR = 50000000  // 默认50MHz->1Hz

)(

    input clk_in,

    input reset_n,

    output reg clk_out

);

  

reg [31:0] counter;

  

always @(posedge clk_in or negedge reset_n) begin

    if (!reset_n) begin

        counter <= 0;

        clk_out <= 0;

    end

    else begin

        if (counter >= (DIVISOR/2 - 1)) begin

            counter <= 0;

            clk_out <= ~clk_out;

        end

        else begin

            counter <= counter + 1;

        end

    end

end

  

endmodule

消抖模块

module debounce(

    input clk,

    input reset_n,

    input key_in,

    output reg key_pulse

);

  

reg [19:0] count;  // 20ms 消抖计数器 (50MHz时钟下,1ms=50000个周期)

reg key_in_sync1, key_in_sync2;

reg key_stable;

  

always @(posedge clk or negedge reset_n) begin

    if (!reset_n) begin

        key_in_sync1 <= 1'b1;

        key_in_sync2 <= 1'b1;

        key_stable <= 1'b1;

        count <= 20'd0;

        key_pulse <= 1'b0;

    end

    else begin

        // 同步输入信号

        key_in_sync1 <= key_in;

        key_in_sync2 <= key_in_sync1;

         

        // 检测按键状态变化

        if (key_in_sync2 != key_stable) begin

            key_stable <= key_in_sync2;

            count <= 20'd0;

        end

        else if (count < 20'd1000000) begin  // 20ms (50MHz * 0.02s)

            count <= count + 1'b1;

        end

        else begin

            key_pulse <= key_in_sync2 & ~key_stable;  // 产生上升沿脉冲

        end

    end

end

  

endmodule



暂停模块

module pause_control(

    input clk,

    input reset_n,

    input pause_pulse,

    output reg pause_toggle

);

  

always @(posedge clk or negedge reset_n) begin

    if (!reset_n) begin

        pause_toggle <= 1'b0;

    end

    else if (pause_pulse) begin

        pause_toggle <= ~pause_toggle;

    end

end

endmodule

计数器模块

module top(

    input clk_50m,         // 50MHz 时钟输入

    input reset_n,         // 复位按键 (低电平有效)

    input pause_key,       // 暂停/继续按键

    output [6:0] hex0,     // 秒个位数码管

    output [6:0] hex1,     // 秒十位数码管

    output [6:0] hex2,     // 分个位数码管

    output [6:0] hex3,     // 分十位数码管

    output [7:0] ledg      // LED 显示秒数 (可选)

);

  

// 内部信号定义

wire clk_1hz;             // 1Hz 时钟

wire pause_pulse;         // 消抖后的暂停脉冲信号

wire pause_toggle;        // 暂停状态信号

wire [3:0] sec_ones;      // 秒个位

wire [3:0] sec_tens;      // 秒十位

wire [3:0] min_ones;      // 分个位

wire [3:0] min_tens;      // 分十位

  

// 时钟分频模块

clock_divider #(

    .DIVISOR(50000000)    // 50MHz -> 1Hz

) clk_div (

    .clk_in(clk_50m),

    .reset_n(reset_n),

    .clk_out(clk_1hz)

);

// 按键消抖模块

debounce debounce_pause (

    .clk(clk_50m),

    .reset_n(reset_n),

    .key_in(pause_key),

    .key_pulse(pause_pulse)

);

  

// 暂停控制模块 (将脉冲信号转换为 toggle 信号)

pause_control pause_ctrl (

    .clk(clk_50m),

    .reset_n(reset_n),

    .pause_pulse(pause_pulse),

    .pause_toggle(pause_toggle)

);

  

// 分秒计数器模块

min_sec_counter counter (

    .clk(clk_1hz),

    .reset_n(reset_n),

    .pause(pause_toggle),

    .sec_ones(sec_ones),

    .sec_tens(sec_tens),

    .min_ones(min_ones),

    .min_tens(min_tens)

);

// 数码管显示模块

seven_seg_display seg0 (.digit(sec_ones), .seg(hex0));

seven_seg_display seg1 (.digit(sec_tens), .seg(hex1));

seven_seg_display seg2 (.digit(min_ones), .seg(hex2));

seven_seg_display seg3 (.digit(min_tens), .seg(hex3));

  

  

endmodule

显示模块

module seven_seg_display(

    input [3:0] digit,

    output reg [6:0] seg

);

  

always @(*) begin

    case (digit)

        4'h0: seg = 7'b1000000;  // 0

        4'h1: seg = 7'b1111001;  // 1

        4'h2: seg = 7'b0100100;  // 2

        4'h3: seg = 7'b0110000;  // 3

        4'h4: seg = 7'b0011001;  // 4

        4'h5: seg = 7'b0010010;  // 5

        4'h6: seg = 7'b0000010;  // 6

        4'h7: seg = 7'b1111000;  // 7

        4'h8: seg = 7'b0000000;  // 8

        4'h9: seg = 7'b0010000;  // 9

        default: seg = 7'b1111111;  // 全灭

    endcase

end

endmodule

顶层模块

module top(

    input clk_50m,         // 50MHz 时钟输入

    input reset_n,         // 复位按键 (低电平有效)

    input pause_key,       // 暂停/继续按键

    output [6:0] hex0,     // 秒个位数码管

    output [6:0] hex1,     // 秒十位数码管

    output [6:0] hex2,     // 分个位数码管

    output [6:0] hex3,     // 分十位数码管

    output [7:0] ledg      // LED 显示秒数 (可选)

);

  

// 内部信号定义

wire clk_1hz;             // 1Hz 时钟

wire pause_pulse;         // 消抖后的暂停脉冲信号

wire pause_toggle;        // 暂停状态信号

wire [3:0] sec_ones;      // 秒个位

wire [3:0] sec_tens;      // 秒十位

wire [3:0] min_ones;      // 分个位

wire [3:0] min_tens;      // 分十位

  

// 时钟分频模块

clock_divider #(

    .DIVISOR(50000000)    // 50MHz -> 1Hz

) clk_div (

    .clk_in(clk_50m),

    .reset_n(reset_n),

    .clk_out(clk_1hz)

);

// 按键消抖模块

debounce debounce_pause (

    .clk(clk_50m),

    .reset_n(reset_n),

    .key_in(pause_key),

    .key_pulse(pause_pulse)

);

  

// 暂停控制模块 (将脉冲信号转换为 toggle 信号)

pause_control pause_ctrl (

    .clk(clk_50m),

    .reset_n(reset_n),

    .pause_pulse(pause_pulse),

    .pause_toggle(pause_toggle)

);

  

// 分秒计数器模块

min_sec_counter counter (

    .clk(clk_1hz),

    .reset_n(reset_n),

    .pause(pause_toggle),

    .sec_ones(sec_ones),

    .sec_tens(sec_tens),

    .min_ones(min_ones),

    .min_tens(min_tens)

);

// 数码管显示模块

seven_seg_display seg0 (.digit(sec_ones), .seg(hex0));

seven_seg_display seg1 (.digit(sec_tens), .seg(hex1));

seven_seg_display seg2 (.digit(min_ones), .seg(hex2));

seven_seg_display seg3 (.digit(min_tens), .seg(hex3));

  

  

endmodule

顶层模块是整个分秒计数器系统的集成中心,它负责协调各个子模块的工作,实现从时钟输入到数码管显示的完整功能链路。

编译无误过后就可以通过Quartus 烧录了。

实物效果

分秒计数器

总结

通过这次分秒计时器的设计实践,我对数字电路的模块化设计和状态机思想有了更深刻的理解。模块化设计将复杂系统分解为分频、消抖、暂停控制、计数器和显示等独立模块,不仅降低了设计难度,还提高了代码的可维护性和可扩展性。状态机的应用则通过定义IDLE和RUNNING两种状态,清晰地实现了计时器的启动和暂停功能。此外,硬件设计中的细节处理,如按键消抖和七段数码管的显示逻辑,让我认识到细节对系统稳定性的重要性。通过延时采样和段码转换,我解决了抖动和显示错误的问题,确保了计时器的准确性和可靠性。

这次实践让我更加体会到硬件设计是一个不断试错和优化的过程。从分频模块的调试到显示模块的优化,每一步都需要反复测试和调整。通过逐步集成和模块化测试,我不仅成功解决了多个技术难题,还积累很多经验。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2329620.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

小白 thingsboard 拆分前后端分离

1、modules 里注释掉ui_ugx <modules><module>netty-mqtt</module><module>common</module><module>rule-engine</module><module>dao</module><module>edqs</module><module>transport</module&g…

4G专网:企业数字化转型的关键通信基石

4G专网 在数字化转型的浪潮下&#xff0c;企业对高可靠性、低时延、安全可控的通信网络需求日益增长。传统的公用蜂窝网络难以满足企业在工业自动化、能源管理、智慧城市等领域的特殊需求&#xff0c;因此4G专网成为众多行业的优先选择。作为行业领先的移动核心网提供商&#x…

基于FLask的共享单车需求数据可视化分析系统

【FLask】基于FLask的共享单车需求数据可视化分析系统 &#xff08;完整系统源码开发笔记详细部署教程&#xff09;✅ 目录 一、项目简介二、项目界面展示三、项目视频展示 一、项目简介 该系统能够整合并处理大量共享单车使用数据&#xff0c;通过直观的可视化手段&#xff0…

STL 性能优化实战:解决项目中标准模板库的性能瓶颈

&#x1f9d1; 博主简介&#xff1a;CSDN博客专家、全栈领域优质创作者、高级开发工程师、高级信息系统项目管理师、系统架构师&#xff0c;数学与应用数学专业&#xff0c;10年以上多种混合语言开发经验&#xff0c;从事DICOM医学影像开发领域多年&#xff0c;熟悉DICOM协议及…

ES使用聚合aggregations实战(自用:2025.04.03更新)

ES使用聚合aggregations实战 聚合模板桶聚合&#xff1a;Bucket Aggregations指标聚合&#xff1a;Metrics Aggregations管道聚合&#xff1a;Pipeline Aggregations嵌套聚合日期直方图&#xff1a;date-histogram 接口实战接口一&#xff1a;根据stu_id分组统计时间段内的各个…

AI Agent设计模式四:Evaluator

概念 &#xff1a;质量验证与反馈机制 ✅ 优点&#xff1a;自动化质量检查&#xff0c;实现持续优化闭环❌ 缺点&#xff1a;评估准确性依赖模型能力 from typing import TypedDict from langchain_openai import ChatOpenAI from langgraph.graph import StateGraph, START, …

AI绘画中的LoRa是什么?

Lora是一个多义词&#xff0c;根据不同的上下文可以指代多种事物。以下将详细介绍几种主要的含义&#xff1a; LoRa技术 LoRa&#xff08;Long Range Radio&#xff09;是一种低功耗广域网&#xff08;LPWAN&#xff09;无线通信技术&#xff0c;以其远距离、低功耗和低成本的特…

Linux网络:数据链路层以太网

目录 认识数据链路层关于以太网1. 基本概念2. 以太网帧格式3. MAC vs IP 认识数据链路层 数据链路层 位于物理层和网络层之间&#xff0c;其作用是将源自物理层来的数据可靠地传输到相邻节点的目标主机的网络层&#xff0c;主要通过物理介质(如以太网&#xff0c;Wi-Fi等)将数…

MySQL基础 [一] - 数据库基础

目录 什么是数据库 站在服务器角度理解 站在用户角度理解 为什么不直接使用文件存储呢&#xff1f; 主流数据库 MySQL的基本使用 数据库的使用样例 服务器管理 服务器数据库表之间的关系 MySQL的架构 MySQL语句分类 存储引擎 查看存储引擎 存储引擎对比 什么…

【华为OD技术面试真题 - 技术面】- Java面试题(17)

华为OD面试真题精选 专栏:华为OD面试真题精选 目录: 2024华为OD面试手撕代码真题目录以及八股文真题目录 文章目录 华为OD面试真题精选虚拟机分区1. **虚拟磁盘分区**2. **虚拟机的内存分区**3. **CPU分配**4. **虚拟网络分区**5. **存储虚拟化和分区**6. **虚拟机分区管理**…

#Linux内存管理# 在32bit Linux中,内核空间的线性映射的虚拟地址和物理地址是如何换算的?

在32位Linux系统中&#xff0c;内核空间的线性映射&#xff08;也称为直接映射或低端内存映射&#xff09;采用固定的偏移量进行虚拟地址和物理地址的换算。以下是详细的转换规则及背景知识&#xff1a; 1. 32位Linux内存布局 用户空间&#xff1a;虚拟地址 0x00000000 到 0x…

006贪心——算法备赛

跨步问题 跳跃游戏|| 问题描述 给定一个长度为 n 的 0 索引整数数组 nums。初始位置为 nums[0]。 每个元素 nums[i] 表示从索引 i 向后跳转的最大长度。换句话说&#xff0c;如果你在 nums[i] 处&#xff0c;你可以跳转到任意 nums[i j] 处: 0 < j < nums[i]i j &…

pytorch中Dropout

Dropout 是一种常用的正则化技术&#xff0c;用于防止神经网络过拟合。PyTorch 提供了 nn.Dropout 层来实现这一功能。 基本用法 torch.nn.Dropout(p0.5, inplaceFalse) 参数说明&#xff1a; p (float): 每个元素被置为0的概率&#xff08;默认0.5&#xff09; inplace (b…

【玩泰山派】2、制作buildroot镜像,并烧录

文章目录 前言制作buildroot镜像过程搭建环境&#xff08;docker版&#xff09;下载泰山派开发的sdk利用制作的镜像和下载的sdk去启动开发docker容器编译buildroot镜像 参考 前言 泰山派官方提供了不少现成的镜像 但是都买了泰山派了&#xff0c;肯定是想自己编译折腾下&…

初阶数据结构--树

1. 树的概念与结构 树是⼀种⾮线性的数据结构&#xff0c;它是由 n&#xff08;n>0&#xff09; 个有限结点组成⼀个具有层次关系的集合。把它叫做 树是因为它看起来像⼀棵倒挂的树&#xff0c;也就是说它是根朝上&#xff0c;⽽叶朝下的。 有⼀个特殊的结点&#xff0c;称…

安装gpu版本的dgl

1.先去网址&#xff0c;找到对应版本的dgl,然后下载到本地。 dgl-whl下载地址 我的是python 3.8 &#xff0c;cuda 11.6. windows 2.在虚拟环境里 输入 pip install E:\dgl-1.0.2cu116-cp38-cp38-win_amd64.whl &#xff08;因为我下载到E盘里了&#xff09; 这样GPU版本的d…

5天速成ai agent智能体camel-ai之第1天:camel-ai安装和智能体交流消息讲解(附源码,零基础可学习运行)

嗨&#xff0c;朋友们&#xff01;&#x1f44b; 是不是感觉AI浪潮铺天盖地&#xff0c;身边的人都在谈论AI Agent、大模型&#xff0c;而你看着那些密密麻麻的代码&#xff0c;感觉像在读天书&#xff1f;&#x1f92f; 别焦虑&#xff01;你不是一个人。很多人都想抓住AI的风…

FPGA——FPGA状态机实现流水灯

一、引言 在FPGA开发中&#xff0c;状态机是一种重要的设计工具&#xff0c;用于处理具有时间顺序的事件。本文将详细介绍如何使用状态机实现一个LED流水灯的效果。 二、状态机概述 状态机&#xff08;FSM&#xff09;是一种行为模型&#xff0c;用于表示系统在不同状态下的…

晶晨S905-S905L-S905LB_S905M2通刷_安卓6.0.1_16S极速开机_线刷固件包

晶晨S905-S905L-S905LB_S905M2通刷_安卓6.0.1_16S极速开机_线刷固件包 线刷方法&#xff1a;&#xff08;新手参考借鉴一下&#xff09; 刷机工具版本请用2.2.0以上&#xff0c;导入固件后&#xff0c;刷机工具右侧两个擦除打勾&#xff0c;然后点开始。插上刷机神器&#xf…

构建第一个ArkTS应用:Hello World之旅

# 构建第一个ArkTS应用&#xff1a;Hello World之旅 在鸿蒙应用开发的领域中&#xff0c;ArkTS语言为我们提供了强大而便捷的开发方式。今天&#xff0c;就让我们一起踏上构建第一个ArkTS应用——Hello World的奇妙旅程。 ## 一、创建ArkTS工程 1. 首先&#xff0c;我们要使用…