LOP 厅外呼叫板 · 单片机与上位机交互协议
3 层电梯 LOP 系统:STM32F103 呼叫板(每层一块)与上位机(WPF / 未来 PLC)通过 CAN 250kbps 通信。
本文档完整说明双方如何交互、控制逻辑如何流转,为「用 PLC 代替上位机」提供协议与控制逻辑依据。
⌕
输入关键词实时过滤。
★快速开始 · 协议速查
三帧核心协议 + 一帧升级协议,全部标准帧,250kbps。
0x123 上位机 → 所有 LOP 板(广播)
轿厢状态帧,500ms 节流周期发送。8 字节:
| 字节 | 含义 | 值域 |
|---|---|---|
| data[0] | 当前楼层 | 1~3 |
| data[1] | 运行方向 | 0 停止 / 1 上行 / 2 下行 |
| data[2] | 门状态 | 0 关 / 1 开门中 / 2 开 / 3 关门中 |
| data[3] | 目标楼层 | 1~3 |
| data[4] | 故障码低字节 | 0=无故障 |
| data[5] | 故障码高字节 | 16 位故障码 |
| data[6..7] | 保留 | 0 |
0x200 LOP 板 → 上位机(事件触发)
呼梯呼叫帧,按钮按下沿触发发一帧(非周期轮询)。3 字节:
| 字节 | 含义 | 值域 |
|---|---|---|
| data[0] | 本板楼层 | 1~3 |
| data[1] | 方向 | 0 上行 / 1 下行 |
| data[2] | 校验 | 固定 0x55 |
0x201 上位机 → LOP 板(按楼层过滤)
灯控帧,登记状态变化时广播,只发变化的楼层方向。3 字节:
| 字节 | 含义 | 值域 |
|---|---|---|
| data[0] | 楼层 | 1~3 |
| data[1] | 方向 | 0 上行 / 1 下行 |
| data[2] | 亮灭 | 1 亮 / 0 灭 |
0x600 / 0x602 升级命令 / 应答(可选)
CAN Bootloader 升级协议,正常运行时不用,详见技能 stm32-can-bootloader。
一系统架构
硬件拓扑与角色分工——先看清谁在总线上、各自负责什么。
硬件拓扑
┌────────────┐ CAN 总线 250kbps 标准帧 ┌────────────┐
│ 上位机 │ ──────────────────────────────→ │ 1F LOP 板 │ STM32F103 + 拨码=1
│ WPF / PLC │ ←────────────────────────────── │ 2F LOP 板 │ STM32F103 + 拨码=2
│ (控制器+显示)│ CANH / CANL(两端 120Ω) │ 3F LOP 板 │ STM32F103 + 拨码=3
└────────────┘ └────────────┘
│
└── 轿内 OLED(显示 0x123 状态,无按钮)
角色分工
LOP 板(单片机)厅外呼叫终端 ×3
发:0x200 呼叫帧(按钮按下沿)
收:0x123 状态帧(更新 OLED)、0x201 灯控帧(按楼层过滤控制指示灯)
硬件:PB10/PB11 按钮、PB12/PB13 指示灯、OLED(I2C)、PA0~PA3 拨码定义楼层
原则:按钮本地按下立即亮灯(即时反馈),最终熄灭由上位机 0x201 决定
收:0x123 状态帧(更新 OLED)、0x201 灯控帧(按楼层过滤控制指示灯)
硬件:PB10/PB11 按钮、PB12/PB13 指示灯、OLED(I2C)、PA0~PA3 拨码定义楼层
原则:按钮本地按下立即亮灯(即时反馈),最终熄灭由上位机 0x201 决定
上位机(当前 WPF / 未来 PLC)控制器 + 轿厢状态源
发:0x123 状态帧(500ms 周期)、0x201 灯控帧(登记状态变化时)
收:0x200 呼叫帧 → 登记呼梯 → 调度电梯 → 服务完成清除登记 → 广播灭灯
职责:电梯运行逻辑(方向/目标/门/故障)+ 灯状态维护 + 轿厢状态广播
收:0x200 呼叫帧 → 登记呼梯 → 调度电梯 → 服务完成清除登记 → 广播灭灯
职责:电梯运行逻辑(方向/目标/门/故障)+ 灯状态维护 + 轿厢状态广播
二协议帧详解 · 0x123 轿厢状态帧
上位机 → 所有 LOP 板广播。500ms 节流发送(SendInterval)。
0x123 标准帧 DLC=8
| 字节 | 字段 | 编码 | 说明 |
|---|---|---|---|
| 0 | CurrentFloor | 1~3 | Clamp 到 1..3 |
| 1 | TravelDirection | 0/1/2 | 1=上、-1=下 → 1/2;停=0 |
| 2 | DoorState | 0~3 | Opening=1, Open=2, Closing=3, 其他=0 |
| 3 | TargetFloor | 1~3 | Clamp 到 1..3 |
| 4 | FaultCode L | 0~255 | 16 位故障码低字节 |
| 5 | FaultCode H | 0~255 | 16 位故障码高字节 |
| 6..7 | 保留 | 0 | — |
二协议帧详解 · 0x200 呼叫帧
LOP 板 → 上位机。按钮按下沿触发,发一帧即止(事件驱动,不周期上报)。
0x200 标准帧 DLC=3
| 字节 | 字段 | 编码 | 说明 |
|---|---|---|---|
| 0 | 楼层 | 1~3 | 本板拨码楼层 |
| 1 | 方向 | 0/1 | 0 上行 / 1 下行 |
| 2 | 校验 | 0x55 | 固定魔数 |
(1,0) 1F 上行 / (2,0)(2,1) 2F 双向 / (3,1) 3F 下行 —— 非法帧丢弃。
✅
事件触发要点。按钮去抖 20ms 后只在「高→低」按下沿返回 1;按住不放不会重复发帧。CAN 邮箱忙时置 Pending 标志,每 20ms 重试装入邮箱,发成功才清——事件不丢,但绝不周期重发。
二协议帧详解 · 0x201 灯控帧
上位机 → LOP 板广播。按楼层过滤:data[0] 必须等于本板楼层才响应。
0x201 标准帧 DLC=3
| 字节 | 字段 | 编码 | 说明 |
|---|---|---|---|
| 0 | 楼层 | 1~3 | 目标板楼层(其他板忽略) |
| 1 | 方向 | 0/1 | 0 上行灯 / 1 下行灯 |
| 2 | 亮灭 | 1/0 | 1 亮 / 0 灭 |
⚠️
灯误灭保护(最容易踩的坑)。如果全量同步无条件广播当前灯状态,会把「未登记的灭」发给单片机,覆盖本地按下的亮灯——表现为按按钮灯亮一下又灭。所以「灭」必须只由服务完成触发。
二协议帧详解 · 0x600/0x602 升级帧
CAN Bootloader 升级协议,正常运行时不涉及,供将来批量升级用。详见技能 stm32-can-bootloader。
0x600 上位机 → 板(升级命令) [A5 进 / AA 探测]
data[1]=目标楼层或 0 广播,data[2]=0x55。应用侧只响应 PING(0xAA 回 ACK) 和 ENTER(A5 写 BKP 魔数复位进 Bootloader)。
三交互流程 · 呼梯完整链路
从乘客按按钮到电梯服务完成灯熄灭,数据怎么流。
1. 乘客按 2F 上行按钮
2F LOP 板:去抖 20ms → 按下沿 → 本地 PB12 灯亮 + 发 0x200 [2, 0, 0x55]
上位机:RX 线程收到 → IsValidCallFrame 校验 ✓ → CallHall(2, up) → 控制器登记 2F 上行
2. 控制器处理呼梯
ElevatorController.Advance():检测 2F 上行登记 → 置 HallUpLamp[2] 亮 → 选择方向/目标
快照 LampState 变化 → UpdateLamps → 发 0x201 [2, 0, 1] 亮灯(2F 板灯保持亮)
3. 电梯运行与到站
上位机每 500ms 发 0x123 [楼层,方向,门,目标,故障] → 所有 LOP 板 OLED 实时显示
电梯到 2F → 开门 → 乘客进出
4. 服务完成 → 灭灯
控制器清除 2F 上行登记(HallUpLamp[2] true→false)→ 变化触发发 0x201 [2, 0, 0]
2F 板收到 → PB12 灯灭(只在门关到位后,见注意事项)
💡
关键。0x123 是唯一周期帧(500ms);0x200 事件触发;0x201 变化触发 + 每 2s 全量补刷亮。上位机靠这三条闭环,无轮询按钮。
三交互流程 · 灯控同步规则
上位机侧 SendLampState 的完整判定逻辑(PLC 移植时照抄)。
SendLampState(floor, up, current, previous, force)
每拍对 4 个灯位调用(1F上 / 2F上 / 2F下 / 3F下):
跳过条件 1:首次(previous==null)且当前为灭 → 不广播(防覆盖本地亮灯)
跳过条件 2:状态无变化,且不是「全量同步强制刷亮」→ 不广播
发送:其余情况(亮变化 / 灭变化 / 全量同步且当前亮)→ 发 0x201
跳过条件 1:首次(previous==null)且当前为灭 → 不广播(防覆盖本地亮灯)
跳过条件 2:状态无变化,且不是「全量同步强制刷亮」→ 不广播
发送:其余情况(亮变化 / 灭变化 / 全量同步且当前亮)→ 发 0x201
全量同步节奏 LampFullSyncInterval = 2s
首次打开 CAN 时 + 之后每 2s:对当前为亮的灯强制重发 0x201(防丢帧导致板灯与实际不一致);
发送失败时退避 100ms 重试,不更新 lastLamps 以便下轮补发。
发送失败时退避 100ms 重试,不更新 lastLamps 以便下轮补发。
三交互流程 · 离线检测
LOP 板如何判断通讯未建立。
STATUS_TIMEOUT_MS = 1500ms
LOP 板连续 1.5s 未收到 0x123 → ElevatorStateOnline=0 → OLED 显示 CAN OFFLINE 界面(第 1 行显示 VER V1.3.0 版本号,含诊断统计行 BOFF/RX/OVFL/FLT)。收到任意合法 0x123 立即恢复在线。
⚠️
PLC 替代时注意。PLC 必须周期发 0x123(建议 ≤500ms),否则所有 LOP 板会切到离线界面。停梯但正常的电梯也要持续广播当前状态。
四控制逻辑 · 电梯控制器(上位机侧)
当前 WPF 上位机的 ElevatorController 是纯函数状态机(100ms 步进),将来移植 PLC 的逻辑蓝本。
呼梯登记 CallHall(floor, up)
收到 0x200 → 置 HallUp/HallDown 中间点 → 控制器下一拍检测上升沿 → 登记(灯亮)→ 消费复位中间点(等价 PLC 复位中间点)。同一层同向重复呼叫会重新登记,电梯不空闲(真实时序:乘客 A 离开后 B 再呼)。
调度决策 方向/目标/顺路
按当前方向优先顺路响应(距离吸收 150 单位内)、层楼顺序、门开关状态机(TON 定时)推进。
3 层改造后的关键:中间层距顶层仅 1 层(=100 单位),"距离吸收"场景在 3 层不存在——顺路判断要按 3 层重排。
3 层改造后的关键:中间层距顶层仅 1 层(=100 单位),"距离吸收"场景在 3 层不存在——顺路判断要按 3 层重排。
服务完成 → 灯灭
电梯到站开门 → 乘客进出 → 门重新关到位后清除呼梯登记(不是到站就清)→ 灯灭广播 0x201。
轿内选层(COP)无硬件,界面模拟按钮 → SetTarget(floor) 同一登记链路。
轿内选层(COP)无硬件,界面模拟按钮 → SetTarget(floor) 同一登记链路。
四控制逻辑 · LOP 板(单片机侧)
统一程序烧 3 块板,拨码定义楼层,角色自动分支。
楼层识别 拨码 PA0~PA3
4 位拨码二进制:1F=0001, 2F=0010, 3F=0011。MAX_FLOOR=3,非法值 OLED 显示 DIP ERROR。
角色规则:1F 仅上行、顶层仅下行、中间层双向,无效方向按钮不响应。
角色规则:1F 仅上行、顶层仅下行、中间层双向,无效方向按钮不响应。
主循环节拍 非中断轮询
按钮扫描 20ms;CAN 轮询每轮最多 8 帧(防饿死按钮/显示);OLED 只在 DisplayDirty 时刷新;
灯控 0x201 按楼层过滤(data[0]==FloorNumber 才响应,方向 data[1]≤1、亮灭 data[2]≤1 校验)。
灯控 0x201 按楼层过滤(data[0]==FloorNumber 才响应,方向 data[1]≤1、亮灭 data[2]≤1 校验)。
四控制逻辑 · 时序参数汇总
两端所有与交互相关的定时器。
定时参数表
| 参数 | 值 | 位置 | 用途 |
|---|---|---|---|
| SendInterval | 500ms | 上位机 | 0x123 状态帧节流 |
| LampFullSyncInterval | 2s | 上位机 | 灯控全量同步(只刷亮) |
| 发送失败退避 | 100ms | 上位机 | Transmit 失败重试间隔 |
| 控制器步进 | 100ms | 上位机 | ElevatorController Tick |
| RX 轮询 | 1ms+5ms | 上位机 | Receive 阻塞 1ms + Sleep 5ms |
| STATUS_TIMEOUT_MS | 1500ms | LOP 板 | 未收 0x123 → 离线界面 |
| 按钮扫描/去抖 | 20ms | LOP 板 | 扫描周期 = 去抖稳定时间 |
| 邮箱重试 | 20ms | LOP 板 | 0x200 装入失败重试 |
| OLED 掉线恢复 | 500ms | LOP 板 | I2C 掉线重试间隔 |
五PLC 替代上位机 · CAN 硬件方案
用 PLC 替换 WPF 上位机时,先解决 CAN 接入。
方案 1 PLC 自带/扩展 CAN
西门子 S7-1200/1500 选配 CAN 模块(如 CM CANopen 经网关),或用第三方 CANopen 主站模块。需确认支持标准帧 + 250kbps + 自定义 ID(本协议非标准 CANopen 对象,是裸 CAN 帧)。
方案 2 USBCAN 网关桥接(推荐过渡)
保留 PC 上的 USBCAN 转接程序(复用现有 ECAN.cs 封装),PC 与 PLC 之间用 Modbus TCP/OPC UA 交换数据。
PLC 程序只写控制逻辑,CAN 收发仍由 PC 网关完成——风险最低,协议帧解析完全复用。
PLC 程序只写控制逻辑,CAN 收发仍由 PC 网关完成——风险最低,协议帧解析完全复用。
五PLC 替代上位机 · 需实现的逻辑
把当前 WPF 上位机三个职责搬进 PLC(或网关+PLC 分工)。
① 收 0x200 → 登记呼梯
CAN 收到 0x200 → 校验(楼层1~3/方向0~1/0x55/合法组合)→ 置位外呼中间点 → 上升沿登记(等价当前 CallHall → 中间点 → 控制器消费)。
② 发 0x201 灯控(登记亮/服务完灭)
登记时发亮;门关到位后清登记发灭。必须遵守:全量同步只刷亮、灭只由变化触发、首次灭不广播——否则按按钮灯会闪灭。
③ 发 0x123 轿厢状态(500ms 周期)
周期广播当前楼层/方向/门/目标/故障。停梯也必须持续广播(否则 LOP 板 1.5s 后离线)。
故障码 16 位:低字节 data[4]、高字节 data[5]。
故障码 16 位:低字节 data[4]、高字节 data[5]。
④ 电梯调度核心逻辑
方向选择(当前方向顺路优先)、目标楼层、门状态机(TON 定时)、故障处理。
现有 ElevatorController 是纯函数状态机(无 async/await,时间基准 ElapsedSeconds),可直接逐行翻译为 SCL(详见技能 plc-sil-verification)。
现有 ElevatorController 是纯函数状态机(无 async/await,时间基准 ElapsedSeconds),可直接逐行翻译为 SCL(详见技能 plc-sil-verification)。
五PLC 替代上位机 · 验收清单
替换后逐项验证,确保行为与 WPF 版一致。
验收测试项
1. 按 1F 上行 → 1F 灯亮 + PLC 登记 → 电梯下行到 1F 开门
2. 服务完成门关后 → 1F 灯灭(不是开门瞬间灭)
3. 按 2F 双向 → 2F 两灯亮 → 电梯响应一个方向后对应灯灭
4. 运行中按同向中间层 → 顺路响应(3 层无距离吸收,直接顺路)
5. 拔掉上位机 CAN → 1.5s 后所有 LOP 板离线界面;恢复 → 在线
6. 长时间运行(≥2s 全量同步窗口)灯状态始终与实际一致
7. 故障置位 → 0x123 故障码正确 → LOP 板 OLED 显示
2. 服务完成门关后 → 1F 灯灭(不是开门瞬间灭)
3. 按 2F 双向 → 2F 两灯亮 → 电梯响应一个方向后对应灯灭
4. 运行中按同向中间层 → 顺路响应(3 层无距离吸收,直接顺路)
5. 拔掉上位机 CAN → 1.5s 后所有 LOP 板离线界面;恢复 → 在线
6. 长时间运行(≥2s 全量同步窗口)灯状态始终与实际一致
7. 故障置位 → 0x123 故障码正确 → LOP 板 OLED 显示
六注意事项
联调实测沉淀的经验与坑。
💡
灯灭时机。PLC 外呼灯在「门重新关到位」后才熄灭(接客等待释放逻辑),不是到站开门时——断言灯灭必须等门关,否则误报。
💡
0x123 是心跳。LOP 板把 0x123 当「通讯存在」信号,不只是显示数据。PLC 必须保证周期发送,哪怕电梯完全停着。
✅
事件 vs 周期。按钮呼叫是事件(按下发一帧);灯控是变化+全量补刷;状态是周期。PLC 实现时别把按钮改成周期扫描上报——总线浪费且时序不一致。
✅
LOP 板本地亮灯是「乐观反馈」。按下即亮,给乘客即时反馈;真正权威状态在上位机/PLC。两者冲突时以 0x201 为准(这就是灯控规则存在的意义)。
⚠️
广播升级(0)危险。升级协议广播会令所有板同时进升级模式,一次失败全擦。多板必须逐块指定楼层升级(详见技能 stm32-can-bootloader)。
⚠️
急停等安全信号不要走 CAN。按电梯安全规范,急停/安全回路必须硬接线直连控制柜,不允许由通信链路承载(见会话讨论)。CAN 只做状态/呼叫/灯控。
没有匹配的内容,换个关键词试试。