最近在整理数字电路学习笔记时,发现很多朋友对JK触发器的理解停留在“功能表”和“波形图”上,一到实际电路搭建和时序分析就容易卡壳。Ben Eater的经典视频教程虽然直观,但缺少中文语境下的系统性梳理和代码仿真验证。本文将结合Ben Eater的硬件演示思路,为你拆解JK触发器的核心原理、真值表背后的“为什么”、从零搭建的完整步骤,并提供可运行的Verilog仿真代码。无论你是电子专业学生、FPGA初学者,还是想夯实数电基础的开发者,都能通过本文获得一套从理论到实践、从硬件到仿真的闭环学习方案。
1. JK触发器的核心概念与为什么需要它
在数字逻辑的世界里,触发器(Flip-Flop)是构成时序电路的基本记忆单元。我们最先接触的往往是SR锁存器(Set-Reset Latch),它简单直接,但有一个致命的缺陷:禁止状态(S=1, R=1)。当两个输入端同时有效时,其输出状态是不确定的,这在实际电路中是必须避免的。
JK触发器正是为了解决SR触发器的“禁止状态”而诞生的。它巧妙地对SR触发器的功能进行了改进和扩展。
1.1 JK触发器是什么?
JK触发器是一种具有时钟边沿触发、保持、置位、复位和翻转功能的双稳态存储器件。它有两个数据输入端J和K,一个时钟输入端CLK(通常是边沿触发),以及输出端Q和其反相输出端Q’。
它的核心价值在于:当J和K同时为高电平(1)时,输出状态会在每个有效时钟沿发生翻转(Toggle)。这个特性使得JK触发器功能上变得“完备”,消除了不确定状态,并为其在计数器、分频器等电路中的应用奠定了基础。
1.2 JK触发器与SR、D触发器的关系
理解JK触发器,最好将其放在触发器家族的进化链中来看:
- SR锁存器(基础):异步操作,有禁止状态(S=R=1)。
- 钟控SR触发器:引入了时钟控制,但禁止状态问题依然存在。
- JK触发器(进化):在钟控SR触发器的基础上,通过内部反馈逻辑,将“禁止状态”重新定义为“翻转状态”。你可以粗略地理解为:
J对应S(置位),K对应R(复位),但当 J=K=1 时,电路不会冲突,而是让输出取反。 - D触发器(简化):可以看作是JK触发器在 J=K’(即J和K始终相反)条件下的一个特例,或者一个更简单的结构,主要用于数据存储。
因此,掌握JK触发器,就等于掌握了最通用、功能最完整的触发器类型,D触发器和T触发器(翻转触发器)都可以看作是它的子集或变体。
2. 环境准备与学习工具说明
学习JK触发器,我们推荐“理论+仿真+硬件”三位一体的方法。以下是所需的“环境”和工具:
2.1 理论分析工具
- 笔和纸:用于绘制真值表、状态转换图(状态图)和时序波形图。这是理解内部逻辑关系不可替代的一步。
- 逻辑代数基础:需要了解与、或、非等基本逻辑运算。
2.2 硬件仿真环境(跟随Ben Eater思路)
如果你想像Ben Eater那样在面包板上搭建,需要准备:
- 集成电路:74LS73N(双JK触发器,带清零端)。这是最经典的教学芯片。
- 面包板、跳线、电源:提供5V直流电源。
- 逻辑开关:用于手动控制J、K和CLK输入。
- LED与电阻:用于观察输出Q和Q’的状态。
- 单脉冲发生器/按键:用于产生干净的手动时钟信号,避免机械抖动。
2.3 数字电路仿真软件(强烈推荐)
对于绝大多数学习者,先用软件仿真可以极大降低门槛,快速验证逻辑:
- Logisim:开源、图形化,适合入门级逻辑电路仿真,可以直观地搭建门级电路。
- Digital(或类似工具):功能更强大的数字电路仿真器。
- FPGA开发工具 + Verilog/VHDL:用于进行寄存器传输级(RTL)仿真和综合,是工业级的学习方式。我们将使用Verilog进行行为级仿真。
本文将以74LS73的功能为基准,同时使用Verilog进行仿真验证,确保你能从抽象逻辑和具体实现两个层面都掌握它。
3. JK触发器的功能真值表与深度解析
真值表是理解触发器功能的钥匙,但死记硬背没有意义。我们结合时钟边沿和内部逻辑来解读。
3.1 功能真值表(以上升沿触发为例)
| 时钟CLK | J | K | Q(n) | Q(n+1) | 功能描述 |
|---|---|---|---|---|---|
| ↑ | 0 | 0 | 0 | 0 | 保持:输出状态不变 |
| ↑ | 0 | 0 | 1 | 1 | 保持:输出状态不变 |
| ↑ | 0 | 1 | 0 | 0 | 复位:输出置0 |
| ↑ | 0 | 1 | 1 | 0 | 复位:输出置0 |
| ↑ | 1 | 0 | 0 | 1 | 置位:输出置1 |
| ↑ | 1 | 0 | 1 | 1 | 置位:输出置1 |
| ↑ | 1 | 1 | 0 | 1 | 翻转:输出状态取反 |
| ↑ | 1 | 1 | 1 | 0 | 翻转:输出状态取反 |
| 0 或 1 | x | x | x | Q(n) | 无效:时钟无效时,忽略J、K输入 |
说明:
CLK ↑表示时钟的上升沿(从0跳变到1的瞬间)是有效触发时刻。Q(n)表示当前状态(时钟沿之前的状态)。Q(n+1)表示下一个状态(时钟沿之后的状态)。x表示“无关项”,可以是0或1。
3.2 真值表背后的逻辑方程
从真值表可以推导出JK触发器的特征方程,它描述了下一个状态Q(n+1)与当前状态Q(n)以及输入J、K之间的关系:
Q(n+1) = J · Q'(n) + K' · Q(n)
这个方程如何理解?
J · Q'(n):当Q(n)=0(即Q'(n)=1)时,若J=1,则Q(n+1)被置为1。这实现了置位功能。K' · Q(n):当Q(n)=1时,若K=0(即K'=1),则Q(n+1)保持为1。这实现了保持功能(当J=0,K=0时,两项都为0,由第二项保持)。 更关键的是,当K=1(即K'=0)时,这一项为0,如果此时J=0,则方程结果为0,实现了复位。- 翻转功能的体现:当
J=1, K=1时,方程变为Q(n+1) = 1·Q'(n) + 0·Q(n) = Q'(n),正好是当前状态的反码,即翻转。
这个方程是理解JK触发器所有行为的数学核心,也是我们后续用逻辑门搭建它的理论基础。
4. 从门级电路到集成电路:JK触发器的内部构造
Ben Eater的视频精彩之处在于他用最基础的门电路(与非门)一步步搭建出了JK触发器。我们来还原这个构建过程,理解其内部工作原理。
4.1 基础:SR锁存器(与非门实现)
一切始于两个交叉耦合的与非门(NAND Gate)。
// 这是一个用Verilog描述的门级SR锁存器(低电平有效) module sr_latch ( input wire S_n, // 低电平有效的置位端 input wire R_n, // 低电平有效的复位端 output reg Q, output wire Q_n ); assign Q_n = ~Q; // Q_n是Q的反相 always @(*) begin // 注意:这是组合逻辑描述,实际锁存器有反馈,这里为简化示意 // 真实的门级实现是交叉连接,会产生锁存器 if (!S_n && R_n) Q = 1‘b1; // S有效,置位 else if (S_n && !R_n) Q = 1’b0; // R有效,复位 // 当 S_n=1, R_n=1 时,Q保持原值(通过反馈实现) // 当 S_n=0, R_n=0 时,是禁止状态(Q和Q_n都输出1) end endmodule关键点:当两个输入都为高电平时,电路保持;同时为低电平时,输出无效(Q和Q_n都为1)。
4.2 升级:钟控SR触发器
在SR锁存器前增加两个与非门,用时钟CLK来控制信号何时能传入。
// 钟控SR触发器(高电平有效,时钟高电平期间透明) module clocked_sr_ff ( input wire CLK, input wire S, input wire R, output reg Q, output wire Q_n ); wire S_gated, R_gated; // 时钟门控逻辑 assign S_gated = ~(S & CLK); // 输入与非门,低电平有效 assign R_gated = ~(R & CLK); // 将门控后的信号接入SR锁存器(低电平有效) // 这里调用上面定义的sr_latch模块(需稍作修改以适应接口) // 简化描述其行为: always @(posedge CLK) begin // 注意,这已经是边沿触发行为的描述 if (S && !R) Q <= 1‘b1; else if (!S && R) Q <= 1’b0; // S=R=1 会导致问题! end assign Q_n = ~Q; endmodule问题:当CLK=1期间,如果S=1且R=1,则S_gated和R_gated同时为0,会使底层SR锁存器进入禁止状态。
4.3 终极形态:主从JK触发器
为了解决上述问题,JK触发器引入了反馈。经典的主从JK触发器结构包含两个级联的钟控SR触发器(一个主触发器,一个从触发器),并将从触发器的输出Q和Q’反馈到主触发器的输入门。
核心改进逻辑:
- 将输出
Q反馈回来,与K和CLK相与后,作为主触发器复位信号的一部分。 - 将输出
Q_n反馈回来,与J和CLK相与后,作为主触发器置位信号的一部分。
这样做的神奇效果:
- 当
Q=1时,Q_n=0。这会阻塞J端通往主触发器置位端的路径(因为与0相与得0),同时为K端打开通往复位端的路径。这意味着,即使J=K=1,也只有复位通路可能被激活。 - 当
Q=0时,Q_n=1。这会阻塞K端通往主触发器复位端的路径,同时为J端打开通往置位端的路径。 - 因此,
J和K永远不会同时有效地作用于主触发器的SR输入端,从而彻底消除了禁止状态。当J=K=1时,电路会根据当前状态,自动选择置位或复位,最终效果就是翻转。
这种结构就是74LS73等芯片内部采用的经典主从结构。它在时钟高电平期间采样输入,在时钟下降沿改变最终输出。
5. 完整实战:使用Verilog仿真JK触发器
理解了原理,我们通过代码来仿真验证其功能。我们将实现一个行为级模型和一个简单的测试平台。
5.1 JK触发器的Verilog行为级描述
// 文件名:jk_ff_behav.v // 功能:上升沿触发的JK触发器,带异步低电平清零端(如同74LS73) module jk_ff_behav ( input wire clk, // 时钟信号 input wire j, // J输入 input wire k, // K输入 input wire clr_n, // 异步清零,低电平有效 (Clear) output reg q, // 输出 output wire q_n // 反相输出 ); assign q_n = ~q; // 反相输出 // 异步清零的优先级最高 // 时钟上升沿触发 always @(posedge clk or negedge clr_n) begin if (!clr_n) begin // 清零信号有效,无条件清零 q <= 1'b0; end else begin // 根据JK真值表进行状态转换 case ({j, k}) 2'b00: q <= q; // 保持 2'b01: q <= 1'b0; // 复位 2'b10: q <= 1'b1; // 置位 2'b11: q <= ~q; // 翻转 default: q <= q; // 默认保持 endcase end end endmodule5.2 编写测试平台(Testbench)
// 文件名:tb_jk_ff.v // 功能:测试JK触发器模块 `timescale 1ns / 1ps // 定义仿真时间单位/精度 module tb_jk_ff(); // 定义测试信号 reg clk; reg j; reg k; reg clr_n; wire q; wire q_n; // 实例化被测试单元(DUT) jk_ff_behav dut ( .clk(clk), .j(j), .k(k), .clr_n(clr_n), .q(q), .q_n(q_n) ); // 生成时钟信号,周期20ns(频率50MHz) initial begin clk = 0; forever #10 clk = ~clk; // 每10ns翻转一次,周期20ns end // 施加测试激励 initial begin // 初始化信号 j = 0; k = 0; clr_n = 1; // 初始不清零 // 等待一个时钟周期,让电路稳定 #20; // 测试用例1:异步清零(优先级最高) $display("[%0t] Test 1: Async Clear", $time); clr_n = 0; // 激活清零 #15; clr_n = 1; // 释放清零 #20; // 测试用例2:保持功能 (J=0, K=0) $display("[%0t] Test 2: Hold (J=0, K=0)", $time); j = 0; k = 0; #40; // 观察两个时钟沿,q应保持不变 // 测试用例3:置位功能 (J=1, K=0) $display("[%0t] Test 3: Set (J=1, K=0)", $time); j = 1; k = 0; #40; // 第一个上升沿置位,之后保持 // 测试用例4:复位功能 (J=0, K=1) $display("[%0t] Test 4: Reset (J=0, K=1)", $time); j = 0; k = 1; #40; // 第一个上升沿复位,之后保持 // 测试用例5:翻转功能 (J=1, K=1) - 最核心的特性! $display("[%0t] Test 5: Toggle (J=1, K=1)", $time); j = 1; k = 1; #80; // 观察四个时钟沿,q应每个周期翻转一次 // 测试用例6:组合测试 $display("[%0t] Test 6: Mixed Inputs", $time); j = 0; k = 1; #40; // 复位 j = 1; k = 0; #40; // 置位 j = 1; k = 1; #40; // 翻转(应变为0) j = 0; k = 0; #40; // 保持(保持0) $display("[%0t] All tests completed.", $time); #20 $finish; // 结束仿真 end // 监控信号变化,打印到控制台 initial begin $monitor("Time=%0tns: clk=%b, j=%b, k=%b, clr_n=%b -> q=%b, q_n=%b", $time, clk, j, k, clr_n, q, q_n); end endmodule5.3 运行仿真与结果分析
使用ModelSim、Vivado Simulator或任何支持Verilog的仿真工具运行上述测试平台。
预期关键波形与输出:
- 异步清零:
clr_n变低的瞬间,q应立即变为0,无需等待时钟沿。 - 保持:在J=K=0期间,
q的值在时钟上升沿前后不变。 - 置位/复位:在J=1,K=0的时钟上升沿后,
q变为1;在J=0,K=1的时钟上升沿后,q变为0。 - 翻转:在J=K=1期间,每个时钟上升沿,
q的值都会翻转一次(1->0->1->0...)。这是JK触发器最标志性的行为。
通过仿真波形,你可以直观地验证真值表中的所有情况,深刻理解边沿触发和异步清零的含义。
6. 常见问题与硬件搭建排错指南
在实际硬件搭建(如使用74LS73芯片)或仿真中,你可能会遇到以下问题:
6.1 问题排查表
| 问题现象 | 可能原因 | 排查思路与解决方案 |
|---|---|---|
| 输出无变化,始终为高或低 | 1. 电源未接通或电压不对。 2. 时钟信号未连接或频率太高。 3. 清零端(CLR)被意外置为有效电平。 | 1. 用万用表检查Vcc(5V)和GND。 2. 用示波器或LED检查时钟信号是否存在。改用低频(1Hz)或手动单脉冲时钟。 3. 检查74LS73的CLR引脚是否已接高电平(无效)。 |
| 输出状态不稳定,随机跳动 | 1. 输入信号存在毛刺或抖动。 2. 面包板接触不良。 3. 未使用去耦电容。 | 1. 对机械开关产生的时钟或输入,使用RC滤波电路或专用防抖芯片(如施密特触发器)。 2. 重新插拔芯片和导线,确保接触牢固。 3. 在芯片Vcc和GND引脚间就近并联一个0.1uF的瓷片电容。 |
| 翻转功能(J=K=1)不正常 | 1. 芯片损坏。 2. 反馈逻辑理解有误(对于分立门电路搭建)。 3. 主从触发器的“一次变化”问题。 | 1. 更换芯片测试。 2. 仔细检查从输出反馈到输入的连接是否正确。 3.注意:经典主从JK触发器在CLK=1期间,主触发器可能因输入变化而只改变一次状态。确保在CLK=1期间J、K保持稳定。 |
| 仿真结果与预期不符 | 1. 测试平台时钟与激励的时序不对齐。 2. Verilog代码中敏感列表或赋值方式错误。 3. 对异步/同步复位理解有误。 | 1. 确保激励信号在时钟上升沿之前足够长时间(建立时间)保持稳定。 2. 检查是否该用 <=(非阻塞赋值)的地方误用了=(阻塞赋值)。时序逻辑统一用<=。3. 确认清零信号的优先级和有效边沿描述是否正确。 |
6.2 硬件搭建核心技巧(Ben Eater风格)
- 单步时钟:一开始不要用信号发生器,用一个按键通过RC防抖电路产生单脉冲作为CLK,按一下,观察一次状态变化,便于调试。
- 先静态后动态:先断开CLK,手动设置J、K,检查输出是否符合锁存特性(对于某些电路)。然后接上时钟。
- 善用LED:不仅用LED看输出Q,也可以用LED(串联电阻)接在关键的中间节点(如主触发器输出),帮助理解内部状态变化。
- 阅读数据手册:务必查阅
74LS73的数据手册,确认引脚图、最大时钟频率、建立保持时间等参数。
7. 最佳实践与工程应用建议
掌握了基础后,如何在更复杂的数字系统和现代FPGA/ASIC设计中用好JK触发器呢?
7.1 何时选用JK触发器?
尽管JK触发器功能全面,但在现代同步数字设计中,D触发器因其结构简单、数据路径清晰而成为绝对主流。JK触发器通常用于:
- 教学场景:理解触发器原理和状态机基础。
- 特定计数器设计:某些异步计数器或环形计数器用JK实现可能更简洁。
- 将现有TTL/CMOS逻辑(如74系列)移植到FPGA:需要理解其行为以进行等效替换。
在FPGA/ASIC中,优先使用D触发器。JK触发器的功能可以通过D触发器加组合逻辑轻松实现:
// 用D触发器和组合逻辑实现JK触发器功能 module jk_using_d_ff ( input wire clk, input wire j, input wire k, input wire clr_n, output reg q ); wire d_input; // 根据JK功能表推导出D输入端的逻辑 // Q(n+1) = J & ~Q(n) | ~K & Q(n) assign d_input = (j & ~q) | (~k & q); always @(posedge clk or negedge clr_n) begin if (!clr_n) q <= 1'b0; else q <= d_input; // D触发器核心 end endmodule7.2 同步设计与时序约束
无论使用哪种触发器,都必须遵守同步设计原则:
- 全局时钟:使用单一的全局时钟网络驱动所有触发器。
- 避免异步反馈:像主从JK触发器内部那种异步反馈,在现代设计中应转化为同步逻辑。
- 关注时序:理解建立时间(Setup Time)和保持时间(Hold Time)。在仿真中,激励信号要在时钟沿前稳定足够长时间。
7.3 从JK触发器到有限状态机(FSM)
JK触发器是构建状态机(尤其是摩尔机)的理想元件。每个触发器存储状态位的一位。设计步骤:
- 状态编码:为每个状态分配二进制码。
- 绘制状态转移图。
- 列出状态转移表:包含当前状态、输入、下一状态。
- 利用JK触发器激励表:根据当前状态和下一状态,反推出每个JK触发器所需的J、K输入值。
- 化简J、K的逻辑方程:使用卡诺图或逻辑化简工具。
- 绘制电路图或编写RTL代码。
这个过程是数字逻辑设计的精髓,虽然现在多用HDL直接描述状态机,但理解其底层硬件实现(由触发器构成)至关重要。
本文从SR触发器的缺陷出发,揭示了JK触发器如何通过巧妙的反馈逻辑消除禁止状态并实现翻转功能。我们不仅深入分析了其真值表和特征方程,还沿着Ben Eater的硬件思路理解了主从结构的内在机理。通过可运行的Verilog行为模型和测试平台,我们能够在仿真环境中直观验证其所有功能。最后,我们探讨了硬件搭建的常见陷阱,并给出了在现代数字设计中应用JK触发器的最佳实践。理解JK触发器,不仅仅是记住一个芯片的功能,更是打通组合逻辑与时序逻辑、理解状态存储与转移的关键一步。建议你亲手运行一遍仿真代码,如果有条件,不妨在面包板上搭一遍74LS73电路,那种“灯随心动”的体验,会让你对数字电路的理解更加深刻。