Skip to content

Latest commit

 

History

History
1243 lines (967 loc) · 40.9 KB

File metadata and controls

1243 lines (967 loc) · 40.9 KB

三层电梯控制器 -- 调度算法与门控逻辑详细设计

目标:提供足够详细的算法描述,可直接翻译为 VHDL-93 代码(双进程状态机风格) 约束:Quartus II 9.1,VHDL-93,3 层楼,双进程 FSM


目录

  1. 请求管理逻辑
  2. SCAN 调度算法详细设计
  3. 门控逻辑详细设计
  4. 特殊场景处理
  5. 空闲返回 1 楼逻辑
  6. 报警功能详细逻辑

1. 请求管理逻辑

1.1 请求信号定义

电梯系统共有 8 个请求输入按钮,经消抖后产生单周期脉冲,用于置位请求寄存器:

按钮信号 含义 楼层 方向/类型
up1 1 楼外呼上行 1 上行外呼
up2 2 楼外呼上行 2 上行外呼
down2 2 楼外呼下行 2 下行外呼
down3 3 楼外呼下行 3 下行外呼
stop1 轿厢内选 1 楼 1 内选停站
stop2 轿厢内选 2 楼 2 内选停站
stop3 轿厢内选 3 楼 3 内选停站
alarm_btn 轿厢内报警 -- 特殊功能

门控按钮(不产生请求,直接影响门控状态机):

按钮信号 含义
open_btn 手动开门
close_btn 手动关门

1.2 请求寄存器设计

使用 7 个独立的 1 位寄存器存储请求状态:

信号定义(全部为 std_logic,初始值 '0'):

  req_up(1)    -- 1 楼上行外呼请求
  req_up(2)    -- 2 楼上行外呼请求
  req_down(2)  -- 2 楼下行外呼请求
  req_down(3)  -- 3 楼下行外呼请求
  req_stop(1)  -- 1 楼停站(内选)请求
  req_stop(2)  -- 2 楼停站(内选)请求
  req_stop(3)  -- 3 楼停站(内选)请求

VHDL 信号声明:

signal req_up   : std_logic_vector(3 downto 1);  -- req_up(3) 恒为 '0',不使用
signal req_down : std_logic_vector(3 downto 1);  -- req_down(1) 恒为 '0',不使用
signal req_stop : std_logic_vector(3 downto 1);

1.3 请求置位逻辑

请求寄存器在**时序进程(同步进程)**中更新。置位条件:消抖后的按钮信号出现上升沿(单周期脉冲为 '1')。

伪代码 -- 请求置位(在时钟上升沿内执行):

  IF up1_pulse = '1'   THEN req_up(1) <= '1'
  IF up2_pulse = '1'   THEN req_up(2) <= '1'
  IF down2_pulse = '1' THEN req_down(2) <= '1'
  IF down3_pulse = '1' THEN req_down(3) <= '1'
  IF stop1_pulse = '1' THEN req_stop(1) <= '1'
  IF stop2_pulse = '1' THEN req_stop(2) <= '1'
  IF stop3_pulse = '1' THEN req_stop(3) <= '1'

注意:置位和清除在同一时序进程中完成。若同一拍同时发生置位和清除,清除优先(先清除再置位,或清除覆盖置位),确保已到达楼层的请求不会因新按键误保留。

1.4 请求清除逻辑

请求在电梯到达该楼层并开门时清除。清除发生在状态机进入 DOOR_OPENING 状态的那一拍。

伪代码 -- 请求清除(在时钟上升沿内执行):

  当状态机转入 DOOR_OPENING 且 current_floor = F 时:
    req_stop(F) <= '0'                 -- 始终清除该层停站请求
    IF direction = UP   THEN req_up(F) <= '0'    -- 上行到达,清除上行外呼
    IF direction = DOWN THEN req_down(F) <= '0'  -- 下行到达,清除下行外呼
    -- 特殊情况:若该方向无更多请求即将换向,同时清除反向外呼
    IF 即将换向 THEN
      req_up(F) <= '0'
      req_down(F) <= '0'
    END IF

清除策略的核心原则:

  • 停站请求(req_stop):到达即清除,不论方向
  • 外呼请求(req_up/req_down):仅清除与当前运行方向一致的外呼
  • 换向时例外:若电梯到达后即将换向(同方向无更多请求),同时清除两个方向的外呼

1.5 请求有效性约束

硬件层面强制约束无效请求永远不会被置位:

硬约束(在请求置位逻辑中实现):

  req_up(3)   <= '0' 恒成立    -- 3 楼不可能有上行外呼(顶楼)
  req_down(1) <= '0' 恒成立    -- 1 楼不可能有下行外呼(底楼)

实现方式:在 VHDL 中,req_up(3) 和 req_down(1) 不接入任何置位逻辑,复位后保持 '0'。或者更简单地,req_up 仅使用索引 1 和 2,req_down 仅使用索引 2 和 3。

1.6 请求聚合信号

为简化调度逻辑,定义以下聚合信号(组合逻辑):

-- 某一楼层是否有任何请求(不区分方向)
req_any(F) = req_up(F) OR req_down(F) OR req_stop(F)    -- F = 1,2,3

-- 当前楼层是否需要停靠(考虑方向)
need_stop_here =
    req_stop(current_floor) = '1'                         -- 内选该层
    OR (direction = UP   AND req_up(current_floor) = '1') -- 同方向外呼
    OR (direction = DOWN AND req_down(current_floor) = '1')

-- 当前方向上方是否有请求
req_above =
    (current_floor < 3) AND (存在 F > current_floor 使得 req_any(F) = '1')

-- 当前方向下方是否有请求
req_below =
    (current_floor > 1) AND (存在 F < current_floor 使得 req_any(F) = '1')

