
没做过这个项目的人可能很难理解为什么一个运动码表要和Excel扯上关系。但如果你正好在学数字逻辑、计算机组成原理这类课或者你正在用Logisim做课程设计那“运动码表”大概率不是你在操场上看秒表那个东西而是一个需要自己设计有限状态机FSM的时序逻辑电路。把状态转换表和输出函数提前在Excel里理清楚往往比直接上手拉电路要快得多也能少烧不少脑细胞。这篇文章就围绕“运动码表—状态转换和输出函数的Excel表”这个主题把我实际做这个项目时完整的设计思路、Excel建模方法、输出函数推导过程、以及最终落到Logisim电路里的步骤都拆开讲一遍。核心内容包含三件事状态机怎么设计、Excel怎么用来整理状态转换表、输出函数怎么从真值表变成逻辑表达式。这套流程同样适配任何需要用FSM实现的数字电路项目比如密码锁、自动售货机、交通灯控制器原理完全一致学会一套就能通吃。1. 项目概述运动码表为什么需要状态机1.1 运动码表的真实使用场景运动码表本质就是一个带暂停、继续、清零功能的计时器。我们在操场上按秒表按一下开始计时再按一下暂停长按或其他按键复位归零。听起来很简单但用数字电路实现时它天然就是一个有限状态机。为什么因为码表在任意时刻一定处于且只能处于一个明确的工作状态而且下一个状态由当前状态和按键输入共同决定。比如未开始计时的时候按启动键进入计时状态正在计时的时候按同一颗键进入暂停状态暂停的时候再按一次又回到计时状态无论哪个状态按复位键都回到初始停止状态。这就是教科书上最典型的Moore型或Mealy型状态机场景。有的同学可能会问直接用计数器加一个使能端不就行了吗确实计时功能本身用计数器就能做但“使能端在什么时候有效、什么时候无效、清零信号怎么产生”这些控制逻辑才是状态机真正要解决的。你不可能让计数器自己知道自己该不该走它只是个被控对象。所以运动码表的整个控制核心就是那个负责调度状态的FSM计数器只是FSM输出函数驱动的一个执行单元。1.2 为什么选择先用Excel搭建状态表我在课程设计那会儿见过太多人一上来就打开Logisim拖几个触发器、接一堆门结果拉到一半发现自己漏了一个状态转换条件又要全部推翻重来。这种折腾其实完全可以避免。先花半小时在Excel里把所有状态、输入、次态、输出列成一张表有几个非常实际的好处。第一状态转换关系一目了然漏条件、重复条件的低级错误在表格里一眼就能发现。第二Excel的公式功能可以做交叉校验比如用VLOOKUP反查每个状态在当前输入下的次态是否唯一比人眼扫表格靠谱得多。第三状态表和输出真值表整理清楚之后卡诺图化简、逻辑表达式推导都有据可依最终落到电路图的时候基本就是照抄不会出现“逻辑想当然”的问题。我自己习惯把Excel当成“纸面验证平台”状态表推完再用它生成激励表的中间结果确认每个触发器下一状态的值。这样真正去Logisim里连线的时候脑子里已经是完全清晰的。接下来就按这个流程从状态定义开始一步步讲。2. 状态转换表的设计与Excel建模2.1 状态定义与编码先把运动码表的状态数清楚。按之前描述的功能完整的状态至少有三个S0停止/复位状态计数器的使能无效显示清零或保持零。S1计时状态计数器正常累加。S2暂停状态计数器停止但显示保持当前值。这里我刻意把“暂停”和“停止”分开因为这两个状态在实际物理行为上完全不同停止必须把显示清零暂停则要保留当前计时结果。如果把两者合并成一个状态复位和暂停的区分就得靠输出函数额外处理反而把逻辑搞复杂了。状态编码有讲究。三个状态最少需要两个触发器编码方案可以是状态编码(Q1Q0)说明S000停止/复位S101计时S210暂停未使用11必须处理否则会进入非法状态这里有个特别重要的点三个状态用了两个比特那么编码11属于未定义状态。如果电路上电或受到干扰进入11必须让它能自动回到有效状态否则整个码表就“卡死”在既不计时也不暂停的怪状态里。这就是为什么状态表里一定不能漏掉无效状态的转移方向。至于11到底跳到哪个状态课程设计里我习惯让它回到S0因为从安全性和可恢复性来说回到停止状态永远是最稳妥的。2.2 转换条件梳理输入信号的定义输入信号怎么定直接决定状态表的列。我见过有人把“开始”“暂停”“复位”分别定义成三个独立输入这样做的后果是逻辑表达式非常庞大而且可能出现多个按键同时按下的非法输入组合。更常规的做法是只定义两个输入A启停键Start/Stop每按一次产生一个高电平脉冲作用是启动计时或暂停/继续切换。B复位键Reset高电平有效任何时候按下都回到S0。按键脉冲在真实电路里需要做消抖和边沿检测但在状态机设计阶段我们先假设输入是干净的单脉冲。这里有个设计细节值得注意A键在S0状态下按一下进入S1开始计时在S1状态下按一下进入S2暂停在S2状态下按一下又回到S1继续计时所以A键在三态之间起的是“切换”作用而B键在所有状态下都强制回到S0。根据这些条件可以画出完整的状态转换表。我把状态转换表直接做成Excel的格式列分别是当前状态、当前状态编码、输入A、输入B、次态、次态编码。每一行代表一个转换分支。完整表格如下当前状态Q1Q0AB次态Q1Q0S00000S000S00010S101S000X1S000S10100S101S10110S210S101X1S000S21000S210S21010S101S210X1S0001111X0S0001111X1S000注意表里我用X代表“任意值”这是状态表里常用的简写。关于无效状态11那两行无论A和B是什么次态都设计成S0这是我自己加上的兜底功能。从逻辑设计角度看无效状态的存在是常态而非异常处理得当才能保证电路上电后永远在可控状态集合里循环。2.3 Excel表格结构与公式辅助校验把上面的表敲进Excel之后别急着走下一步先用几个小技巧把表格做成“半自动”的方便检查错误。第一列和第五列可以设置数据有效性下拉这样每行只能选S0/S1/S2/11不会因为手滑打错状态名。更关键的是加一列“合法性检查”用公式判断当前行是否矛盾。比如复位键B1的情况无论A是什么次态都必须是S0那就可以写IF(AND(B1, 次态S0), 错误复位状态下次态必须是S0, OK)类似这样的检查公式把“复位优先于启动”的规则固化成Excel逻辑比我对着表格肉眼扫十遍都管用。还有一个非常实用的做法用COUNTIFS做覆盖性检查。状态机的每个当前状态都要覆盖所有输入组合漏了一个组合电路就会在某个输入下“无路可走”。我在表尾加了个统计区COUNTIFS(A:A, S0) COUNTIFS(A:A, S1) COUNTIFS(A:A, S2) COUNTIFS(A:A, 11)每个状态的覆盖行数必须是整数且和输入组合数对上。S0、S1、S2按表中写法分别是3行其中AX、B1的行合并了情况11状态是2行。如果统计结果和预期不符多半就是漏行了。提示用Excel建模状态机最大的价值不是“画张好看的表格”而是用公式把状态机内部的一致性规则自动检查出来。任何一行转换条件和规则冲突Excel能立刻标红这在纯纸面上是做不到的。3. 输出函数推导从真值表到逻辑表达式3.1 输出变量的定义状态转换表解决的是“下一个状态怎么来”的问题接下来要解决“当前状态下输出什么”的问题。运动码表的输出函数相对简单核心输出就是计数器的控制信号。我定义了三个输出EN计数器使能信号EN1时计数器累加EN0时保持。CLR计数器清零信号高电平清零。S_LED状态指示比如暂停状态用一个LED点亮提示。根据状态定义这三个输出在每个状态下的取值很清晰状态Q1Q0ENCLRS_LEDS0停止00010S1计时01100S2暂停10001非法11010这里要特别说明一下CLR信号。S0状态下CLR1意味着只要处于停止状态计数器就持续被清零这样才能保证从S0进入S1时计时是从零开始的。有些设计会把清零信号做成一个脉冲而不是电平但作为课程设计电平清零更简单直观配合计数器同步清零端使用效果完全没问题。非法状态11的CLR也设为1一方面兜底清零一方面配合次态回到S0双保险。3.2 用Excel辅助化简逻辑表达式有了输出真值表接下来就是化简。三个输出分别以Q1、Q0为变量列出函数EN 1的条件是状态为01也就是Q10且Q01。写成逻辑表达式就是EN (¬Q1) · Q0CLR 1的条件是状态为00或11也就是Q1和Q0相同的两种情况同或关系CLR ¬(Q1 ⊕ Q0) (¬Q1 · ¬Q0) (Q1 · Q0)S_LED 1的条件是状态为10即Q11且Q00S_LED Q1 · (¬Q0)这三个表达式都非常简单。但如果是更复杂的项目输出变量多、状态也多手工化简容易出错这时候Excel就能派上用场。方法不复杂把所有输入组合和输出值列出真值表然后按卡诺图的思路在Excel里用条件格式把小方格标成高亮填入输出值利用条件格式的颜色块观察相邻1的合并规律比拿笔画方格纸更快改起来也方便。有个小技巧可以分享Excel里做卡诺图可以用矩阵区域行标签是Q1Q0的取值列标签是输入A和B的取值每个交叉格填输出值。填完之后用条件格式把值为1的格子标成浅黄色背景哪些1相邻、哪些1可以通过折叠合并一眼就能看出来。表达式化简完再用Excel的布尔值对照表验证一步把化简后的表达式在表格里用AND、OR、NOT函数展开成公式列对比原真值表的输出完全一致才算通过。这一步叫“逻辑一致性校验”我在实际项目中几乎每次都做因为它能挡住绝大部分化简错误。3.3 输出方程与电路对应化简出的表达式最终都要对应到门电路。比如EN (¬Q1) · Q0需要两个输入Q1经过非门得到¬Q1再和Q0一起进入与门输出就是EN。CLR ¬(Q1 ⊕ Q0)可以用一个异或门加非门也可以直接用同或门器件。S_LED Q1 · (¬Q0)则是一个非门加一个与门。我在这类项目中总结了一个心得不要让每个输出函数都单独搭一套逻辑可以提前把公共部分提取出来。例如CLR表达式里同时包含了¬Q1·¬Q0和Q1·Q0而EN只需要¬Q1·Q0S_LED需要Q1·¬Q0。如果后面要用D触发器实现状态机激励方程里也会大量出现¬Q1·¬Q0这类乘积项。提前在Logisim里把¬Q1、¬Q0这些信号引出来作为公共总线再分别接到各个与门电路的整洁度会高很多排错也容易。4. 实操从Excel表到可运行电路4.1 选定触发器与激励方程推导状态转换表里的“次态”只是目标真正让触发器“动起来”还需要把次态转换成触发器的激励输入。课程设计里我最常用的是D触发器因为D触发器的特性方程最简单Q D。也就是说触发器的D输入直接等于次态值不需要像JK触发器那样额外列激励表。回到状态转换表把Q1和Q0单独提取出来根据当前状态和输入A、B写出激励方程。这里有个处理技巧因为B1时无条件回到S0所以先处理B1的情况再看B0时A的作用。当B1时Q10Q00。当B0时看A和当前状态当前00A0次态00A1次态01当前01A0次态01A1次态10当前10A0次态10A1次态01当前11次态00把这个表用卡诺图化简可以得到D1和D0的表达式D1 (¬B) · [ (Q1 · ¬Q0 · ¬A) (¬Q1 · Q0 · A) ] D0 (¬B) · [ (¬Q1 · ¬Q0 · A) (¬Q1 · Q0 · ¬A) (Q1 · ¬Q0 · A) ]这里如果不做合并表达式会显得很长。实际化简后D1可以写成(¬B)·(Q1∧¬Q0∧¬A ∨ ¬Q1∧Q0∧A)D0可以写成(¬B)·(Q0∧¬A ∨ ¬Q0∧A∧(¬Q1∨Q1)) (¬B)·(Q0·¬A ¬Q0·A)注意这里Q1和¬Q1互为补所以可以合并掉一个变量最终D0 (¬B)·(Q0 ⊕ A)这个异或形式在做电路时就非常简洁。化简过程如果在纸面上推容易乱建议还是回到Excel把各状态的Q1、Q0、A、B和对应的D1、D0真值表列出来用卡诺图或逻辑公式化简确认后再往下走。4.2 Logisim搭建步骤D触发器方案下搭建步骤可以拆成清晰的几步照做基本不会乱。第一步拖两个D触发器Logisim里在Memory库里或者用FF库的D Flip-Flop分别命名为Q1和Q0再接一个公共时钟源。时钟频率先用默认的1Hz或手动时钟都行验证逻辑时我一般用手动时钟逐拍触发方便观察。第二步把两个触发器的Q端和¬Q端都用Splitter引出来为后面的组合逻辑提供信号源。第三步搭复位优先电路。B按键通过一个与门控制所有回S0路径实际上只要把B取反后作为公共使能条件与到D1、D0的表达式里就能保证B1时强制D1D00。第四步根据D1和D0的化简表达式用与门、或门、非门、异或门搭建组合逻辑。公共的¬B信号拉一根总线出来分别接到两个D表达式的与门输入端。第五步接输出函数。EN、CLR、S_LED按第3节的表达式接到对应的门输出EN再连到计数器的计数使能端CLR连到计数器清零端。第六步添加计数器。在Logisim的Arithmetic库里找到Counter设置合适的位宽。码表要显示分秒的话计数器需要多位BCD计数这个可以后续扩展。验证阶段先用一个4位计数器顶住看EN和CLR信号控制它是否正确执行“走、停、清零”三种行为。第七步加显示。可以用Logisim的Hex Digit Display接计数器的输出。按下A键从S0进S1计数器开始走再按A进入S2计数器停住按B回到S0计数器清零。S_LED在S2状态点亮。注意Logisim里的按键需要设置成“按一下产生一个脉冲”而且一定要做“防抖处理”。如果按键信号在跳变沿反复抖动状态机可能会一次按多次跳转从S0直接跳到S2甚至跳回S0。常用的办法是给按键加一个上升沿检测电路用另一个D触发器把按键信号延迟一拍原信号和延迟信号取与非就能得到一个稳定脉冲。4.3 时序验证方法电路搭完不能直接交差一定要做时序验证。我的习惯是分三步验证。第一步静态检查。用Logisim的“时钟脉冲”工具手动打拍每按一次时钟观察两个触发器Q端是否按照状态转换表变化。比如在S0状态按A后打一拍时钟Q1Q0应该从00变成01。第二步动态跑测。切换到自动时钟观察计数器是否稳定累加EN0时是否保持CLR1时是否归零。重点观察暂停状态下计数器有没有偷跑。我在实测中遇到过EN信号有一个毛刺导致暂停瞬间计数器多跳了一个数后来发现是组合逻辑的门延迟造成的加上输出寄存器或调整时钟边沿才解决。第三步边界测试。连续快速按A键10次看状态是否始终在三个状态里循环是否出现跳到11非法状态的情况在计时状态按复位确认立即回S0且计数器清零在暂停状态按复位确认显示清零而不是继续计时。这三步都过了基本可以认为状态机的时序逻辑是正确可靠的。5. 常见问题与排查技巧实录5.1 Excel建模阶段的坑先说说Excel这边的几个问题。第一个是状态表里X任意值没展开就急着化简导致卡诺图错误。Excel表里可以写X表示任意但到了要推导激励方程时必须把X展开成0和1两种情况去填卡诺图否则化简结果会丢掉有效项。我的做法是在Excel里单独建一个“展开表”把带X的行拆成具体行再基于展开表做后续推导。第二个问题是公式里的引用范围。用COUNTIFS检查覆盖行数时我一开始用的是类似A2:A10这样的固定范围后来临时加了行范围没扩统计结果就错了。建议直接用整列引用如A:A或者把数据区域定义为Excel表格CtrlT这样新增行的时候统计范围能自动跟着扩展。第三个问题是“复用状态表”时的编码混乱。我曾经同时维护了两份Excel一份用状态名一份用编码后来改了一个状态的编码另一份忘改了导致后面所有激励方程全错。教训就是编码信息只维护一份其他表全部用查找公式引用绝不手工复制。5.2 状态机设计的经典错误状态机设计里最常见的错误就是漏处理无效状态。之前已经说过三个状态用两个触发器会多出编码11。有些同学在Logisim里测试时发现电路“偶尔失灵”大概率就是仿真初始化随机进入11状态后因为没有转移路径而一直卡在那里。解决方式就是状态表里强制给11状态加转移行11在任何输入下都回到S0。第二个经典错误是把复位信号的优先级搞反。状态转换表里B1时要忽略A强制回S0而有些设计先判断A后判断B会导致复位和启动同时按下时行为不确定。我在推导D表达式时专门把¬B作为一个公共因子提出来就是为了从结构上保证复位优先。如果你的表达式里没有这个公共因子建议回头检查一下。第三个错误是触发器的时钟边沿选择不当。Logisim里的D触发器默认是上升沿触发如果你的计数器和状态机触发器一个是上升沿一个是下降沿在自动时钟下偶尔能跑通但手动时钟验证时就会出现“按一下没反应”的诡异问题。统一用上升沿触发是所有状态机设计的一致约定。5.3 从Excel到电路衔接的实用技巧最后分享几个我实操中觉得特别有用的小技巧。第一个技巧是“先画Excel连线图再拉电路”。不是真的画电线而是在Excel里用矩形表示触发器、用文字描述门连接关系把D1、D0的每个乘积项来源标清楚。这个过程相当于在纸上把电路“预演”了一遍Logisim里拉线速度会快很多而且少出错。第二个技巧是给Logisim里的每个元件都改名。默认的D触发器叫“D Flip-Flop”计数器叫“Counter”两个长得一样连线一多完全分不清。我把它们重命名为Q1、Q0、Counter、EN_Gate、CLR_Gate等排错时按照名字就能快速定位到具体逻辑块。这个习惯在复杂一点的项目里节省的时间非常可观。第三个技巧是用Logisim的“组合逻辑分析”功能做交叉验证。把状态转换表的每一行输入组合喂进去让Logisim自动生成真值表和表达式跟我用Excel推出来的结果做对比。如果两边有差异说明有一方错了优先检查Excel展开表因为Logisim的分析功能是经过验证的、可靠性很高。这个双保险机制帮我抓住过至少两处卡诺图化简的错误。第四个小技巧也是我一直在用的给状态机加一个“上电复位”电路。Logisim仿真刚开始时触发器输出是未知的状态不确定直接跑会出现随机行为。在电路里加一个RC电路中常用的复位电路或者在仿真开始前手动按一次B键复位先把状态定到S0后面所有验证结果才有意义。如果是实物板子上电复位电路几乎是必须的这个习惯从仿真阶段就要养成。第五个技巧是关于计数器位宽的。课程设计如果只要求“能走能停能清零”4位计数器就够了。但如果要求显示到分和秒那需要把计数器拆成秒个位、秒十位、分个位每个都是8421 BCD计数需要额外的模60和模10设计。这个扩展在状态机层面不用动只要把EN和CLR信号并行接到所有计数器就行状态机的复用能力在这个场景下体现得特别明显。做运动码表这个项目我把最大的技术精力花在了状态转换表的设计和输出函数的化简上Excel在其中扮演了“外部大脑”的角色。它不需要有多高深的功能无非就是表格、公式、条件格式这几样基础能力但在组织逻辑关系、自动校验一致性方面的效率比纯脑内推演高出一截。如果你也在做类似的数字逻辑设计题我强烈建议先把状态表写到Excel里去哪怕最后交付的成果只是Logisim里的电路图Excel草稿也是你整个设计过程最扎实的背书。