ARTICLE DETAIL

资讯详情

深耕商务建站与企业官网运营的一线实战洞察。

2-5计数器设计详解:从JK触发器状态表到Verilog仿真全流程

2-5计数器设计详解:从JK触发器状态表到Verilog仿真全流程 简介一份面向数字电子线路课程的实验指导文档围绕计数器逻辑功能与设计展开适合高校电类专业学生及数字电路学习者阅读。内容覆盖同步/异步、二进制/十进制/任意进制、加法/减法/可逆计数器等基础分类重点讲解反馈归零法和反馈置数法设计任意进制计数器并结合74HC161、74HC390等中规模集成芯片以简易数字钟为例呈现六十进制、二十四进制计数器的级联构建方法。资源共1个docx文件大小207KB文档包含实验目的、仪器设备、预习要求、原理说明、芯片结构图与引脚图、实验步骤和思考题等完整环节既可作为实验预习资料也可在设计接线时提供方法与思路参考。已有288人学习该资源适合需要完成同类数字电路实验并希望深入理解计数功能与设计方法的读者。1. 2-5 计数器是什么先把这个逻辑功能翻译成电路术语拿到“数字电子线路基础2-5 计数器逻辑功能和设计”这个题目时最容易踩的坑是把它当成一个简单的“从 2 数到 5 再停下来”的计数器。实际上数字电路里说 2-5 计数器通常指一个同步计数器让状态按 2→3→4→5→2 循环每来一个时钟上升沿前进一次本质是一个模 4 计数器只是有效状态从 2 开始、到 5 结束。这种序列控制逻辑在电子钟分频、步进电机换相、交通灯时序和简单状态机里非常常见。适合的人群是正在上数字电子线路课的初学者以及想做课程设计但不想只抄仿真图的硬件新人。这篇文章的目标很直接帮你把“逻辑功能”拆成状态表再用 JK 触发器设计出可复现的电路最后用仿真验证它真的在循环而不是停在某个非法状态里出不来。2. 状态设计2→3→4→5→2 的循环怎么变成状态表2.1 三比特编码2、3、4、5 到底对应哪一组 Q2Q1Q0数字电路的计数器最终要落到触发器的 Q 端上。2-5 这个序列里一共有四个有效状态理论上两个触发器就够了但两个触发器只能表示 0 到 3如果直接把 2、3、4、5 当作数值两个触发器根本装不下 4 和 5。因此常见做法是用三个触发器 Q2、Q1、Q0把状态编码成三位二进制计数序号计数值Q2 Q1 Q0020 1 0130 1 1241 0 0351 0 14回到 20 1 0注意这里的状态编码不是连续的 000→001→010→011而是 010→011→100→101 循环。为什么要这样编码因为这样最直观地对应输出数值Q2Q1Q0 本身就是二进制数010 等于十进制 2011 等于 3100 等于 4101 等于 5。后续接数码管显示或者接比较器判断数值时不需要再额外做复杂的算术转换。剩下的 000、001、110、111 四个状态是非法状态。设计时我一般不会直接忽略它们否则仿真跑得通、上电后可能卡在 000 或者 111 里永远进不了循环。真正可靠的计数器设计必须把非法状态也拉回到合法状态这就是后面要说的自启动设计。2.2 把状态转换写成激励表JK 触发器对每个翻转需要什么输入有了状态编码下一步是列出状态转换关系。每来一个时钟上升沿电路从当前状态跳到下一状态。四个合法转换是010→011011→100100→101101→010。再用 JK 触发器的特性逐位分析。JK 触发器的行为是J1、K0 时置位为 1J0、K1 时清零为 0J1、K1 时翻转J0、K0 时保持。所以对于某一位 Q如果它要从 0 变 1那 J 必须为 1如果要从 1 变 0那 K 必须为 1如果保持原值则 J 和 K 中至少有一个为 0具体是 00 还是 10 还是 01 都可以通常填 X 表示任意。下面是三个触发器的完整性激励表当前状态下一状态J2 K2J1 K1J0 K00100110 XX 01 X0111001 XX 1X 1100101X 00 X1 X101010X 11 XX 1以 011→100 这行为例Q2 从 0 变 1所以 J21Q1 从 1 变 0所以 K11Q0 从 1 变 0所以 K01。其他位填 X这不代表引脚可以悬空而是说化简时可以灵活选择 0 或 1用来凑最简逻辑式。2.3 先把可运行的计数器模块搭出来一个 Verilog 最小框架状态表写完我先不急着化简而是用 Verilog 把 JK 触发器和计数器骨架搭出来。这样做的好处是后面每一步化简出的方程都可以直接替换进代码里跑仿真不会等到接线完才发现逻辑矛盾。先写一个行为级 JK 触发器module jk_ff ( input wire clk, input wire rst_n, input wire j, input wire k, output reg q ); always (posedge clk or negedge rst_n) begin if (!rst_n) begin q 1b0; end else begin case ({j, k}) 2b10: q 1b1; // J1,K0 置位 2b01: q 1b0; // J0,K1 清零 2b11: q ~q; // J1,K1 翻转 default: q q; // 保持 endcase end end endmodule这段代码里的 case 语句直接对应 JK 触发器的四种行为。注意 default 分支不能省略否则 j/k 为 00 时 q 的行为在仿真里可能没有定义综合时也容易多出锁存器或不定态。再写计数器顶层module counter_2_5 ( input wire clk, input wire rst_n, output wire [2:0] q ); wire j2, k2, j1, k1, j0, k0; wire [2:0] q; // 先临时用最保守的激励保持为低后面用卡诺图化简结果替换 assign j2 1b0; assign k2 1b0; assign j1 1b0; assign k1 1b0; assign j0 1b0; assign k0 1b0; jk_ff u2 (.clk(clk), .rst_n(rst_n), .j(j2), .k(k2), .q(q[2])); jk_ff u1 (.clk(clk), .rst_n(rst_n), .j(j1), .k(k1), .q(q[1])); jk_ff u0 (.clk(clk), .rst_n(rst_n), .j(j0), .k(k0), .q(q[0])); endmodule复位后三个触发器输出都是 000所以计数器会先从 000 跳到 010再进入 2→3→4→5 循环。这里把复位统一做成低电平异步复位所有触发器在一个复位信号控制下回到 000避免不同触发器复位时间不一致导致状态错位。下一步就是使用激励表求出这六根线 j2、k2、j1、k1、j0、k0 的表达式。3. 从状态表到逻辑图卡诺图化简和接线3.1 用卡诺图圈 JK 激励J2、K2、J1 怎么圈最少卡诺图是手算组合逻辑最快的方法。我以 J2 为例演示化简过程。J2 只在当前状态 011 时为 1其他合法状态为 0 或 X非法状态里 110、111 是 X000、001 我们暂时希望它是 0。按 Q2Q1 作行、Q0 作列可得到下面的三变量卡诺图J2 的卡诺图 Q00 Q01 Q2Q100 0 0 Q2Q101 0 1 Q2Q111 X X Q2Q110 X X卡诺图圈“1”和“X”时尽量圈 2、4、8 个相邻格。这张图里 011 这个 1 与 111 这个 X 相邻可以圈成一行化简结果是 Q1·Q0。所以 J2 Q1·Q0。K2 的卡诺图同样处理K2 需要在 Q2Q1Q0101 时为 1为了让非法状态 110、111 也能被清回合法状态K2 要在这两个非法状态也为 1圈出来就是 Q2·(Q1Q0)。3.2 六条激励方程和自启动校验按同样的卡诺图方法把所有激励项圈完得到一组可以直接接线使用的方程信号化简表达式作用位说明J2Q1·Q0只有 Q1、Q0 同时为 1 时把 Q2 置 1K2Q2·(Q1Q0)Q2 为 1 且 Q1 或 Q0 为 1 时把 Q2 清 0J1~Q1·(~Q2Q0)让 Q1 在状态 000、001、101 时置 1K1~Q2·Q1·Q0从 011 转到 100 时把 Q1 清 0J0Q2 XOR Q1两个高位不同时让 Q0 置 1K0Q0Q0 为 1 时下一个周期必翻转这组方程和上一章激励表的合法状态完全一致同时还处理了非法状态。比如 000 状态下J1 为 1、K1 为 0一个时钟脉冲后 Q1 变 1状态变成 010也就是回到计数值 2。111 状态下 J2、K2 同时为 1Q2 翻转成 0J0、K0 同时为 1Q0 翻转成 0Q1 保持 1于是也落到 010。四个非法状态都会在 1 到 2 个时钟周期内回到合法循环里这就是自启动。写一小段 Python 脚本可以快速验证状态转移def jk_next(q, j, k): if j and not k: return 1 if k and not j: return 0 if j and k: return 1 - q return q def next_state(q2, q1, q0): j2 q1 and q0 k2 q2 and (q1 or q0) j1 (not q1) and ((not q2) or q0) k1 (not q2) and q1 and q0 j0 q2 ^ q1 k0 q0 return ( jk_next(q2, j2, k2), jk_next(q1, j1, k1), jk_next(q0, j0, k0), ) seen {} for s in range(8): q2 (s 2) 1 q1 (s 1) 1 q0 s 1 path [] while (q2, q1, q0) not in path: path.append((q2, q1, q0)) q2, q1, q0 next_state(q2, q1, q0) print(f初始 {path[0]} - 第一步 {path[1] if len(path)1 else } 循环长度 {len(path)})这段脚本把八个状态全部跑一遍如果每个状态的路径最终都能落进 010→011→100→101 这个环就说明自启动没问题。参数上要注意Python 里的 and/or 返回的是布尔值JK 触发器的 q 是 0 或 1所以在 jk_next 里直接返回 0/1不会出现 None 或字符串问题。3.3 逻辑图接线从触发器输出到 2-5 输出端得到激励方程后实际接线就是把门电路连接到三个 JK 触发器的 J/K 端。J2 用一只二输入与门输入接 Q1 和 Q0K2 用一只二输入或门接 Q1、Q0再和 Q2 相与J1 需要两只门组合先让 Q2 取反后与 Q0 相或再与 Q1 的反相相与K1 是三输入与门J0 用异或门K0 直接接 Q0。整体是简单的 SSI 门电路不需要锁存器或额外寄存器。输出端不需要二次译码因为 Q2Q1Q0 本身就是二进制编码的 2、3、4、5。若你想接数码管需要再送 BCD 转七段译码器因为数码管驱动要求的是 8421 BCD 码而不是原始三位二进制码。进位信号则可取 K2 的输出因为每当 Q2 被清零时就意味着状态要从 5 回到 2这个时刻就是一轮循环结束。4. 仿真验证让计数器按预期跑完一轮循环4.1 最小测试平台时钟、复位和一轮 2→3→4→5→2逻辑图画得对不对最快的方法是跑行为仿真。测试平台最小化到只要三样东西时钟发生器、复位信号、计数器实例。下面是一个完整的 testbenchmodule tb_counter_2_5; reg clk 0; reg rst_n 0; wire [2:0] q; counter_2_5 uut ( .clk(clk), .rst_n(rst_n), .q(q) ); always #5 clk ~clk; // 时钟周期 10ns always (posedge clk) begin if (rst_n) $display(t%0t q%b %d, $time, q, q); end initial begin $dumpfile(counter_2_5.vcd); $dumpvars(0, tb_counter_2_5); #10 rst_n 1; // 复位释放等待第一个上升沿 repeat (8) (posedge clk); $finish; end endmodule这里时钟用 always #5 clk ~clk生成 10ns 周期的方波。复位保持 10ns即一个完整时钟周期后才释放确保电源和复位建立稳定。repeat(8) 表示观察八个时钟沿足够覆盖 000→010→011→100→101→010 两轮循环。$display 会在每个时钟上升沿后打印当前 Q 值方便对照状态表。4.2 跑仿真并读结果四个边沿对应四个状态如果使用免费的 Verilog 仿真工具命令行方式如下iverilog -s tb_counter_2_5 -o counter_tb.out counter_2_5.v tb_counter_2_5.v vvp counter_tb.out也可以换成自己熟悉的图形化仿真器把 counter_2_5.v 和 testbench 一起加进工程即可。运行后打印结果应该是时钟边沿序号Q2 Q1 Q0十进制值第 1 个上升沿0 1 02第 2 个上升沿0 1 13第 3 个上升沿1 0 04第 4 个上升沿1 0 15第 5 个上升沿0 1 02注意如果在第 1 个上升沿看到的是 011先别急着改代码检查复位释放时间。由于复位释放后电路首先把 000 拉到 010这个跳变发生在复位释放后的第一个上升沿正好对应“2”。如果复位释放与时钟同时发生可能会看到复位竞争把第一个状态吃掉这个问题后面单独讲。4.3 波形检查时重点看哪几个信号时钟沿、复位释放、毛刺打开波形文件后我习惯先看三个东西。第一是复位释放位置是否在时钟低电平区间不要在时钟上升沿附近释放复位第二是 Q 端变化是否都在上升沿后出现若某个 Q 在时钟低电平期间变了通常说明组合逻辑存在竞争冒险第三是 J/K 信号本身有没有毛刺尤其要观察 J1 和 K1因为这两个信号用了取反和或门门延迟不匹配时容易在时钟沿附近出现极窄的尖峰。波形里还有一个容易被忽略的细节Q2 从 1 变 0 的时刻应该和 Q1 从 0 变 1 基本同步因为 J0 依赖 Q2 XOR Q1两个高位翻转时间相差太大会让 Q0 多翻一次。如果发现 Q0 出现双脉冲就要回到卡诺图检查是否把 J0 化简得太大或者给 K0 增加一个小的滤波电容。5. 计数器设计避坑五个常见翻车点5.1 上电停在非法状态计数器不进循环现象仿真时把复位释放后状态一直是 000 或 111看不到 2→3→4→5 的序列。原因电路没有做自启动设计非法状态各自形成死循环比如 111→111。解决把非法状态在激励表里显式处理至少要保证 000 和 111 能进入合法环。本文先前的方程已经在 000、001、110、111 上都做了跳转处理如果你自己化简时把非法状态全部当作 X 圈掉就可能得到非自启动电路。5.2 触发器的复位方式不统一状态错位一个周期现象板子实测时有时从 2 开始有时从 3 开始每上电结果都不一样。原因某些触发器用了异步复位另一些用了同步置数复位释放的瞬间三位的恢复时间不一致。解决所有触发器统一接同一个低电平复位并在复位释放后预留至少一个完整时钟周期再开始计数。不要在复位信号上串联电阻或电容那只会让沿更钝。5.3 JK 同时为 1 造成的翻转毛刺和竞争冒险现象波形图上 Q 端在同一个时钟沿出现快速 0→1→0 的跳动。原因某个位从 0 变 1 时J 和 K 同时为 1触发器实际发生翻转而相邻位的组合逻辑还在变化造成二次翻转。解决设计激励方程时尽量让“置位”和“清零”两支路径互斥避免 JK1 出现在关键转换上。如果电路已经焊好不方便改可以在 J/K 输入端加同步寄存器让激励信号和时钟严格同源同沿。5.4 把 Q2Q1Q0 直接当十进制输出数码管乱码现象LED 或数码管显示的不是 2、3、4、5而是跳出一堆乱码。原因大多数七段数码管需要 BCD 码而 Q2Q1Q0 是以 010、011、100、101 编码的原始状态直接接上去当然对不上。解决接一个 BCD 七段译码器或者用单片机读 Q 后再查表输出。若只想验证逻辑功能用三只 LED 分别显示 Q2、Q1、Q0 就够了010 灯亮状态就是计数值 2。5.5 复位时间太短上电瞬间就把 2 漏掉现象示波器看到计数器启动后第一个状态是 3而不是 2。原因复位信号宽度小于一个时钟周期电路还没从 000 跳到 010第一次有效上升沿就把它当成了 010 本身直接推进到 011。解决复位低电平至少要维持两个时钟周期以上并在时钟低电平中段释放。上电复位电路可以用施密特触发器反相器加 RC 延迟RC 时间常数取时钟周期的 10 倍以上。6. 进阶用法把一个固定序列计数器变成可预置分频器6.1 加一个置数端计数器就能从指定位置重新开始固定 2→3→4→5 的计数器再往上走一步就是加置数端。常见做法是把拨码开关预置到 010当 LD 信号有效时在下一个时钟沿把 Q2、Q1、Q0 分别装入预设值。这样不但能随时让状态回到 2还能通过改变预置数改变起始位置把一个固定序列计数器变成可编程的分频器。比如预置 100 时计数序列变成 4→5→2→3输出脉冲密度不变但相位整体移动了。实际使用时LD 信号要和时钟同步否则仍然会出现上一章说的竞争问题。6.2 用级联和进位输出扩展计数范围单个计数器只有 4 拍。要做更长定时把低位计数器的进位输出接到高位计数器的时钟端就得到级联结构。低位从 5 跳回 2 时K2 产生一个低脉冲经过简单整形后送给高位时钟高位计数器加 1。这样两级级联后周期为 16 个低位周期输出频率是输入时钟的十六分之一。级联时要注意高位时钟触发沿低位进位信号翻转沿不能太缓建议用上升沿触发的边沿检测电路整形后再接高位。6.3 一次系统性自检脚本模拟八个周期验证自启动最后分享一个我常用的收尾动作把设计好的电路在软件里做一次全状态遍历。做法是把上一章的 Python 脚本扩展一下从 8 个状态分别运行 8 个时钟周期断言最终都进入 010→011→100→101 环。只要这个断言过了综合和接线再出问题基本可以排除逻辑功能错误。固定序列计数器最怕的不是门电路复杂而是某些“想当然能忽略”的非法状态在断电上电时偷偷接管电路。我习惯把这套自检脚本留在工程目录里每次改完激励方程都跑一遍给自己留一张后悔药。希望这篇从状态表到仿真、再到实物排错的笔记能帮你少走一圈弯路也希望你能把这套自启动的思路用在你手头的下一个序列控制电路上。本文还有配套的精品资源点击获取
返回列表
PREV
查看更多资讯
NEXT
返回资讯列表