-- 全局是否有任何请求
req_exist = req_any(1) OR req_any(2) OR req_any(3)

VHDL 中 req_above 和 req_below 的具体展开(current_floor 为 integer range 1 to 3):

-- req_above:当前楼层上方是否有请求
req_above <= '1' when (current_floor = 1 and (req_any(2) = '1' or req_any(3) = '1'))
                   or (current_floor = 2 and req_any(3) = '1')
             else '0';

-- req_below:当前楼层下方是否有请求
req_below <= '1' when (current_floor = 3 and (req_any(2) = '1' or req_any(1) = '1'))
                   or (current_floor = 2 and req_any(1) = '1')
             else '0';

1.7 请求优先级规则

请求本身无固定优先级排序。优先级完全由 SCAN 调度算法的方向策略决定:

  1. 方向内近优先:同方向上,距离当前楼层最近的请求先响应
  2. 同方向优先于反方向:先服务完当前方向的所有请求,再换向
  3. 内选等同外呼:req_stop 与 req_up/req_down 在调度中地位相同,不区分优先级
  4. 报警最高优先级:报警按钮立即中断一切正常调度(见第 6 节)

2. SCAN 调度算法详细设计

2.1 方向信号定义

type direction_type is (DIR_UP, DIR_DOWN, DIR_IDLE);
signal direction : direction_type;
  • DIR_UP:电梯当前处于"上行模式",优先向上搜索请求
  • DIR_DOWN:电梯当前处于"下行模式",优先向下搜索请求
  • DIR_IDLE:电梯空闲,无确定方向

2.2 核心状态机概览

与调度相关的状态(完整状态机含门控、报警等,此处仅列调度核心):

IDLE       -- 电梯空闲,等待请求
MOVING_UP  -- 电梯上行中(每个慢时钟周期移动一层)
MOVING_DOWN -- 电梯下行中
ARRIVED    -- 到达目标楼层,准备开门
DOOR_OPENING / DOOR_OPEN / DOOR_CLOSING -- 门控序列(见第 3 节)
CHECK_REQ  -- 关门后检查请求,决定下一步动作
RETURNING  -- 空闲返回 1 楼

2.3 电梯空闲时的请求选择

当电梯处于 IDLE 状态(停在某层,门已关闭,无方向)时:

伪代码 -- IDLE 状态的请求选择:

  IF req_exist = '0' THEN
    -- 无请求,保持 IDLE
    保持 IDLE 状态
    空闲计时器递增(见第 5 节)

  ELSIF req_any(current_floor) = '1' THEN
    -- 当前楼层有请求,直接开门
    direction <= DIR_IDLE  -- 暂不确定方向
    转入 DOOR_OPENING

  ELSIF req_above = '1' AND req_below = '0' THEN
    -- 仅上方有请求
    direction <= DIR_UP
    转入 MOVING_UP

  ELSIF req_below = '1' AND req_above = '0' THEN
    -- 仅下方有请求
    direction <= DIR_DOWN
    转入 MOVING_DOWN

  ELSIF req_above = '1' AND req_below = '1' THEN
    -- 上下均有请求,默认先上行
    direction <= DIR_UP
    转入 MOVING_UP

  END IF

关键决策:上下均有请求时默认先上行(DIR_UP)。这是一个简单的约定,确保行为确定性。

2.4 上行模式的请求处理

当电梯处于 MOVING_UP 状态或在 CHECK_REQ 中 direction = DIR_UP 时:

伪代码 -- 上行模式运动逻辑:

  MOVING_UP 状态下,每个慢时钟周期:
    current_floor <= current_floor + 1

    到达新楼层后,检查是否需要停靠:
    IF need_stop_here = '1' THEN
      -- 该楼层有同方向请求或内选请求,停靠
      转入 ARRIVED → DOOR_OPENING
    ELSE
      -- 该楼层无需停靠
      IF current_floor = 3 THEN
        -- 已到顶楼,必须停止(即使无请求也不能继续上行)
        -- 检查是否有下方请求,决定换向
        转入 CHECK_REQ
      ELSE
        -- 未到顶楼,继续上行
        保持 MOVING_UP
      END IF
    END IF

need_stop_here 在上行模式下的展开:

need_stop_here(上行模式) =
    req_stop(current_floor)                    -- 内选该层
    OR req_up(current_floor)                   -- 同方向(上行)外呼
    -- 注意:不停靠反方向外呼(req_down),除非是顶楼
    OR (current_floor = 3 AND req_down(3))     -- 顶楼的下行外呼视为需要停靠

2.5 下行模式的请求处理

伪代码 -- 下行模式运动逻辑:

  MOVING_DOWN 状态下,每个慢时钟周期:
    current_floor <= current_floor - 1

    到达新楼层后,检查是否需要停靠:
    IF need_stop_here = '1' THEN
      转入 ARRIVED → DOOR_OPENING
    ELSE
      IF current_floor = 1 THEN
        -- 已到底楼
        转入 CHECK_REQ
      ELSE
        保持 MOVING_DOWN
      END IF
    END IF

need_stop_here 在下行模式下的展开:

need_stop_here(下行模式) =
    req_stop(current_floor)                    -- 内选该层
    OR req_down(current_floor)                 -- 同方向(下行)外呼
    OR (current_floor = 1 AND req_up(1))       -- 底楼的上行外呼视为需要停靠

2.6 方向切换条件

方向切换发生在 CHECK_REQ 状态中,即关门完成后或到达终端楼层后:

