ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

触发器与计数器:从双稳态本质到工程实践

触发器与计数器:从双稳态本质到工程实践 学数字电路的时候我一度觉得触发器是莫名其妙冒出来的东西。前面组合逻辑学得好好的与门、或门、非门算清楚门路突然冒出一堆RS、D、JK、T还扯什么CP、上升沿、下降沿……看例题会做一到项目里就不知道怎么选。直到后来自己搭计数器做分频把LED秒闪、按键计数、数码管显示整个串起来才意识到触发器才是数字系统里真正“干活”的角色而计数器/分频器就是触发器最典型的应用场景。这篇就把这块内容按我自己的学习路径重新捋一遍从触发器的双稳态本质讲起一直到门电路搭D触发器、二分频、模N计数器最后落到单片机按键计数和FPGA里的Verilog计数器全程按一个能复现的实验思路来写。1. 触发器到底解决了什么问题1.1 组合逻辑的盲区它没有记忆先想一个最基本的问题组合逻辑的输出只取决于当前输入。你给一个与门两个高电平它输出高撤掉一个输入它立刻变低。它不记得刚才发生过什么。可是真实数字系统里绝大多数的功能都依赖“历史状态”计数器的计数值是一路累加上来的状态机下一步往哪走要看上一步在哪显示屏上的数据要一直保持住直到下次更新。这些功能共同需要一个东西——记忆。触发器就是数字电路里最基础的记忆单元。这也解释了为什么触发器总是跟时钟绑在一起。没有时钟约束的记忆是混乱的谁都能写、随时都能改那数据就乱套了。加上时钟之后大家约定在固定的时刻采样输入、更新状态整个系统才有一个统一的节拍。所以触发器在教材里归入“时序逻辑电路”跟组合逻辑的最大区别就是它带状态能记住东西。1.2 双稳态是记忆的最小单元记忆一位二进制信息本质上需要一个“双稳态”结构两个稳定状态分别代表0和1而且外界不干预时状态能一直保持。最经典的做法是把两个反相器首尾相接第一个反相器的输出接第二个的输入第二个的输出再接回第一个的输入。这个环路天然有两个稳定点一端为1另一端为0或者反过来。只要电路上电后有足够的时间建立起这个正反馈状态就锁住了。这就是双稳态。用CMOS工艺实现时一个反相器是两个晶体管一个PMOS一个NMOS两个反相器就是四个晶体管。但大家经常听说的“6管SRAM单元”是在这两个交叉耦合反相器之外再额外加两个存取管作为写入/读出的开关。也就是说纯粹实现双稳态锁存最低四个管子就够了六个管子是为了让外部能可控地往这个环路里写数据。把这个逻辑想明白再看各种触发器电路图就不会被“几个晶体管”这个问题卡住。1.3 RS触发器最原始的存储单元双稳态结构能锁存但没法外部可控地写入。RS触发器干的事就是在双稳态环路基础上把输入信号引进来让外部能强行把状态置成1或0。用两个与非门交叉连接是最常见的RS触发器形式S端加低电平输出Q置1R端加低电平输出Q置0。两个输入都是高电平时电路保持原状态如果S和R同时为低两个输出都被拉高Q和Q非同时为1这既违背互补定义撤销输入后状态也不确定所以这个组合是禁止的。RS触发器是异步的没有时钟输入一变状态就跟着变。在简单的防抖开关、按键锁存电路里还能见到它但作为大规模时序电路的基本单元它有个致命问题无法跟其他电路同步。所以后来才演化出带时钟控制的同步触发器和各种改进型。学习时把RS触发器当成理解触发器工作原理的入口但设计实际电路时现在基本都直接用D触发器或JK触发器不会有人真的从RS开始搭计数器。1.4 触发器家族怎么选教材里常列四种RS、D、JK、T。不少初学者在这里背表格背到头晕实际工程里的选择逻辑其实很简单。D触发器最常用。原因无他接口简单Q端在时钟边沿无条件跟随D端的值。做寄存器、分频器、计数器都顺手。JK触发器功能最完整J置1、K置0、JK同时为1则翻转。当年的TTL芯片74LS76、74LS112里大量用JK因为一个JK能同时实现置位、复位、保持、翻转设计状态机时很方便。T触发器只有保持和翻转两种状态本质上就是模2计数单元专门为计数而生的理论模型。RS触发器现在主要出现在教材和特殊场合。实际选型一句话写Verilog或者画原理图需要“状态保存”时无脑选D做计数器想减少芯片数量且手头有现成JK芯片那用JKT触发器在FPGA里就是一行代码的事不用单独找硬件。至于数据库里那个同名概念“触发器”跟数字电路完全是两码事只在关键字上撞了车学的时候别弄混就行。2. 从门电路到边沿D触发器2.1 电平触发的问题数据会“穿透”先看一个最直接的D锁存器结构用一个传输门或门电路把D信号引到双稳态节点上时钟为高时输入直接穿到输出Q跟着D变时钟为低时切断输入锁存当前值。这种叫电平触发也叫锁存器Latch。它最大的问题在于时钟为高的整个时间段里输出都在跟随输入而不是只在某一个瞬间采样。如果D在时钟高电平期间发生了任意一次变化锁存进去的值可能就是变化之后的那个值。这导致电路对毛刺很敏感。一个典型例子把两个电平触发的锁存器串起来做寄存器组当公共时钟为高时第一个锁存器跟随输入第二个也在跟随第一个的输出整个链路的延迟全都不可控。这种“数据穿透”在工程上会造成竞争冒险轻则出现偶发错误重则整个时序逻辑神秘崩溃。所以现代数字电路设计尤其是FPGA几乎全部采用边沿触发。2.2 主从结构边沿触发的经典思路边沿触发想解决的问题是只在时钟上升沿或下降沿的“那一瞬间”采样D的值其他时间输出保持不变。一个朴素的做法是两个锁存器主锁存器和从锁存器串联时钟相位相反。第一个时钟高电平时主锁存器跟随输入第二个主锁存器的输出这时被从锁存器隔离从锁存器保持旧值。当时钟变低主锁存器切换为保持从锁存器切换为跟随把主锁存器里锁住的值送到输出。因为主锁存器已经在时钟下降沿前把D的值截住了所以整体上表现为“在时钟上升沿时刻对输入采样并更新输出”输出在整个时钟周期内只变化一次。这个主从D触发器结构是理解“边沿触发”最好的模型。你只要记住边沿触发不等于“只在边沿那一刻读输入”它是通过两段锁存器配合把“采样”和“输出更新”隔离开从而杜绝数据穿透。2.3 用CMOS传输门搭一个D触发器画CMOS级电路图时最经典的是用两个传输门完成主从锁存器。每个锁存器由一对反相器和两个传输门组成。用蜘蛛网一样的C、C非信号控制两个传输门一组导通另一组断开就能实现“跟随→保持”的交替。如果只是想徒手分析逻辑图优先看教材里那种“与非门主从D触发器”前面一级G1/G2构成置位复位锁存中间G3/G4跟随后面G5/G6输出锁存。这类电路图网上真正常见画图工具用draw.io也能拖出足够的逻辑门符号不一定非得用专业EDA。我自己画这类图的经验是不要一上来盯晶体管级先把“主锁存器、从锁存器、反馈路径”三个框画出来再把每个框替换成逻辑门组合结构瞬间就清晰了。逻辑图上你追我赶的连接关系看起来很乱本质就是两个锁存器接力。2.4 分立三极管做边沿触发器的价值用三极管分离元件搭边沿触发器在工程上几乎没人这么干因为几个门一片74HC00就解决了。但做过的都知道它对理解电路原理的推动作用非常大。用三极管搭与非门再用两个与非门交叉成锁存器再级联成主从结构整个过程就像把一台机器拆成零件重新组装一遍你会真正意识到“门”不是抽象符号而是由晶体管工作在导通和截止两种状态实现的。当年经典的三极管分离元件边沿触发器用的就是两个三极管锁存器加一个阻容微分网络实现的微分电路把时钟边沿变成窄脉冲再配合三极管锁存器截取脉冲时刻的数据。这类设计在有TTL和CMOS芯片的年代看起来像“考古”但你要是想弄明白为什么教科书画了那么多触发器的内部电路搭一次比看十遍管用。3. 分频器触发器最拿手的“除以2”3.1 二分频就是D触发器的一个反馈很多人在这一步卡过D触发器做二分频电路简单到令人怀疑——把D接到Q非上CLK输入信号Q端输出的频率就是输入的一半。原理也不难上升沿到来时Q采样D也就是当前Q非的值于是Q更新为Q非相当于翻转一次。下一个上升沿再翻转一次。输入给两个周期的时钟输出才完成一个完整的高/低周期所以频率减半。这个结构是所有数字分频器的地基。模2计数器本质上就是二分频器。后面所有的计数器都可以看作是对“时钟脉冲计数并产生相应状态输出”的扩展。你掌握的每一个触发器板子上的每一块CD4020、74LS74核心都跑不出这个翻转逻辑。3.2 级联分频想要多少分频都行单个D触发器只能分频2。想要4分频把第一个触发器Q输出接到第二个触发器的CLK想要8分频再接一级以此类推。级联N级分频系数就是2的N次方。这个做法在需要把高频信号降到低频的场景特别常用。例如32.768kHz的晶振经过15级分频得到1Hz驱动秒针。单片机上如果外接晶振经过内部PLL倍频到几十MHz再用定时器或者计数器分频得到合适的通信波特率时钟也是同一套逻辑在芯片内部的翻版。级联的时候要注意占空比和输出驱动能力。每级触发器输出的方波占空比理论上还是50%只要输入占空比合适但级联多了以后前级驱动的扇出和上升/下降时间会逐渐变差。在低速应用里无所谓一旦往MHz以上走就需要考虑加缓冲器了。3.3 非整数分频不能只靠触发器如果只需要整数分频触发器级联就够了。但现实里常遇到“从12MHz分出一个1MHz”“从24MHz分出1.5MHz”这种要求。2的幂次明显不够用就得用可编程计数器做任意整数分频计数到N时清零并输出一个脉冲分频系数就是N。至于非整数分频常见套路是“吞脉冲”或者“分数分频”。比如要分频5.5可以每两个5分频脉冲后插入一个6分频脉冲平均下来就是5.5分频。这类设计在收音机频率合成器里很常见市场上那些超高频预分频器芯片比如有人提到的MB506那种能处理GHz级信号的器件内部就是高速计数器和分频链的组合。到了这个层次触发器的“翻转”只是基本动作真正复杂的部分在于怎么让分频比稳定可控。3.4 555定时器顺手当个时钟源讨论分频器时总绕不开时钟从哪来。用晶振搭配反相器、或者用单片机的时钟输出是最规范的方案。但做实验时最省事的是555定时器接成无稳态多谐振荡器配合两个电阻和一个电容就能产生一个频率可调的方波用它再驱动CD4017或74LS90做分频计数学习效果非常直观。555的好处是输出驱动能力强、电路几乎是教科书级别的固定接法。坏处是频率稳定性差受温度和电源电压影响明显。所以它适合做学习实验的信号源不适合做依赖精度的产品时钟。我建议手边常备几个555很多入门级的数字电路实验都能用它当“心跳”信号调电阻还能直观看到分频输出的频率跟着变理解比单纯看仿真波形深刻得多。4. 计数器从数脉冲到模N设计4.1 计数器和分频器其实是一家人从功能上看计数器是“对输入脉冲数进行累加并输出当前计数值”分频器是“输出频率为输入频率的1/N”。区别在于计数器关心的是“数到几了”分频器关心的是“输出频率是多少”。但从实现上讲两者用的是同一批触发器一个N进制计数器最高位输出的频率就是输入频率的1/N反过来一个分频器的内部状态也可以看作在计数。理解了这层关系你就不会被“计数器和分频器要选不同电路”这种想法误导。学习时先学二进制计数器4个D触发器的Q3Q2Q1Q0按二进制累加再学模10、模60这种非二进制计数器很多规律都是通用的。4.2 异步计数器和同步计数器计数器有两种实现风格工程项目里抉择很关键。异步计数器也叫行波计数器做法简单粗暴前一级的Q输出直接接后一级的时钟输入。第一个触发器翻转一次第二个才跟着翻转一级一级像波浪一样传播。电路简单、芯片数量少但高位翻转总是比低位晚几个门延迟所以整个计数值在翻转瞬间是“从低位到高位依次变化”的这叫毛刺容易出现在解码电路里频率一高就容易乱。同步计数器的做法是所有触发器的CLK接同一个时钟至于要不要翻转由组合逻辑按当前状态和计数方向控制每个触发器的D端或JK端。这种方式多了些门电路但所有触发器同时更新输出状态几乎同一时刻稳定高频下可靠得多。工程上做中低速计数无所谓异步也能跑做高速计数或需要后级译码的老老实实用同步计数器。74LS161、CD4017这类集成计数器内部基本都是同步结构单片机里你用定时器溢出做计数本质也是同步时序的行为建模。4.3 模N计数器清零法和置数法想计数到10就回到0而不是自然二进制数到15这叫模10计数器BCD十进制计数。两种实现办法清零法计数器自然计数到N时用译码逻辑产生一个高/低脉冲把所有触发器异步清零。需要注意清零信号宽度和毛刺因为组合逻辑译码出的清零脉冲本身就有尖峰可能导致提前清零或者漏清零。置数法计数器预设一个初值计到最大值时回跳到初值。好处是可以通过初值直接设定模值不需要外部复杂译码有些芯片支持同步置数时序更安全。本科实验里特别喜欢让做60进制计数器秒/分显示我推荐的做法是个位用模10十位用模6两个计数器级联到59之后整体清零。这个思路用到70、99也都一样本质就是“低位满则高位进一”的十进制思想。4.4 常用计数器芯片速查初学者手边备几颗常见芯片比纯仿真更能建立手感74LS74 / CD4013双D触发器做寄存器、二分频。74LS90异步十进制计数器内部有模2和模5两部分可以组合成模10或模7。74LS161 / CD45164位同步二进制计数器带预置和清零可做任意模数计数器。CD4017十进制计数/分配器输出10个依次高电平的端口做流水灯、步进时序控制特别好用。CD4020 / CD404014级/12级二进制计数器天然就是大分频比分频器晶振电路后面经常挂它取秒信号。74LS193可预置、可加/减计数的同步计数器做双向计数很顺手。这些芯片的管脚功能大同小异用之前查一下数据手册注意是TTL还是CMOS电平、高有效还是低有效的清零端接错就莫名其妙不工作。5. 把计数器接到真实场景里5.1 四位共阴极数码管动态扫描计数器的结果最终要给人看。最直观的输出就是数码管。一个数码管有七段加一个小数点共阴极就是把所有LED的阴极接在一起段码端给高电平点亮位选端选择哪一位亮。四位共阴极数码管如果每一位都独立接一组段码IO根本不够用。实际都采用动态扫描同一时间只点亮一位轮流点亮千、百、十、个位利用人眼视觉暂留扫描频率高于50Hz看起来就是四位同时在亮。我见过不少初学者把段码电阻估错导致亮度不均或数码管发烫。共阴数码管每段工作电流一般取5~15mA段码输出串330Ω~1kΩ限流电阻位选三极管如PNP负责灌入更大电流。别图省事不接限流电阻直接怼IO口亮度刺眼还烧LED。5.2 按键防抖必须做按键计数的项目几乎都会碰到一个问题按一下数字跳了好几下。原因是机械按键在按下和释放瞬间会有几毫秒到几十毫秒的抖动电平反复跳动计数器把这些抖动全数进去了。软件层面最简单的做法是延时消抖检测到按键按下后延时10~20ms再读一次引脚确认还是按下才算有效。硬件层面可以在按键两端并一个RC低通滤波器比如10kΩ电阻串联后接100nF电容到地在信号进入计数器前先滤掉毛刺。如果用的是纯数字电路比如CD4017做计数建议用RS触发器做成一个锁存消抖器按键信号第一次触发后锁存住释放后再复位。这个场景恰好就是用上第一节讲过的RS触发器的经典例子学完立刻能用。5.3 用STC8G1K08做一个0-999计数器单片机上做计数器是另一种思路不自己搭一堆触发器而是用芯片内部的定时器/计数器外设。以STC8G1K08为例做一个按键累加计数并显示到四位共阴数码管的项目整体流程是这样的用定时器0做一个5ms中断在中断里做数码管动态扫描刷新千位、百位、十位、个位。用一个普通IO口接按键10ms软件消抖检测到一次有效按下就把计数值加1计到999后归0。把计数值拆成四位个位 计数 % 10十位 计数 / 10 % 10百位 计数 / 100 % 10千位可以固定显示0。段码输出到数码管位选用三极管控制。如果位选分别接P1.0千位、P1.1百位、P1.6十位、P1.7个位段码由另一组IO输出那扫描顺序就按这四个引脚依次拉低/拉高。这里有个和纯数字电路不同但很实用的点单片机里的“计数器”外设本质上也是一个寄存器累加器外部信号接定时器/计数器引脚后硬件自动对脉冲计数溢出时产生中断。你省去了物理触发器芯片的搭建但背后原理完全一致。我用STC8G1K08做过带计数清零、单步加一、数码管显示的小装置实际跑下来最大的麻烦不是计数而是消抖和扫描时序——一个地方没做好显示就会乱跳或闪烁。把定时器中断优先级调高、扫描时长均匀地放在中断里问题基本就解决了。5.4 Verilog里的计数器写法进了FPGA的世界触发器变成了一行例化但原理反而要求更精确。典型的一个模100计数器module counter_100( input clk, input rst_n, output reg [6:0] count ); always (posedge clk or negedge rst_n) begin if (!rst_n) count 7d0; else if (count 7d99) count 7d0; else count count 1b1; end endmodule这里有几个关键点复位用异步复位posedge clk or negedge rst_n因为FPGA上电瞬间状态不可控必须给一个明确的初始值。你不用复位直接仿真也能跑但烧到板子上就不知道从哪个状态开始了。count的位宽要足够。模100需要7位二进制最大99如果写成6位99就会溢出结果完全不对。这是一个最常见的低级错误改了位宽仿真立刻正常。用非阻塞赋值写时序逻辑不要在always块里用阻塞赋值去描述触发器否则综合出来的电路和你脑子里想的完全不是一回事。如果想用Verilog实现按键计数数码管扫描思路和单片机是一样的按键输入先做同步化处理把异步按键信号打两拍再检测下降沿然后对计数寄存器加1。自己用Verilog写一遍分频器再写一遍计数器再回头看教科书上的D触发器你会觉得所有知识都串起来了。6. 调试中的几个坑与经验6.1 毛刺和抖动怎么抓纯数字电路实验里最常见的故障现象是计数乱跳、LED偶尔闪错一位。这类问题的罪魁祸首通常是毛刺异步计数器翻转不同步导致的中间状态、组合逻辑译码产生的尖峰、按键抖动症状看起来一模一样。调试时别急着怀疑芯片坏了。先用万用表或示波器看时钟信号干不干净再看清零/置数引脚的瞬时波形。示波器能捕捉到毛刺最好抓不到就把扫描时间调到微秒级观察计数器的状态在同一次时钟边沿前后是否有“穿越”不合法状态的迹象。一个典型例子异步计数器从01117变到10008时Q3、Q2、Q1、Q0不是同时翻转的中间可能出现0000之类短暂状态。如果后面紧跟一个组合逻辑译码就可能在这个瞬间误触发一次清零或进位。解决办法很简单要么改同步计数器要么把状态输出打一拍再送去译码。6.2 上电初始状态不定的处理触发器上电那一刻锁存的内容可能是0也可能是1谁也没法保证。这在很小的电路上可能不影响但一旦状态参与运算上电就随机就会出现不可预测行为。纯逻辑电路的做法是加一个上电复位电路用一个RC延时电路在电源刚建立的几十毫秒内给所有触发器的复位端一个有效电平把状态清零。单片机和FPGA则都有复位引脚或内部上电复位逻辑所以写Verilog时一定要把复位分支写清楚而且尽量不要用那种“上电后自然稳定”的侥幸心理。我踩过的坑是做了复位电路但复位信号宽度不够或者复位信号在时钟边沿附近释放导致某些触发器恰好错过了复位的有效窗口一部分状态没被复位。后来统一用同步复位或者保证复位释放和时钟沿之间有足够的时间裕量就再也没有神秘问题了。6.3 仿真过了实物不行的原因用仿真软件看波形一切正常一接实物就失灵几乎每个人都经历过。仿真和实物的差距主要在三方面仿真默认所有逻辑门延迟为零或可设定而实物的传播延迟、建立时间、保持时间是硬约束。高频下输入变化太晚触发器采样到的就是旧值。仿真没有电源噪声和地弹实物的电源不是理想电压源。CMOS器件对电源毛刺敏感信号线过长还会引入天线效应和串扰。仿真没有“未连接引脚”问题。实物上悬空的输入引脚会飘在高低电平之间导致莫名其妙的翻转。养成把所有不用的输入引脚接高或接低的好习惯。所以调电路时先检查电源、地、所有输入引脚有没有正确端接再用示波器看时钟的边沿质量最后才查逻辑关系。顺序反了你会在一个接线错误的电路上分析半天“逻辑设计问题”。6.4 几个能直接带走的检查清单根据自己的折腾经验我把触发器相关电路最容易栽跟头的地方整理成一个简化版检查清单每次搭建或调试时过一遍能省下大量时间时钟信号是否干净有没有过冲、振铃、明显抖动。触发器的复位/置位端在不需要时是否被固定到无效电平有没有悬空。异步计数器级联时是否考虑过级间延迟对后续译码的影响。清零法做模N计数时清零信号来自组合逻辑有没有产生毛刺风险。按键输入有没有做消抖是硬件RC滤波还是软件延时确认。数码管和LED的限流电阻是否匹配位选驱动是否足够。用Verilog的always块描述时序逻辑时用的是非阻塞赋值还是阻塞赋值。计数器位宽是否容纳得下最大值模100至少要7位模99要7位模9只要4位。上电复位是否可靠复位释放是否避开了时钟边沿。我现在的习惯是任何包含触发器的数字电路项目先花五分钟把这九条过一遍再上电调试。很多问题根本不用上示波器就能提前排除掉。最后再分享一个我实际用到的技巧手头没有逻辑分析仪时把计数器的最高位Q接到一个LED上用肉眼观察LED闪烁频率是否等于预期分频比这就是一个最廉价的频率计。把整个系统拆成“每个触发器都正常工作”这个小目标来验证再复杂的计数器电路也能稳扎稳打地调通。
返回列表