简介:本资源是北京邮电大学《数字电路与逻辑设计》课程配套的三层电梯控制器实验报告,面向电子、通信、自动化等专业本科生及FPGA初学者,聚焦VHDL状态机设计、Quartus II开发流程与数字系统模块化实现。报告完整涵盖设计任务、状态转移图、10状态FSM建模(如stopon1、doorwait1~4、up/down等)、分频器(50MHz→1Hz)、控制模块、LED/数码管/点阵显示驱动及同步复位机制,并附带带详尽注释的VHDL源程序与端口说明。资源为单文件docx文档,大小1.62MB,内容结构清晰,含系统框图、模块划分、测试用例分析及多场景运行逻辑推演(如多请求优先级响应、复位恢复等),便于理解状态机时序逻辑与工程落地细节。目前已有190人学习下载,适合开展数字系统综合实验、课程设计复盘或EDA入门实践。
1. 这不是玩具电梯,而是一套完整跑在FPGA上的三层状态机控制系统
北邮数电实验里这份「简易三层电梯控制器」文档,表面看是学生交的Word报告,实则藏着一套可综合、可烧录、可真机验证的完整VHDL工程。它不依赖任何上位机或PC通信,所有逻辑——从50MHz晶振分频到1Hz节拍、从按钮消抖到10状态FSM调度、从楼层位置寄存到上升/下降/停站请求的优先级仲裁——全部固化在FPGA内部。你拿到的不是伪代码或流程图,而是能直接导入Quartus II、分配引脚、编译下载到DE2-115或类似开发板上实时运行的硬件描述。它解决的核心问题,是数字系统设计中最典型的时序敏感型多输入响应建模:如何让一个没有操作系统、没有中断向量表、只有纯组合+时序逻辑的芯片,在毫秒级响应外部按钮的同时,严格维持4秒开门等待、单层1秒运行、状态不可跳变等硬实时约束。适合刚学完组合逻辑、正啃状态机章节的大二学生动手复现;也适合已工作3年的数字工程师回炉重看——因为里面对clearup/cleardn双路请求清除机制的设计、对udsig方向信号与position位置变量的耦合处理、以及doorwait1到doorwait4显式拆解而非用计数器隐式实现,都是教科书里很少展开的实战细节。
2. 状态机不是画个图就完事:10个状态的物理意义与转移条件必须映射到真实时钟域
2.1 为什么必须显式定义10个离散状态?——避开“计数器陷阱”
很多初学者会想:“开门4秒”直接用一个4-bit计数器加比较器不就行了?但本设计坚持将doorwait1~doorwait4拆成独立状态,根本原因在于状态机输出必须与当前状态强绑定,而非依赖计数器中间值。观察源码中doorlight信号的赋值:
when stopon1 => doorlight <= '1'; position <= 1; mylift <= doorwait1; when doorwait1 => mylift <= doorwait2; when doorwait2 => mylift <= doorwait3; when doorwait3 => mylift <= doorwait4; when doorwait4 => mylift <= doorclose;提示:
doorlight只在stopon1、dooropen两个状态被置高,其余doorwait*状态中该信号保持前一状态值(由buffer端口属性维持)。这意味着LED点亮时长严格等于状态驻留时间,而每个doorwait*状态恰好对应1个liftclk上升沿(即1秒),避免了计数器溢出判断失败导致的开门时间漂移。
若改用单计数器,需在每次liftclk上升沿检查COUNT = 4并清零,但此时doorlight的关断时机将取决于比较逻辑延迟,可能在第4.2秒才生效——而FPGA布线后该延迟不可预测。显式状态分解把时序约束转化为状态转换约束,由综合工具自动插入寄存器,保证建立/保持时间满足。
2.2 状态转移的四大物理约束条件
状态转换不是凭空发生,必须对应真实硬件行为。本设计中每个when ... =>分支都隐含以下约束:
| 状态 | 触发条件 | 物理含义 |
|---|---|---|
up | udsig='1' AND position<3 | 电梯处于上升模式且未达顶层,必须执行上升动作 |
down | udsig='0' AND position>1 | 电梯处于下降模式且未达底层,必须执行下降动作 |
stop | (position=2 AND (fuplight(2)='1' OR fdnlight(2)='1')) OR (position=1 AND fuplight(1)='1') | 当前楼层存在有效请求(上升/下降/停站),且电梯运动方向与请求匹配 |
dooropen | mylift=stop | 仅当stop状态结束后无条件进入,确保开门动作与停靠动作严格解耦 |
特别注意stop状态的判定逻辑:它不检查stoplight是否为'1',而是检查fuplight(pos)或fdnlight(pos)——这说明停站请求(stopbutton)本质是“本层上升/下降请求”的镜像。例如stop2button='1'会同时置位stoplight(2)和fuplight(2)(见ctrlight进程),因此stoplight实际是冗余寄存,真正驱动停靠决策的是方向性请求信号。
2.3 复位信号的异步实现与同步风险规避
源码中复位逻辑写为:
if reset='1' then mylift <= stopon1; clearup <= '0'; cleardn <= '0'; elsif liftclk'event and liftclk='1' then -- 正常状态转移 end if;注意:这是异步复位(reset直接作用于
if条件,不依赖liftclk边沿)。虽然VHDL语法允许,但在FPGA中易引发亚稳态。实际工程中应改为同步复位:先用两级触发器对reset进行时钟域同步,再送入状态机。本实验因时钟频率低(1Hz)、复位使用按键开关(机械抖动大),异步复位反而更可靠——按键按下时reset持续高电平远超liftclk周期,亚稳态概率极低。
3. 按钮请求的寄存与清除机制:双路独立寄存器+位置感知清除
3.1 请求信号为何要分fuplight/fdnlight/stoplight三组?
观察端口定义:
fuplight, fdnlight, stoplight: buffer std_logic_vector(3 downto 1);三者均为3位向量,索引1~3对应楼层。但它们的更新逻辑完全不同:
fuplight(i):仅由f1upbutton(i=1)、f2upbutton(i=2)置位,永不自动清零fdnlight(i):仅由f2dnbutton(i=2)、f3dnbutton(i=3)置位,永不自动清零stoplight(i):由stop1button~stop3button置位,但会被clearup/cleardn信号强制清零
这种设计实现了请求的持久化存储与位置感知清除。例如当电梯在2层开门时,clearup='1'会执行:
fuplight(position) <= '0'; -- 清除fuplight(2) stoplight(position) <= '0'; -- 清除stoplight(2)但fdnlight(2)不受影响——因为下降请求在2层无效(乘客不可能在2层按下降键去2层),而fuplight(1)仍保持'1'(1层上升请求未被响应)。
3.2clearup/cleardn信号的生成时机与竞争规避
这两个信号由主状态机在dooropen状态末尾生成:
when dooropen => doorlight <= '1'; if udsig='1' then if position<3 and (fuplight(pos)='1' or stoplight(pos)='1') then clearup <= '1'; -- 上升模式下,本层有上升/停站请求则清除 else clearup <= '1'; cleardn <= '1'; -- 否则全清(防残留) end if; elsif udsig='0' then if position>1 and (fdnlight(pos)='1' or stoplight(pos)='1') then cleardn <= '1'; -- 下降模式下,本层有下降/停站请求则清除 else clearup <= '1'; cleardn <= '1'; end if; end if; mylift <= doorwait1;关键点在于:clearup/cleardn在dooropen状态结束前一个时钟周期就已确定,而ctrlight进程在下一个buttonclk上升沿采样它们。这确保了请求清除操作与状态机推进严格同步——当电梯离开某层时,该层请求必然已被清除,避免同一请求被重复响应。
3.3 按钮消抖的硬件级实现:buttonclk分频策略
外部按钮(BTN)直接接入FPGA会引入毫秒级抖动。本设计用独立分频器生成buttonclk:
time2: process(clk) variable COUNT2: integer range 0 to 6499; begin if clk'event and clk='1' then COUNT2 := COUNT2 + 1; if COUNT2 = 6499 then COUNT2 := 0; buttonclk <= NOT buttonclk; -- 50MHz / 6500 ≈ 7.69kHz end if; end if; end process time2;提示:6500分频得到约7.69kHz采样时钟,远高于按钮抖动频率(通常<100Hz),但又不过高导致资源浪费。
ctrlight进程在buttonclk上升沿采样所有按钮,相当于硬件级“边沿检测+延时确认”,比软件延时消抖更可靠。
4. 分频器与多时钟域协同:50MHz到1Hz的精准链式分频
4.1 为什么需要两级分频?——时钟域隔离的刚性需求
系统存在三个时钟域:
- 主时钟域:50MHz
clk,驱动time1/time2计数器 - 状态机时钟域:1Hz
liftclk,驱动ctrlift进程(电梯运行节拍) - 按钮采样时钟域:7.69kHz
buttonclk,驱动ctrlight进程(请求寄存)
若用同一分频器生成liftclk和buttonclk,会导致二者相位强耦合。例如当liftclk上升沿恰好落在按钮抖动区间,可能采样到错误电平。因此必须用独立计数器:
time1:COUNT1计数至?(原文缺失数值,按50MHz→1Hz计算应为49999999)生成liftclktime2:COUNT2计数至6499生成buttonclk
两者起始计数相位完全独立,天然实现时钟域隔离。
4.2liftclk分频系数的精确计算与代码补全
原文time1进程中COUNT1上限为空白,需根据50MHz→1Hz反推:
分频比 = 50,000,000 / 1 = 50,000,000 计数范围:0 to 49,999,999 → 共50,000,000个状态补全后代码:
time1: process(clk) variable COUNT1: integer range 0 to 49999999; begin if clk'event and clk='1' then COUNT1 := COUNT1 + 1; if COUNT1 = 49999999 then COUNT1 := 0; liftclk <= NOT liftclk; end if; end if; end process time1;注意:
COUNT1声明为variable(非signal)是关键。variable在进程内立即生效,避免signal赋值延迟一周期导致分频误差累积。
4.3 多时钟域信号交互的跨时钟域处理(CDC)
ctrlight进程输出fuplight等信号给ctrlift进程使用,而前者时钟为buttonclk(7.69kHz),后者为liftclk(1Hz)。直接连接会引发亚稳态。本设计通过两级寄存器同步规避:
ctrlight进程输出的fuplight已是buttonclk域信号ctrlift进程在liftclk上升沿采样fuplight,因liftclk频率远低于buttonclk,采样时fuplight已稳定(满足建立时间)
这是典型的慢时钟采样快时钟信号场景,无需额外同步电路。若反过来(快时钟采样慢时钟信号),则必须用两级触发器。
5. 实战验证技巧:用Quartus II SignalTap抓取关键信号波形
5.1 必须监控的5个信号及其物理意义
在Quartus II中配置SignalTap Logic Analyzer时,优先捕获以下信号(按重要性排序):
| 信号名 | 所属模块 | 验证目标 | 异常表现 |
|---|---|---|---|
liftclk | time1 | 确认1Hz节拍是否稳定(周期≈1.000s) | 周期跳变、占空比严重失衡 |
position | ctrlift | 检查楼层变化是否符合预期(1→2→3或3→2→1) | 跳层(1→3)、停滞(卡在某层) |
udsig | ctrlift | 验证方向信号是否与运动一致(udsig='1'时position应递增) | 方向与运动相反 |
doorlight | ctrlift | 开门时长是否严格4秒(高电平持续4个liftclk周期) | 高电平过短(<4s)或过长(>4s) |
fuplight(2) | ctrlight | 按下2层上升按钮后是否置位,开门后是否被clearup清除 | 按钮按下无反应、清除不及时 |
5.2 SignalTap配置关键参数设置
# 在Quartus II SignalTap界面中: Sampling Clock: liftclk # 必须用状态机时钟,否则无法对齐状态 Depth: 1024 # 足够捕获4秒以上波形(4个liftclk周期) Trigger Position: 50% # 居中触发,前后各512点 Trigger Condition: - Trigger on: fuplight(2) rising edge # 捕获按钮按下瞬间 - Then wait for: position == 2 AND doorlight == '1' # 再捕获开门时刻提示:若发现
doorlight高电平不足4周期,检查doorwait1→doorwait4状态转移是否被意外跳过——常见原因是reset信号在测试中误触发,或liftclk分频系数错误导致周期缩短。
5.3 数码管显示异常的快速定位法
数码管显示楼层由position信号经7段译码器驱动。若显示乱码(如显示"8"或全灭),按以下顺序排查:
- 查
position值:用SignalTap确认position是否在1~3范围内跳变。若为0或4,说明up/down状态中position<=position+1或position<=position-1语句执行异常(常见于position未声明为buffer导致读写冲突) - 查译码逻辑:确认
.vhd文件中是否有dout1输出赋值(原文未给出,需自行补充)。标准共阴极译码:process(position) begin case position is when 1 => dout1 <= "0000001"; -- '1' when 2 => dout1 <= "1001111"; -- '2' when 3 => dout1 <= "1000111"; -- '3' when others => dout1 <= "1111111"; -- 灭 end case; end process; - 查
choose选通信号:choose控制哪一位数码管点亮。若choose未正确循环(如始终为"000001"),则只亮第一位。需检查choose是否由独立计数器驱动,且频率足够高(>100Hz)避免闪烁。
验证时,可临时将position强制赋值为2,观察数码管是否稳定显示'2'。若仍异常,则问题必在译码或驱动电路。
本文还有配套的精品资源,点击获取