伪代码 -- CHECK_REQ 状态的方向决策:

  IF req_exist = '0' THEN
    -- 无任何请求
    direction <= DIR_IDLE
    转入 IDLE

  ELSIF direction = DIR_UP THEN
    IF req_above = '1' THEN
      -- 上方仍有请求,继续上行
      保持 direction = DIR_UP
      转入 MOVING_UP
    ELSIF req_below = '1' THEN
      -- 上方无请求,下方有请求,换向下行
      direction <= DIR_DOWN
      转入 MOVING_DOWN
    ELSE
      -- 仅当前层有请求(理论上已在 ARRIVED 处理)
      转入 DOOR_OPENING
    END IF

  ELSIF direction = DIR_DOWN THEN
    IF req_below = '1' THEN
      -- 下方仍有请求,继续下行
      保持 direction = DIR_DOWN
      转入 MOVING_DOWN
    ELSIF req_above = '1' THEN
      -- 下方无请求,上方有请求,换向上行
      direction <= DIR_UP
      转入 MOVING_UP
    ELSE
      转入 DOOR_OPENING
    END IF

  ELSIF direction = DIR_IDLE THEN
    -- 从 IDLE 进入(当前层开门后关门,检查新请求)
    IF req_above = '1' THEN
      direction <= DIR_UP
      转入 MOVING_UP
    ELSIF req_below = '1' THEN
      direction <= DIR_DOWN
      转入 MOVING_DOWN
    ELSE
      direction <= DIR_IDLE
      转入 IDLE
    END IF

  END IF

2.7 SCAN 算法运行示例

以电梯从 1 楼空闲开始为例:

初始:current_floor=1, direction=DIR_IDLE, 所有请求='0'

时刻 T0: up1='1', down3='1' 同时按下
  → req_up(1)='1', req_down(3)='1'
  → IDLE 检测到 req_any(1)='1'(当前层有请求)
  → 转入 DOOR_OPENING,清除 req_up(1)
  → direction 暂为 DIR_IDLE

时刻 T1: 开门→等待→关门完成
  → 转入 CHECK_REQ
  → direction=DIR_IDLE, req_above='1'(3楼有请求)
  → direction <= DIR_UP, 转入 MOVING_UP

时刻 T2: current_floor=2, 检查 need_stop_here
  → req_stop(2)='0', req_up(2)='0' → 不停靠
  → 继续 MOVING_UP

时刻 T3: current_floor=3, 检查 need_stop_here
  → req_down(3)='1', 且 current_floor=3(顶楼下行外呼视为需停靠)
  → 转入 ARRIVED → DOOR_OPENING,清除 req_down(3)

时刻 T4: 开门→等待→关门
  → CHECK_REQ: req_exist='0'
  → direction <= DIR_IDLE, 转入 IDLE
  → 空闲计时器开始计数

2.8 运动过程中新请求的处理

电梯在 MOVING_UP 或 MOVING_DOWN 状态中,请求寄存器仍在接收新按键:

规则:

1. 运动过程中,请求寄存器可以在任意时刻被置位
2. 每到达一个新楼层,重新计算 need_stop_here
3. 新请求在方向上若为同向且在前方,会被自动纳入路径
4. 新请求在反方向,会在当前方向无更多请求后换向时处理
5. 新请求恰好是当前正在经过的楼层 -- 不会停靠(因为判断发生在到达时刻)
   但下一个到达楼层开始,正常判断

3. 门控逻辑详细设计

3.1 门控状态定义

门控逻辑作为主状态机的子序列,包含以下状态:

DOOR_OPENING   -- 开门动作(模拟用时 1 秒)
DOOR_OPEN      -- 门完全打开,开始 10 秒倒计时等待
DOOR_CLOSING   -- 关门动作(模拟用时 1 秒)

3.2 10 秒定时器设计

使用一个整数计数器实现,在 1Hz 慢时钟(或主时钟分频使能脉冲)下递增:

signal door_timer : integer range 0 to 10;  -- 0~10,计到 10 表示 10 秒到

定时器行为:

伪代码 -- door_timer 控制逻辑:

  在时序进程中:

  CASE current_state IS

    WHEN DOOR_OPENING =>
      door_timer <= 0                     -- 重置计时器
      -- 开门动作持续 1 秒(1 个慢时钟周期)
      -- 1 秒后转入 DOOR_OPEN

    WHEN DOOR_OPEN =>
      IF close_btn = '1' THEN
        door_timer <= 0                   -- 手动关门,立即跳过等待
        -- 转入 DOOR_CLOSING
      ELSIF door_timer >= 9 THEN
        door_timer <= 0                   -- 10 秒到(计数 0~9 共 10 拍)
        -- 转入 DOOR_CLOSING
      ELSE
        door_timer <= door_timer + 1      -- 继续计时
        -- 保持 DOOR_OPEN
      END IF

    WHEN DOOR_CLOSING =>
      door_timer <= 0                     -- 复位
      IF open_btn = '1' THEN
        -- 关门中按开门 → 重新开门
        -- 转入 DOOR_OPENING
      ELSE
        -- 关门动作持续 1 秒后
        -- 转入 CHECK_REQ
      END IF

    WHEN OTHERS =>
      door_timer <= 0

  END CASE

计数范围说明:door_timer 从 0 计到 9,经过 10 个慢时钟周期(每周期 1 秒),总计 10 秒。当 door_timer = 9 时(即第 10 个周期结束),下一拍转入 DOOR_CLOSING。

3.3 开门过程计时

开门动作本身需要时间模拟(真实电梯开门约 2~3 秒,本设计简化为 1 秒):

方案 A(简单):DOOR_OPENING 状态持续 1 个慢时钟周期后自动转入 DOOR_OPEN

方案 B(更真实):使用额外计数器 door_action_timer
  DOOR_OPENING: door_action_timer 从 0 计到 OPEN_TIME-1 后转入 DOOR_OPEN
  DOOR_CLOSING: door_action_timer 从 0 计到 CLOSE_TIME-1 后转入 CHECK_REQ

推荐方案 A(简单),因为课程设计中门的开关速度不是重点。

3.4 手动开门按钮逻辑

