基于74系列芯片与Quartus II的数字钟设计与FPGA实现
1. 从零开始:为什么选择74系列芯片和FPGA来做数字钟?
很多刚接触数字电路的朋友,可能会觉得数字钟是个“老掉牙”的项目,网上代码一抓一大把,用单片机分分钟就能搞定。没错,用Arduino或者STM32,配合一个RTC时钟芯片,可能半小时就能让数码管亮起来。但如果你正处在数字电路、EDA工具或者FPGA的入门阶段,那我强烈建议你亲手用74系列芯片在Quartus II里搭一遍。这绝对不是走弯路,而是一次“知其所以然”的绝佳实战。
我自己带过不少学生,发现一个普遍问题:大家学了一堆门电路、触发器、计数器的理论知识,但一到实际设计,脑子里还是一团浆糊。怎么把一个个独立的芯片连接成一个能协同工作的系统?时钟信号怎么传递?进位信号如何处理?这些光看书是看不明白的。而“基于74系列芯片的数字钟设计”就是一个完美的桥梁项目。它规模适中,涵盖了组合逻辑(译码器、数据选择器)、时序逻辑(计数器)和综合应用(动态扫描),能让你把书本上的知识点真正串起来。
那么,为什么还要扯上FPGA呢?这就是传统与现代的结合了。过去,我们得在面包板或万能板上,用真实的74HC190、74LS48这些芯片,配上电阻电容,一根线一根线地焊,调试起来那叫一个痛苦,接触不良、芯片烧毁是家常便饭。现在,我们有了FPGA和Quartus II这样的EDA工具。你不需要真实的物理芯片,而是在软件里调用它们的“虚拟模型”(符号),用画原理图的方式完成设计,然后通过仿真验证逻辑,最后下载到一块FPGA开发板上。FPGA内部的可编程逻辑单元就模拟了那一堆74系列芯片的功能。这大大降低了硬件门槛和成本,让你能专注于逻辑设计本身,快速迭代和调试。
所以,这个项目的核心价值在于:用经典的、你熟悉的74系列芯片作为设计思路,借助现代EDA工具和FPGA平台,高效、无风险地完成一个完整的数字系统设计流程。 你会经历从原理图输入、功能仿真、引脚分配、到硬件下载调试的全过程,这对你以后做更复杂的FPGA设计,有莫大的好处。下面,我就带你一步步走通它。
2. 核心芯片“三剑客”:74190、74161与七段译码器
要搭好数字钟,我们得先认识几位“老朋友”。整个系统的核心是计时,而计时的核心就是计数器。原始资料里提到了74190或74191,这是非常经典的可逆计数器。但根据我的经验,在做单纯的递增计数时,74161(四位二进制同步计数器)可能更直观、更常用。别担心,我会把两种方案都给你讲明白,你可以根据自己的理解或实验要求来选择。
2.1 计数器:选择74190还是74161?
先说74190。它是一个“十进制可逆计数器”,意思是它能从0数到9,并且既能加也能减。对于我们24小时制的时钟(分钟和秒是00-59,小时是00-23),我们需要的是模60和模24的计数器。直接用74190做模60有点别扭,因为它本身是十进制的。通常的做法是:用两片74190级联,一片设为十进制(0-9),另一片设为六进制(0-5),这样就能组成一个60进制的计数器。具体怎么设?这就涉及到它的控制引脚了。
74190有几个关键引脚:CLK(时钟输入)、UP/DOWN(加减控制,高电平加,低电平减)、LOAD(并行置数使能,低有效)、A/B/C/D(并行数据输入)。让它工作在计数模式,通常把LOAD和CTEN(计数使能,低有效)接高电平(无效),UP/DOWN接高电平(加法计数)。那怎么让它数到9(或5)就自动清零并产生进位呢?这就需要利用它的MAX/MIN(最大/最小输出)和RCO(行波时钟输出)引脚。RCO会在计数器达到最大值(加法时是9)时输出一个低电平脉冲,这个脉冲正好可以作为下一级芯片的时钟信号或进位信号。通过将RCO反馈到LOAD,并提前在A/B/C/D数据输入端设置好要装载的值(比如0),就能实现自动归零,形成任意进制。
再来看74161。它是一个“四位二进制同步计数器”,能直接从0数到15。用它来构造任意进制计数器,逻辑更清晰,也是教科书上的标准方法。比如我们要一个模6的计数器(0-5),就用74161从0开始计数,当它计到5(二进制0101)的时候,我们通过一个与非门检测这个状态(Q1=1, Q3=1),产生一个低电平信号,送到74161的同步清零端CLR(低有效),这样下一个时钟上升沿到来时,计数器就被清零,从0重新开始。模10、模24都可以用类似的状态检测+反馈清零或置数的方法来实现。
我个人的建议是:如果你是第一次做,想更清晰地理解任意进制计数器的设计原理,用74161配合反馈清零法,思路更直接。 如果你想让电路更贴近“传统”的十进制累加思维,或者实验指定要用74190,那就用后者。两者都能实现,只是实现路径略有不同。
2.2 显示驱动:七段译码器与动态扫描
计数器输出的的是BCD码(四位二进制表示一位十进制数),我们要把它变成数码管上能显示的段码,这就需要七段译码器,比如7448或74LS47。这两者略有区别:7448输出高电平有效,用于驱动共阴极数码管;74LS47输出低电平有效,用于驱动共阳极数码管。你需要根据自己FPGA开发板上的数码管类型来选择。在Quartus的符号库中,它们通常就叫7448或74147,用法很简单,直接把计数器的四个输出Qa, Qb, Qc, Qd (或 QA, QB, QC, QD) 接到译码器的A, B, C, D输入端,译码器的7个输出端(a, b, c, d, e, f, g)就直接对应数码管的7个段。
现在问题来了:我们有4位数码管(小时两位、分钟两位),但译码器输出只有一套。怎么用一套电路驱动4个数码管?答案就是动态扫描。这是节省FPGA引脚资源、降低功耗的经典方法。我们需要一个额外的芯片:数据选择器(MUX),比如四选一数据选择器74153或74157。
动态扫描的原理是这样的:我们让4个数码管轮流点亮,由于人眼的视觉暂留效应,只要轮流的速度足够快(比如每秒扫描50次以上),看起来就像是同时亮着的。具体实现时,我们用两个二进制位(比如来自一个简单的2位计数器)作为74153的选择端S0, S1。74153有4组数据输入,我们分别接入个位分钟、十位分钟、个位小时、十位小时的BCD码。选择端S0, S1循环变化(00, 01, 10, 11),就能依次将4个BCD码选通到输出端。这个输出再接到一个共用的7448译码器上,译码器输出段信号给所有数码管。
那怎么控制哪个数码管亮呢?这就需要位选信号。同样根据S0, S1的状态,经过一个2-4译码器(比如74139),产生4个位选控制信号,每个信号在对应的时刻为有效电平(共阴极为低,共阳极为高),去打开对应的数码管。这样,段码和位选信号同步切换,就实现了动态显示。在Quartus里画这个部分时,你会对“时分复用”有非常直观的理解。
3. 在Quartus II中“搭积木”:原理图输入实战详解
理论说再多,不如动手画一遍。打开你的Quartus II(我这里以13.1版本为例,其他版本大同小异),我们开始“搭积木”。
3.1 创建项目与添加原理图文件
首先,File -> New Project Wizard,给你的项目起个名字,比如Digital_Clock,选好项目存放路径。在“Add Files”那一步,因为我们还没有文件,直接跳过。在“Family & Device Settings”这一步,这是关键! 你必须选择与你手头FPGA开发板型号一致的器件系列和具体型号。比如你用Altera的Cyclone IV EP4CE6系列,就找到并选中它。选错了会导致最后无法下载。
项目创建好后,File -> New,选择Design Files下的Block Diagram/Schematic File,这就是我们的原理图编辑窗口。
3.2 放置元件与连线
在原理图空白处双击,或者点击工具栏的符号工具,会弹出元件库窗口。Quartus的库非常丰富,我们需要的74系列芯片都在megafunctions库之外的基本库里,通常直接在Name框里输入芯片型号,比如74161,就能找到。如果找不到,试试输入74161n(带后缀的)。同样地,放置7448、74153、AND2(与门)、NOT(非门)、INPUT(输入引脚)、OUTPUT(输出引脚)等。
画图技巧分享:
- 模块化设计:别把所有东西都画在一张图上。可以分别建立三个原理图文件(
.bdf):clock_divider.bdf(分频器)、counter_module.bdf(计数模块)、display_driver.bdf(显示驱动)。然后在顶层的原理图文件中,将它们作为三个“符号”(Symbol)调用。这就像编程中的函数,让顶层图非常清晰。创建符号的方法是:画好一个.bdf文件后,File -> Create/Update -> Create Symbol Files for Current File。 - 连线要规整:使用总线(Bus)功能。比如计数器有4位输出
Q[3..0],你可以用一条粗线(总线)来表示,右键点击线,选择Properties,给它命名,比如min_low[3..0]。连接到单个引脚时,需要引出分支线,并给分支线命名相同的网络名,Quartus会自动识别连接。这比画4根细线清爽多了。 - 用好网络标签(Net Label):对于需要远距离连接的信号,不必拉一根长长的线穿过整个图纸。在线的端点放置一个网络标签(比如
clk_1Hz),在另一个需要连接的地方也放置一个同名的网络标签,它们就在逻辑上连接起来了。这是保持图纸整洁的必备技能。
一个具体例子:用两片74161构建分钟计数器(模60)
- 放第一片74161,作为个位计数器(0-9)。将其
CLK接时钟信号(比如1Hz的秒脉冲),CLR接高电平(先不用清零),LOAD接高电平(不用置数),ENT和ENP接高电平(使能计数)。 - 我们的目标是让它计到9(1001)时,自己清零并给十位一个进位。所以,我们需要检测
Q0和Q3同时为1的状态。放一个二输入与门AND2,输入端接Q0和Q3。与门输出接一个非门NOT,非门的输出接到74161的同步清零端CLR。这样,当计到9时,CLR变低,下一个时钟上升沿,个位清零。 - 同时,这个“计到9”的信号(与门输出)也可以作为十位计数器的进位使能信号。因为个位每计满10次(0-9),十位才需要加1。
- 放第二片74161,作为十位计数器(0-5)。它的
CLK同样接那个1Hz时钟。它的使能端ENT和ENP接哪里?这里有个关键:我们希望十位只在个位从9跳回0时才计数。所以,把第一步中与门的输出(个位计满信号)接到十位的ENT或ENP上(通常接ENT)。这样,只有当个位计满时,十位才在下一个时钟沿有效。 - 十位自己需要实现模6计数。检测状态5(二进制0101),即
Q0=1, Q2=1。再用一个与门检测这个状态,反馈到十位自己的CLR端。 - 这样,一个完整的模60计数器就搭好了。小时计数器(模24)原理完全一样,只是反馈检测的状态变成23(十进制)或0010 0011(BCD码)。
3.3 引脚分配(Pin Planner)
原理图画完,编译通过后,最重要的一步就是引脚分配。你的时钟信号、数码管段选、位选信号,都需要对应到FPGA芯片真实的物理引脚上。点击Assignments -> Pin Planner,会打开一个表格。在表格的“Node Name”列,你会看到所有输入输出信号。在“Location”列,为每个信号指定一个引脚号。
如何知道引脚号? 这完全取决于你的FPGA开发板。你必须找到开发板的原理图或用户手册,上面会标明:哪个按键接在哪个FPGA引脚(作为时钟或复位),数码管的段a、b、c...以及位选1、2、3、4分别接在哪些引脚上。比如,开发板上的50MHz晶振可能接在PIN_E1,你就把clk_50M这个输入信号分配到PIN_E1。数码管的段a接PIN_G1,你就把seg_a输出信号分配到PIN_G1。这一步绝对不能错,错了要么不工作,要么烧坏芯片。 分配好后,关闭Pin Planner,重新全编译一次。
4. 让电路“跑起来”前先“仿个真”:ModelSim仿真验证
原理图编译通过,只代表语法和基本连接没问题,不代表逻辑行为正确。直接下载到板子上,如果显示乱跳或者根本不计数,调试起来会很盲目。所以,仿真(Simulation)是数字设计里省时省力的“神器”。我们用Quartus自带的仿真工具或者ModelSim来验证。
4.1 创建测试波形文件(Vector Waveform File)
在Quartus中,File -> New,选择University Program VWF(或者Vector Waveform File)。这会打开一个波形编辑器。在左边空白处双击,添加你需要观察的节点(Nodes)。通常包括:主时钟clk_50M、分频后的秒时钟clk_1Hz、各个计数器的输出(如hour_ten[3..0], hour_one[3..0], min_ten[3..0], min_one[3..0])、以及最终的数码管段选seg[6..0]和位选dig[3..0]信号。
4.2 设置输入激励
我们需要给电路输入信号。对于数字钟,主要的输入就是高频时钟(比如50MHz)和复位信号。
- 选中
clk_50M信号,在左侧工具栏点击时钟信号图标,设置时钟周期。50MHz的周期是20ns,你可以设置一个值,比如Period=20ns,Duty cycle=50%。 - 设置复位信号
rst_n(假设低电平复位)。在开始的一小段时间(比如前100ns)内,让它为低电平,然后拉高。 - 对于分频器产生的
clk_1Hz,我们不需要设置,它是电路内部自己产生的,我们只观察。
4.3 运行仿真与结果分析
设置好仿真时间(比如模拟10秒钟的真实时间),然后点击Processing -> Start Simulation。仿真工具会基于你画的原理图,计算每个信号在每一个时刻的值,并以波形图显示出来。
怎么看波形?
- 看计时逻辑:找到
clk_1Hz信号,它应该是一个周期为1秒的方波。观察min_one[3..0],它应该随着每个clk_1Hz的上升沿,从0递增到9,然后回0,同时min_ten[3..0]加1。验证分钟是否在59后能正确跳回00,并触发小时加1。验证小时是否在23:59后能正确跳回00:00。这是最基本的计时功能验证。 - 看显示逻辑:观察
dig[3..0]这四个位选信号,它们应该是在高速循环扫描,比如dig0、dig1、dig2、dig3依次为有效电平,周期很短(通常在毫秒级)。同时,seg[6..0]段码信号应该随着dig信号的变化,同步切换成对应数码管要显示的数字的编码。你可以把seg信号显示格式改成“七段显示”(Seven-Segment Display),这样波形上就能直接看到数字,非常直观。
仿真踩坑提醒:
- 如果仿真结果全是红色(不定态
X),很可能是因为你的电路有未初始化的触发器,或者形成了组合逻辑环路。检查所有触发器的复位/置位端是否接了确定的电平(上拉或下拉)。 - 如果计数不规律,检查进位逻辑。特别是使用反馈清零法时,要确保清零信号只持续一个非常短的时间(通常就是一个时钟周期的一部分),否则计数器可能一直处于清零状态。在波形图里放大看清零信号和时钟沿的关系。
- 仿真通过,是下载成功的最重要保障。我习惯是仿真完全没问题了,才去连接板子。
5. 下载与硬件调试:让数字钟在FPGA上“活”起来
仿真完美,引脚分配确认无误,就可以进行最后一步了:把设计“烧录”进FPGA。
5.1 连接开发板与程序下载
用USB-Blaster或其他下载器,连接电脑和FPGA开发板,给开发板上电。在Quartus中,点击Tools -> Programmer。如果第一次使用,可能需要Hardware Setup,选择你的下载器型号(如USB-Blaster)。然后,在Programmer窗口里,应该会自动加载你当前项目编译生成的.sof文件(SRAM Object File,掉电丢失)。
.sof和.jic文件区别:
.sof文件是直接配置到FPGA的SRAM中的,下载速度快,但一掉电,程序就没了。适合调试阶段。.jic文件是通过Quartus转换生成的,可以烧写到FPGA板载的配置芯片(如EPCS)里。这样每次上电,配置芯片都会把程序加载到FPGA,实现“固化”。等你的设计完全稳定后,再生成并烧写.jic文件。
点击Start按钮,进度条走完,看到“Success”提示,程序就下载完成了。
5.2 硬件调试实战技巧
下载成功,但数码管可能不亮,或者显示乱码,别慌,硬件调试才是真正长经验的环节。
- 电源与时钟检查:首先确认开发板供电正常,电源指示灯亮。用示波器或开发板上的LED(如果有的话)检查你的主时钟(如50MHz)是否已经引入到FPGA引脚。如果时钟都没有,后面一切免谈。
- 复位信号检查:如果你的设计有复位按键,按下复位键,观察计数器是否归零。如果没有复位键,确保你的设计在仿真时已经验证了上电后的初始状态是正常的。
- 显示问题排查:
- 全不亮:先检查位选信号。用示波器探头测量
dig[3..0]这几个引脚,应该能看到周期性的脉冲波形。如果没有,可能是你的动态扫描模块根本没工作,或者引脚分配错了。如果有波形,但数码管还是不亮,可能是数码管共阴/共阳接法与你用的译码器(7448/74LS47)不匹配,或者驱动电流不足(FPGA引脚直接驱动可能电流不够,需要加三极管或驱动芯片)。 - 显示乱码/重叠:这是典型的动态扫描时序问题。用示波器同时看一个段选信号(如
seg_a)和一个位选信号(如dig0)。理想情况下,当dig0有效时,seg_a应该根据个位分钟的值稳定输出高或低电平。如果你发现seg_a在dig0有效期间还在快速变化,说明你的段码数据切换得太快了,可能是在dig信号变化的同时,MUX的选择信号也在变化,但两者没有对齐,存在竞争冒险。解决办法是,确保控制MUX选择端的计数器时钟,和产生位选信号的计数器时钟,是同一个时钟,且没有额外的延迟逻辑。 - 亮度不均:某个数码管特别暗或特别亮。暗是因为该位数码管的位选信号有效时间太短,或者驱动电流小;亮是因为有效时间太长。调整动态扫描的时钟频率(通常1KHz到10KHz之间比较合适),并确保4个位选信号的有效占空比一致。
- 全不亮:先检查位选信号。用示波器探头测量
- 计时不准:这是分频器的问题。你的基准时钟是50MHz,要分频出1Hz,分频系数是50,000,000。这个计数器如果没设计对,误差会很大。建议在仿真时,多跑一些时钟周期,对比实际计时和仿真时间是否吻合。在硬件上,可以用秒表对比一分钟,看看误差有多大。如果误差明显,检查分频计数器是否在仿真中出现了漏计数或错计数。
调试过程就是这样一个“观察现象 -> 推测问题 -> 验证修改”的循环。遇到问题,回头去看仿真波形,往往能找到线索。我在第一次做的时候,就因为位选和段码切换不同步,调试了一下午。后来在波形图里放大看,才发现两个信号边沿对不齐,加了个触发器打了一拍就解决了。所以,耐心和细致的观察是关键。
6. 从功能到优化:让数字钟更实用、更稳定
基本的计时和显示功能实现后,你可以在这个基础上添加更多实用功能,这会让你的项目从“实验”升级到“作品”。
6.1 添加时间设置与闹钟功能
这需要引入外部按键输入。假设我们用三个按键:KEY_mode(模式切换)、KEY_up(加)、KEY_down(减)。
- 模式切换逻辑:设计一个2位状态机。状态
00:正常计时模式。状态01:设置小时模式(此时小时数字闪烁)。状态10:设置分钟模式(此时分钟数字闪烁)。按KEY_mode键在不同模式间循环切换。 - 按键消抖:机械按键按下时会产生一段时间的抖动(几十毫秒),必须处理,否则一次按键会被误认为多次。最简单的办法是用一个20ms的定时器检测按键稳定状态。在Quartus里,你可以用计数器实现一个20ms的定时使能信号,然后用这个使能信号去采样按键状态。
- 设置逻辑:在设置模式下,相应的计数器(小时或分钟)应停止从
clk_1Hz计数,转而接收KEY_up或KEY_down按键产生的脉冲进行加减。同时,为了让用户知道在设置,可以让对应的数码管以1Hz频率闪烁(即用另一个1Hz信号控制位选信号的使能)。 - 闹钟功能:可以再增加一个闹钟时间寄存器(同样由小时和分钟计数器构成)。在正常计时模式下,不断将当前时间与闹钟时间比较,相等时,驱动一个蜂鸣器或LED闪烁。同样需要增加设置闹钟时间的模式。
6.2 提高系统稳定性与可靠性
- 全局时钟与复位网络:一个稳健的数字系统必须有干净的全局时钟和可靠的复位。在FPGA设计中,外部晶振输入的时钟信号需要通过芯片内部的全局时钟网络(Global Clock Network)布线到各个触发器,以减少时钟偏斜。在原理图中,你可以用
ALTCLKCTRL(Altera的时钟控制IP核)来接入全局时钟。复位信号也最好做同步处理,即用系统时钟打两拍,产生一个同步复位信号,避免异步复位引起的亚稳态问题。 - 时序约束:这是进阶内容,但对于保证高速或复杂设计稳定工作至关重要。你可以通过
Assignments -> Timing Analyzer进行简单的约束,比如告诉工具你的主时钟频率是50MHz。这样Quartus在布局布线时会尽量满足这个时序要求,避免建立/保持时间违规。 - 资源优化:当你添加了更多功能后,可能会收到“资源不足”的编译错误。这时可以查看编译报告的“Flow Summary”部分,了解用了多少逻辑单元(LE)、寄存器、内存。优化方法包括:复用一些逻辑模块;如果显示部分用了很多组合逻辑,可以考虑用时序逻辑(寄存器)来输出段码和位选,让工具更容易优化。
这个基于74系列芯片的数字钟项目,就像学习游泳时在泳池里的练习。它用最经典的元件,让你理解了数字系统最底层的构建逻辑。而Quartus II和FPGA,则是给你的泳池加上了现代化的过滤系统和安全员,让你能更安全、更高效地练习。当你熟练掌握了从设计、仿真到下载调试的完整流程后,再去学习用Verilog或VHDL这些硬件描述语言来做设计,你会发现思路异常清晰,因为你知道你写的每一行代码,最终对应的是什么样的电路结构。这才是这个经典实验在今天的真正价值所在。
更多推荐



所有评论(0)