news 2026/10/5 6:19:58

运动码表状态机设计:Excel状态转换表与Logisim实现

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
运动码表状态机设计:Excel状态转换表与Logisim实现

没做过这个项目的人可能很难理解,为什么一个运动码表要和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次态Q1'Q0'
S00000S000
S00010S101
S000X1S000
S10100S101
S10110S210
S101X1S000
S21000S210
S21010S101
S210X1S000
1111X0S000
1111X1S000

注意表里我用X代表“任意值”,这是状态表里常用的简写。关于无效状态11那两行,无论A和B是什么,次态都设计成S0,这是我自己加上的兜底功能。从逻辑设计角度看,无效状态的存在是常态而非异常,处理得当才能保证电路上电后永远在可控状态集合里循环。

2.3 Excel表格结构与公式辅助校验

把上面的表敲进Excel之后,别急着走下一步,先用几个小技巧把表格做成“半自动”的,方便检查错误。

第一列和第五列可以设置数据有效性下拉,这样每行只能选S0/S1/S2/11,不会因为手滑打错状态名。更关键的是加一列“合法性检查”,用公式判断当前行是否矛盾。比如复位键B=1的情况,无论A是什么,次态都必须是S0,那就可以写:

=IF(AND(B=1, 次态<>"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行(其中A=X、B=1的行合并了情况),11状态是2行。如果统计结果和预期不符,多半就是漏行了。

提示:用Excel建模状态机,最大的价值不是“画张好看的表格”,而是用公式把状态机内部的一致性规则自动检查出来。任何一行转换条件和规则冲突,Excel能立刻标红,这在纯纸面上是做不到的。

3. 输出函数推导:从真值表到逻辑表达式

3.1 输出变量的定义

状态转换表解决的是“下一个状态怎么来”的问题,接下来要解决“当前状态下输出什么”的问题。运动码表的输出函数相对简单,核心输出就是计数器的控制信号。我定义了三个输出:

  • EN:计数器使能信号,EN=1时计数器累加,EN=0时保持。
  • CLR:计数器清零信号,高电平清零。
  • S_LED:状态指示,比如暂停状态用一个LED点亮提示。

根据状态定义,这三个输出在每个状态下的取值很清晰:

状态Q1Q0ENCLRS_LED
S0(停止)00010
S1(计时)01100
S2(暂停)10001
非法11010

这里要特别说明一下CLR信号。S0状态下CLR=1,意味着只要处于停止状态,计数器就持续被清零,这样才能保证从S0进入S1时,计时是从零开始的。有些设计会把清零信号做成一个脉冲而不是电平,但作为课程设计,电平清零更简单直观,配合计数器同步清零端使用,效果完全没问题。非法状态11的CLR也设为1,一方面兜底清零,一方面配合次态回到S0,双保险。

3.2 用Excel辅助化简逻辑表达式

有了输出真值表,接下来就是化简。三个输出分别以Q1、Q0为变量列出函数:

EN = 1的条件是状态为01,也就是Q1=0且Q0=1。写成逻辑表达式就是:

EN = (¬Q1) · Q0

CLR = 1的条件是状态为00或11,也就是Q1和Q0相同的两种情况(同或关系):

CLR = ¬(Q1 ⊕ Q0) = (¬Q1 · ¬Q0) + (Q1 · Q0)

S_LED = 1的条件是状态为10,即Q1=1且Q0=0:

S_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·Q0,S_LED需要Q1·¬Q0。如果后面要用D触发器实现状态机,激励方程里也会大量出现¬Q1·¬Q0这类乘积项。提前在Logisim里把¬Q1、¬Q0这些信号引出来作为公共总线,再分别接到各个与门,电路的整洁度会高很多,排错也容易。

4. 实操:从Excel表到可运行电路

4.1 选定触发器与激励方程推导

状态转换表里的“次态”只是目标,真正让触发器“动起来”还需要把次态转换成触发器的激励输入。课程设计里我最常用的是D触发器,因为D触发器的特性方程最简单:Q' = D。也就是说,触发器的D输入直接等于次态值,不需要像JK触发器那样额外列激励表。

回到状态转换表,把Q1'和Q0'单独提取出来,根据当前状态和输入A、B写出激励方程。这里有个处理技巧:因为B=1时无条件回到S0,所以先处理B=1的情况,再看B=0时A的作用。

当B=1时,Q1'=0,Q0'=0。

当B=0时,看A和当前状态:

  • 当前00:A=0次态00,A=1次态01
  • 当前01:A=0次态01,A=1次态10
  • 当前10:A=0次态10,A=1次态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的表达式里,就能保证B=1时强制D1=D0=0。

第四步,根据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。

第二步,动态跑测。切换到自动时钟,观察计数器是否稳定累加,EN=0时是否保持,CLR=1时是否归零。重点观察暂停状态下计数器有没有偷跑。我在实测中遇到过EN信号有一个毛刺,导致暂停瞬间计数器多跳了一个数,后来发现是组合逻辑的门延迟造成的,加上输出寄存器或调整时钟边沿才解决。

第三步,边界测试。连续快速按A键10次,看状态是否始终在三个状态里循环,是否出现跳到11非法状态的情况;在计时状态按复位,确认立即回S0且计数器清零;在暂停状态按复位,确认显示清零而不是继续计时。

这三步都过了,基本可以认为状态机的时序逻辑是正确可靠的。

5. 常见问题与排查技巧实录

5.1 Excel建模阶段的坑

先说说Excel这边的几个问题。第一个是状态表里X(任意值)没展开就急着化简,导致卡诺图错误。Excel表里可以写X表示任意,但到了要推导激励方程时,必须把X展开成0和1两种情况去填卡诺图,否则化简结果会丢掉有效项。我的做法是在Excel里单独建一个“展开表”,把带X的行拆成具体行,再基于展开表做后续推导。

第二个问题是公式里的引用范围。用COUNTIFS检查覆盖行数时,我一开始用的是类似A2:A10这样的固定范围,后来临时加了行,范围没扩,统计结果就错了。建议直接用整列引用(如A:A)或者把数据区域定义为Excel表格(Ctrl+T),这样新增行的时候统计范围能自动跟着扩展。

第三个问题是“复用状态表”时的编码混乱。我曾经同时维护了两份Excel,一份用状态名,一份用编码,后来改了一个状态的编码,另一份忘改了,导致后面所有激励方程全错。教训就是:编码信息只维护一份,其他表全部用查找公式引用,绝不手工复制。

5.2 状态机设计的经典错误

状态机设计里最常见的错误,就是漏处理无效状态。之前已经说过,三个状态用两个触发器会多出编码11。有些同学在Logisim里测试时发现电路“偶尔失灵”,大概率就是仿真初始化随机进入11状态后,因为没有转移路径而一直卡在那里。解决方式就是状态表里强制给11状态加转移行,11在任何输入下都回到S0。

第二个经典错误是把复位信号的优先级搞反。状态转换表里B=1时要忽略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草稿也是你整个设计过程最扎实的背书。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/5 6:19:31

Dinic算法复杂度O(V²E)详解:从阻塞流到当前弧优化

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/5 6:19:04

无限debugger反调试绕过实战:三种方法彻底解决DevTools卡死

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/5 6:19:04

基于CNN的疲劳驾驶检测系统实战:SSD300与VGG16源码全解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/5 6:18:42

快递微服务架构实战:业务域拆分与Nacos动态配置

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/5 6:18:12

步进电机开环控制系统设计:基于8086与8255A/8253的完整实现

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/5 6:18:11

TCP通讯录应用:协议选型与C语言实现原理

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华