open_btn 的作用:在关门过程中可以中断关门,重新回到开门状态。

伪代码 -- open_btn 处理:

  仅在 DOOR_CLOSING 状态下有效:
    IF open_btn = '1' THEN
      转入 DOOR_OPENING     -- 重新开门
      door_timer <= 0       -- 10 秒计时器重置
    END IF

  在 DOOR_OPEN 状态下:
    IF open_btn = '1' THEN
      door_timer <= 0       -- 重置 10 秒计时器(延长等待)
    END IF

  在其他状态下(运动、空闲):
    open_btn 无效,忽略

3.5 手动关门按钮逻辑

close_btn 的作用:在门打开等待期间可以提前关门。

伪代码 -- close_btn 处理:

  仅在 DOOR_OPEN 状态下有效:
    IF close_btn = '1' THEN
      door_timer <= 0       -- 清除计时
      转入 DOOR_CLOSING     -- 立即开始关门
    END IF

  在 DOOR_OPENING 状态下:
    close_btn 无效(开门未完成不能关门)

  在其他状态下:
    close_btn 无效

3.6 到站开门完整流程

当电梯到达目标楼层(ARRIVED 状态)时,触发开门序列:

时序图(每行代表 1 个慢时钟周期 = 1 秒):

  周期 0:  ARRIVED → 清除当前层请求 → 转入 DOOR_OPENING
  周期 1:  DOOR_OPENING → 门开启中(door_open_sig <= '1')→ 转入 DOOR_OPEN
  周期 2:  DOOR_OPEN, door_timer=0
  周期 3:  DOOR_OPEN, door_timer=1
  ...
  周期 11: DOOR_OPEN, door_timer=9 → 10秒到 → 转入 DOOR_CLOSING
  周期 12: DOOR_CLOSING → 门关闭中 → 转入 CHECK_REQ
  周期 13: CHECK_REQ → 检查请求 → 决定下一步动作

3.7 完整门控时序状态转移

ARRIVED
  │
  ▼
DOOR_OPENING ──(1 秒后)──▶ DOOR_OPEN
                              │
                              ├── close_btn='1' ──▶ DOOR_CLOSING
                              ├── open_btn='1' ──▶ 重置 door_timer,保持 DOOR_OPEN
                              ├── door_timer=9 ──▶ DOOR_CLOSING
                              └── 否则 ──▶ door_timer+1,保持 DOOR_OPEN
                                                        │
DOOR_CLOSING ◀────────────────────────────────────────────┘
  │
  ├── open_btn='1' ──▶ DOOR_OPENING(重新开门,计时重置)
  └── 1 秒后 ──▶ CHECK_REQ
                    │
                    ├── req_exist='0' ──▶ IDLE
                    ├── 同方向有请求 ──▶ MOVING_UP 或 MOVING_DOWN
                    └── 仅反方向有请求 ──▶ 换向 ──▶ MOVING_UP 或 MOVING_DOWN

3.8 门控输出信号

-- 门状态输出
door_open_sig  <= '1' when (current_state = DOOR_OPENING
                         or current_state = DOOR_OPEN) else '0';
door_close_sig <= '1' when (current_state = DOOR_CLOSING) else '0';

-- 门状态 LED 指示
door_led_green <= door_open_sig;   -- 开门时绿灯亮
door_led_red   <= door_close_sig;  -- 关门时红灯亮

4. 特殊场景处理

场景 A:电梯在 2 楼上行中,1 楼有下行请求,3 楼有上行请求

前提:电梯刚离开 2 楼,direction = DIR_UP,current_floor 即将变为 3。

状态分析:
  current_floor = 2(正在上行)
  direction = DIR_UP
  req_down(1) = '0'(1 楼不存在下行按钮,此请求无效)
  -- 重新理解:1 楼有请求 → req_stop(1)='1' 或 req_up(1)='1'
  -- 3 楼有请求 → req_stop(3)='1' 或 req_down(3)='1'
  -- 注意:3 楼不存在上行按钮,req_up(3)恒='0'

处理流程:
  T0: MOVING_UP, current_floor 从 2 变为 3
  T1: 到达 3 楼,检查 need_stop_here
      → req_stop(3)='1' 或 req_down(3)='1'(3 楼有请求,且在顶楼)
      → need_stop_here = '1'
      → 转入 ARRIVED → DOOR_OPENING,清除 3 楼请求

  T2~T12: 开门→等待 10 秒→关门

  T13: CHECK_REQ
      → req_above = '0'(已在顶楼)
      → req_below = '1'(1 楼有请求)
      → direction <= DIR_DOWN
      → 转入 MOVING_DOWN

  T14: current_floor = 2,检查 need_stop_here → '0'(2 楼无请求)
      → 继续 MOVING_DOWN

  T15: current_floor = 1,检查 need_stop_here
      → req_stop(1)='1' 或 req_up(1)='1'
      → 停靠,开门,清除请求

结论:先去 3 楼(同方向优先),再回 1 楼。

场景 B:电梯在 2 楼开门等待中,3 楼有请求

状态分析:
  current_state = DOOR_OPEN
  current_floor = 2
  door_timer 正在计数中
  此时 req_stop(3) 或 req_down(3) 被置位

处理流程:
  门控序列不受新请求影响,继续执行:
  1. door_timer 继续计到 9(或乘客按 close_btn 提前关门)
  2. 转入 DOOR_CLOSING
  3. 关门完成后转入 CHECK_REQ
  4. 在 CHECK_REQ 中:
     - 若之前 direction = DIR_UP:req_above='1'(3 楼有请求),继续上行
     - 若之前 direction = DIR_DOWN:req_below='0'(1 楼无请求),req_above='1',换向上行
     - 若之前 direction = DIR_IDLE:req_above='1',direction <= DIR_UP,上行

结论:等关门后上行去 3 楼。门控序列不被新请求中断。

场景 C:电梯在 2 楼向上运动中,2 楼有停站请求

状态分析:
  current_state = MOVING_UP
  current_floor 刚从 2 变为 3(或正处于 2→3 的运动中)
  req_stop(2) 被置位

关键时序分析:
  电梯运动采用慢时钟(1Hz),每个时钟周期移动一层。
  current_floor 的更新发生在时钟上升沿。

  情况 1:req_stop(2) 在 current_floor 已经变为 3 之后才置位
    → 电梯已不在 2 楼,2 楼请求不影响当前运动
    → 3 楼判断 need_stop_here 时不看 req_stop(2)
    → 2 楼请求将在换向下行时处理

  情况 2:req_stop(2) 在 current_floor 还是 2 时的同一时钟周期置位
    → 电梯在该周期到达 2 楼(从 1 楼上来),need_stop_here 判断已在组合逻辑中完成
    → 若 need_stop_here 在该拍 = '1'(取决于时序),会停靠
    → 若按钮按下稍晚(在 need_stop_here 判断之后),则不会停靠

设计决策:
  运动状态下,楼层移动和停靠判断在同一时钟沿完成。
  按钮请求的置位也在同一时钟沿完成。
  由于双进程 FSM 中组合逻辑先于时序逻辑求值,
  实际行为取决于按钮消抖后脉冲和楼层变化的相对时序。

  简化处理:如果电梯正在移动经过某楼层,且在移动的同一拍该楼层按钮被按下,
  按保守策略不停靠(已过)。请求保留,待换向时处理。

结论:不停(已过该楼层),请求保留,后续换向时处理。

场景 D:所有请求同时到达

状态分析:
  假设电梯在 2 楼 IDLE 状态,同一时刻:
  req_up(1), req_up(2), req_down(2), req_down(3), req_stop(1), req_stop(2), req_stop(3) 全部置位

处理流程:
  T0: IDLE 状态检测
    → req_any(current_floor=2) = '1'(req_up(2) 和 req_down(2) 和 req_stop(2) 都是 '1')
    → 当前层有请求,先开门
    → 转入 DOOR_OPENING,清除 req_stop(2)
    → direction 暂为 DIR_IDLE

  T1~T11: 开门→等待→关门

  T12: CHECK_REQ
    → direction = DIR_IDLE
    → req_above = '1'(3 楼有请求)
    → direction <= DIR_UP(默认先上行)
    → 转入 MOVING_UP

  T13: current_floor=3, need_stop_here='1'
    → 停靠,清除 req_down(3), req_stop(3)

  T14~T24: 开门→等待→关门

  T25: CHECK_REQ
    → direction = DIR_UP, req_above='0'
    → req_below='1'(1 楼有请求,2 楼可能仍有 req_up(2) 或 req_down(2))
    → direction <= DIR_DOWN
    → 转入 MOVING_DOWN

  T26: current_floor=2, need_stop_here
    → req_down(2)='1'(同方向下行外呼)→ 停靠,清除 req_down(2)

  T27~T37: 开门→等待→关门

  T38: CHECK_REQ
    → direction=DIR_DOWN, req_below='1'(1 楼有请求)
    → 转入 MOVING_DOWN

  T39: current_floor=1, need_stop_here
    → req_stop(1)='1' 或 req_up(1)='1' → 停靠,清除

  继续类推,直到所有请求清空。

结论:按 SCAN 方向优先原则处理 -- 先当前层,再上行方向,再换向下行。
      上行途中遇到的上行外呼会被顺路响应。
      具体清除顺序注意:每次开门只清除与当前方向一致的外呼。
      2 楼的 req_up(2) 在上行途中经过时已被清除(同方向外呼)。

场景 E:运动中按下报警按钮

状态分析:
  current_state = MOVING_UP 或 MOVING_DOWN
  alarm_btn 被按下(消抖后脉冲='1')

处理流程:
  报警按钮具有最高优先级,在任何状态下(除 IDLE 在 1 楼外)均可触发。

  T0: 检测到 alarm_btn_pulse = '1'
    → 立即转入 ALARM_STATE
    → 保存当前状态和楼层(alarm_saved_state, alarm_saved_floor)
    → 电机停止(motor_up <= '0', motor_down <= '0')
    → 所有请求寄存器保持不变(不清除)

  ALARM_STATE 下:
    → 电梯不响应任何楼层请求
    → 门控按钮无效
    → 报警 LED 闪烁,蜂鸣器报警(见第 6 节)
    → 等待报警解除信号

  报警解除后:
    → 回到报警前的状态继续执行
    → 或保守策略:回到当前楼层的 IDLE 状态,重新调度

  推荐实现(简单可靠):
    报警解除后转入 IDLE 状态(不恢复之前的运动状态)
    请求寄存器保留,IDLE 会重新调度

结论:立即停止运动,进入报警状态,报警解除后重新调度。

场景 F:开门等待中按关门按钮

状态分析:
  current_state = DOOR_OPEN
  door_timer = 5(例如已等了 5 秒)
  close_btn 被按下

处理流程:
  T0: DOOR_OPEN 状态检测到 close_btn = '1'
    → door_timer <= 0(重置计时器)
    → 转入 DOOR_CLOSING

  T1: DOOR_CLOSING → 1 秒后 → 转入 CHECK_REQ

结论:立即跳过剩余等待时间,开始关门。
      从按下关门到门完全关闭约 1 秒(关门动作时间)。

场景 G:关门过程中按开门按钮

状态分析:
  current_state = DOOR_CLOSING
  open_btn 被按下

处理流程:
  T0: DOOR_CLOSING 状态检测到 open_btn = '1'
    → 转入 DOOR_OPENING
    → door_timer <= 0(重置,重新开始 10 秒计时)

  T1: DOOR_OPENING → 1 秒后 → 转入 DOOR_OPEN
  T2: DOOR_OPEN, door_timer=0(重新开始 10 秒等待)

结论:中断关门动作,重新完整执行开门→等待→关门序列。
      10 秒计时器重新从 0 开始。

场景 H:无任何请求持续 5 秒

状态分析:
  current_state = IDLE
  current_floor ≠ 1(不在 1 楼)
  req_exist = '0' 持续 5 秒

处理流程:见第 5 节"空闲返回 1 楼逻辑"。

简述:
  idle_timer 从 0 计到 4(5 个慢时钟周期 = 5 秒)
  计满后:
    direction <= DIR_DOWN
    转入 MOVING_DOWN(或 RETURNING 状态)
    逐层下降直到 current_floor = 1
    到达 1 楼后不开门,直接转入 IDLE

结论:5 秒无请求后自动下行返回 1 楼。

5. 空闲返回 1 楼逻辑

5.1 空闲检测计时器

signal idle_timer : integer range 0 to 5;  -- 0~5,计到 5 时触发返回

5.2 计时器行为

伪代码 -- idle_timer 逻辑(在时序进程中):

  IF current_state = IDLE THEN
    IF req_exist = '1' THEN
      idle_timer <= 0          -- 有请求,重置计时
    ELSIF current_floor = 1 THEN
      idle_timer <= 0          -- 已在 1 楼,无需返回
    ELSIF idle_timer >= 4 THEN
      -- 5 秒到(计数 0~4 共 5 拍),触发返回
      idle_timer <= 0
      -- 转入 RETURNING 状态
    ELSE
      idle_timer <= idle_timer + 1
    END IF
  ELSE
    idle_timer <= 0            -- 非 IDLE 状态,重置计时
  END IF

5.3 返回流程

返回 1 楼使用独立的 RETURNING 状态,行为类似 MOVING_DOWN 但有以下区别:

伪代码 -- RETURNING 状态:

  RETURNING:
    direction <= DIR_DOWN     -- 方向灯显示下行

    每个慢时钟周期:
      current_floor <= current_floor - 1

    到达新楼层后:
      IF req_exist = '1' THEN
        -- 返回途中出现新请求,中断返回,转入正常调度
        转入 CHECK_REQ
      ELSIF current_floor = 1 THEN
        -- 到达 1 楼
        direction <= DIR_IDLE
        转入 IDLE
        -- 不开门(无乘客等待)
      ELSE
        -- 继续下降
        保持 RETURNING
      END IF

5.4 关键设计决策

问题 决策 理由
返回 1 楼时需要开门吗? 不开门 返回是因为无人使用,开门无意义
方向灯如何显示? 显示下行灯 让外部观察者知道电梯在移动
返回途中有新请求怎么办? 中断返回,转入正常调度 响应用户请求优先于空闲返回
已在 1 楼时怎么处理? 保持 IDLE,不触发返回 idle_timer 不计数,保持 0
2 楼空闲,返回到 1 楼的过程 2 楼不开门 → 下行到 1 楼 → 不开门 → IDLE 全程无门动作

5.5 返回过程中途停站

伪代码 -- RETURNING 中途检查:

  RETURNING 状态,到达中间楼层(仅可能是 2 楼,因为从 3 楼返回):
    IF req_any(current_floor) = '1' THEN
      -- 当前层有请求,停下来服务
      转入 ARRIVED → DOOR_OPENING
      -- 方向由 CHECK_REQ 重新决定
    ELSIF req_exist = '1' THEN
      -- 其他层有请求,转入正常调度
      转入 CHECK_REQ
    ELSE
      -- 继续返回
      current_floor <= current_floor - 1
    END IF

6. 报警功能详细逻辑

6.1 报警进入条件

进入 ALARM_STATE 的条件:

  alarm_btn_pulse = '1'(消抖后的按钮单周期脉冲)
  AND current_state ≠ ALARM_STATE(避免重复进入)

  可从以下状态进入报警:
    IDLE, MOVING_UP, MOVING_DOWN, DOOR_OPENING, DOOR_OPEN,
    DOOR_CLOSING, CHECK_REQ, ARRIVED, RETURNING
  即:从任何非报警状态均可进入。

6.2 报警退出条件

方案选择:

  方案 A(简单):再次按下 alarm_btn 退出
    退出条件:alarm_btn_pulse = '1' AND current_state = ALARM_STATE
    退出后:转入 IDLE 状态,保留所有请求

  方案 B(安全):使用独立的 reset 按钮退出
    退出条件:系统复位信号 rst_n = '0'
    退出后:所有状态归零,回到初始状态

  推荐方案 A,因为:
    - reset 是全局复位,会清除所有请求和状态
    - alarm 更像一个可恢复的暂停
    - 真实电梯也允许解除报警后继续运行

6.3 报警状态下的信号值

ALARM_STATE 下各输出信号:

  motor_up      <= '0'        -- 电机停止
  motor_down    <= '0'        -- 电机停止
  door_open_sig <= '0'        -- 门不动作(保持报警前状态)
  door_close_sig<= '0'        -- 门不动作
  dir_up_led    <= '0'        -- 方向灯全灭
  dir_down_led  <= '0'

  alarm_led     <= alarm_blink -- 1Hz 闪烁
  buzzer        <= buzzer_pattern -- 间歇报警音

  floor_display <= current_floor  -- 继续显示当前楼层(供救援参考)

  -- 请求寄存器:保持不变(不清除,也不接受新请求)
  -- 报警期间新按下的请求按钮被忽略(可选策略)

6.4 LED 闪烁逻辑

报警 LED 以 1Hz 频率闪烁(亮 0.5 秒,灭 0.5 秒):

-- 使用慢时钟(1Hz)直接翻转
signal alarm_blink : std_logic;

-- 在时序进程中:
IF current_state = ALARM_STATE THEN
  alarm_blink <= NOT alarm_blink;  -- 每秒翻转一次 → 1Hz 闪烁(0.5s亮/0.5s灭)
ELSE
  alarm_blink <= '0';
END IF;

-- 报警 LED 输出
alarm_led <= alarm_blink when current_state = ALARM_STATE else '0';

若需要更快的闪烁(2Hz),可使用单独的分频计数器或更高频率的使能信号。

6.5 蜂鸣器报警模式

蜂鸣器产生间歇性报警音("嘟-嘟-嘟"模式):

报警音模式:响 0.5 秒 → 停 0.5 秒 → 响 0.5 秒 → 停 0.5 秒 ...

实现方式:
  蜂鸣器驱动 = 报警使能信号 AND 蜂鸣器载波

  报警使能信号 = alarm_blink(1Hz 方波,与 LED 同步)
  蜂鸣器载波 = 约 1kHz~4kHz 方波(产生可听见的"嘟"声)
-- 蜂鸣器载波产生(假设主时钟 50MHz,产生 2kHz 方波)
-- 50MHz / (2 * 2000) = 12500
signal buzzer_cnt : integer range 0 to 12499;
signal buzzer_carrier : std_logic;

process(clk, rst_n)
begin
  if rst_n = '0' then
    buzzer_cnt <= 0;
    buzzer_carrier <= '0';
  elsif rising_edge(clk) then
    if buzzer_cnt >= 12499 then
      buzzer_cnt <= 0;
      buzzer_carrier <= not buzzer_carrier;
    else
      buzzer_cnt <= buzzer_cnt + 1;
    end if;
  end if;
end process;

-- 蜂鸣器输出
-- 报警模式:间歇性响(1Hz 调制的 2kHz 载波)
-- 到站提示:短响一声(0.5 秒的 2kHz 载波)
buzzer <= buzzer_carrier AND alarm_blink   when current_state = ALARM_STATE
     else buzzer_carrier AND arrival_beep  when current_state = ARRIVED
     else '0';

6.6 到站提示音(附加功能)

电梯到达楼层时短响一声(约 0.5 秒):

信号:arrival_beep : std_logic

在时序进程中:
  WHEN ARRIVED =>
    arrival_beep <= '1'      -- 使能蜂鸣器
    -- 持续时间由 ARRIVED 状态的持续时间决定(1 个慢时钟周期 = 1 秒)
    -- 若需更短可用额外计数器

  WHEN OTHERS =>
    arrival_beep <= '0'

6.7 报警期间对按钮输入的处理

ALARM_STATE 下的输入处理策略:

  alarm_btn:  有效 -- 再次按下用于退出报警
  open_btn:   无效 -- 忽略
  close_btn:  无效 -- 忽略
  up1~up2:    无效 -- 忽略(请求不置位)
  down2~down3: 无效 -- 忽略
  stop1~stop3: 无效 -- 忽略
  rst_n:       有效 -- 全局复位始终有效

实现方式:在请求置位逻辑中增加条件
  IF current_state /= ALARM_STATE THEN
    -- 正常置位逻辑
    IF up1_pulse = '1' THEN req_up(1) <= '1'; END IF;
    ...
  END IF;

附录 A:完整状态转移总表

状态            | 转移条件                              | 目标状态       | 动作
----------------|---------------------------------------|----------------|---------------------------
IDLE            | alarm_btn_pulse='1'                   | ALARM_STATE    | 保存状态
IDLE            | req_any(current_floor)='1'            | DOOR_OPENING   | 清除当前层请求
IDLE            | req_above='1' (only)                  | MOVING_UP      | direction<=DIR_UP
IDLE            | req_below='1' (only)                  | MOVING_DOWN    | direction<=DIR_DOWN
IDLE            | req_above='1' AND req_below='1'       | MOVING_UP      | direction<=DIR_UP
IDLE            | idle_timer>=4 AND floor≠1             | RETURNING      | direction<=DIR_DOWN
                |                                       |                |
MOVING_UP       | alarm_btn_pulse='1'                   | ALARM_STATE    | 电机停
MOVING_UP       | need_stop_here='1'                    | ARRIVED        | --
MOVING_UP       | current_floor=3 AND need_stop='0'     | CHECK_REQ      | --
MOVING_UP       | 其他                                  | MOVING_UP      | floor+1
                |                                       |                |
MOVING_DOWN     | alarm_btn_pulse='1'                   | ALARM_STATE    | 电机停
MOVING_DOWN     | need_stop_here='1'                    | ARRIVED        | --
MOVING_DOWN     | current_floor=1 AND need_stop='0'     | CHECK_REQ      | --
MOVING_DOWN     | 其他                                  | MOVING_DOWN    | floor-1
                |                                       |                |
ARRIVED         | 无条件(1拍过渡)                      | DOOR_OPENING   | 清除请求, beep
                |                                       |                |
DOOR_OPENING    | alarm_btn_pulse='1'                   | ALARM_STATE    | --
DOOR_OPENING    | 1秒到                                 | DOOR_OPEN      | timer<=0
                |                                       |                |
DOOR_OPEN       | alarm_btn_pulse='1'                   | ALARM_STATE    | --
DOOR_OPEN       | close_btn='1'                         | DOOR_CLOSING   | timer<=0
DOOR_OPEN       | open_btn='1'                          | DOOR_OPEN      | timer<=0 (重置)
DOOR_OPEN       | door_timer>=9                         | DOOR_CLOSING   | timer<=0
DOOR_OPEN       | 其他                                  | DOOR_OPEN      | timer+1
                |                                       |                |
DOOR_CLOSING    | alarm_btn_pulse='1'                   | ALARM_STATE    | --
DOOR_CLOSING    | open_btn='1'                          | DOOR_OPENING   | timer<=0
DOOR_CLOSING    | 1秒到                                 | CHECK_REQ      | --
                |                                       |                |
CHECK_REQ       | req_exist='0'                         | IDLE           | direction<=DIR_IDLE
CHECK_REQ       | 同方向有请求                           | MOVING_UP/DOWN | 保持方向
CHECK_REQ       | 仅反向有请求                           | MOVING_UP/DOWN | 换向
CHECK_REQ       | 仅当前层有请求                         | DOOR_OPENING   | 清除请求
                |                                       |                |
RETURNING       | alarm_btn_pulse='1'                   | ALARM_STATE    | 电机停
RETURNING       | current_floor=1                       | IDLE           | direction<=DIR_IDLE
RETURNING       | req_exist='1'                         | CHECK_REQ      | 中断返回
RETURNING       | 其他                                  | RETURNING      | floor-1
                |                                       |                |
ALARM_STATE     | alarm_btn_pulse='1'                   | IDLE           | 解除报警
ALARM_STATE     | 其他                                  | ALARM_STATE    | LED闪烁,蜂鸣器响

附录 B:信号完整清单

-- ======== 输入信号(经消抖后) ========
signal up1_pulse, up2_pulse       : std_logic;  -- 上行外呼脉冲
signal down2_pulse, down3_pulse   : std_logic;  -- 下行外呼脉冲
signal stop1_pulse, stop2_pulse, stop3_pulse : std_logic;  -- 内选停站脉冲
signal open_btn, close_btn        : std_logic;  -- 门控按钮(电平有效)
signal alarm_btn_pulse            : std_logic;  -- 报警按钮脉冲

-- ======== 请求寄存器 ========
signal req_up   : std_logic_vector(3 downto 1);  -- (3)恒='0'
signal req_down : std_logic_vector(3 downto 1);  -- (1)恒='0'
signal req_stop : std_logic_vector(3 downto 1);

-- ======== 聚合信号(组合逻辑) ========
signal req_any   : std_logic_vector(3 downto 1);
signal req_above : std_logic;
signal req_below : std_logic;
signal req_exist : std_logic;
signal need_stop_here : std_logic;

-- ======== 状态机 ========
type state_type is (
    IDLE, MOVING_UP, MOVING_DOWN, ARRIVED,
    DOOR_OPENING, DOOR_OPEN, DOOR_CLOSING,
    CHECK_REQ, RETURNING, ALARM_STATE
);
signal current_state, next_state : state_type;

-- ======== 运行状态 ========
type direction_type is (DIR_UP, DIR_DOWN, DIR_IDLE);
signal direction     : direction_type;
signal current_floor : integer range 1 to 3;

-- ======== 计时器 ========
signal door_timer : integer range 0 to 10;
signal idle_timer : integer range 0 to 5;

-- ======== 输出信号 ========
signal motor_up, motor_down     : std_logic;  -- 电机方向控制
signal door_open_sig            : std_logic;  -- 门开启信号
signal door_close_sig           : std_logic;  -- 门关闭信号
signal dir_up_led, dir_down_led : std_logic;  -- 方向指示灯
signal door_led_green           : std_logic;  -- 开门指示(绿)
signal door_led_red             : std_logic;  -- 关门指示(红)
signal alarm_led                : std_logic;  -- 报警灯
signal alarm_blink              : std_logic;  -- 报警闪烁信号
signal buzzer                   : std_logic;  -- 蜂鸣器
signal arrival_beep             : std_logic;  -- 到站提示
signal seg7                     : std_logic_vector(6 downto 0);  -- 七段数码管
signal floor_leds               : std_logic_vector(3 downto 1);  -- 楼层指示灯

附录 C:时钟域划分

主时钟 (clk, 50MHz)
  │
  ├── 消抖模块:直接使用主时钟,内部计数 20ms
  │
  ├── 分频产生 1Hz 使能脉冲 (clk_1hz_en)
  │   │
  │   ├── 状态机状态转移
  │   ├── 楼层位置更新 (current_floor +/- 1)
  │   ├── 门控计时器 (door_timer)
  │   ├── 空闲计时器 (idle_timer)
  │   └── 报警 LED 闪烁 (alarm_blink)
  │
  └── 分频产生 2kHz 方波 (buzzer_carrier)
      │
      └── 蜂鸣器驱动

注意:所有逻辑在同一主时钟域内工作。
      1Hz 使能脉冲不作为门控时钟使用,而是作为时钟使能 (clock enable)。
      即在 rising_edge(clk) 内部用 if clk_1hz_en = '1' then ... 来控制慢速逻辑。
      这避免了门控时钟带来的时序问题。

使能脉冲方式的 VHDL 模板:

-- 分频计数器,产生 1Hz 使能脉冲
signal div_cnt : integer range 0 to 49999999;  -- 50M-1
signal clk_1hz_en : std_logic;

process(clk, rst_n)
begin
  if rst_n = '0' then
    div_cnt <= 0;
    clk_1hz_en <= '0';
  elsif rising_edge(clk) then
    if div_cnt >= 49999999 then
      div_cnt <= 0;
      clk_1hz_en <= '1';   -- 单周期脉冲
    else
      div_cnt <= div_cnt + 1;
      clk_1hz_en <= '0';
    end if;
  end if;
end process;

-- 状态机时序进程中使用使能脉冲
process(clk, rst_n)
begin
  if rst_n = '0' then
    current_state <= IDLE;
    current_floor <= 1;
    -- ... 初始化
  elsif rising_edge(clk) then
    if clk_1hz_en = '1' then
      current_state <= next_state;
      -- ... 慢速逻辑更新
    end if;
    -- 请求置位逻辑不受使能脉冲限制(按钮响应需要快速)
    -- ... 请求寄存器更新
  end if;
end process;