简介:《Otis RSL远程串行接口协议标准》是一份面向电梯通信开发、接口调试与维护人员的英文技术文档,围绕 Otis 远程串行链路(RSL)的数据交换规范展开,可用于理解电梯产品与外部设备之间的通信机制。资源为 PDF 格式,共 1 个文件,压缩包约 663KB,原文约 16 页,含修订记录、目录和版权声明,便于查阅归档。文档从协议概览、OSI 七层模型映射讲到物理层、链路层与应用层,涵盖 RS-485 半双工通信、信号电平、帧结构、CRC 校验、命令集与错误代码等要点,并给出初始化、建链、传输、错误处理和断开连接的流程。已有 138 人学习,适合需要核对 rs5、rs14、rs18 等协议细节、开展串行接口对接与故障排查的工程技术人员参考。
1. 一帧 800 微秒只驮 5 个 bit:RSL 远程串行接口协议标准到底解决什么问题
RSL(Remote Serial Link,远程串行接口协议)最容易被误判成 RS-485 的变种。我第一次翻这份十来页的标准时也这么想,直到把示波器夹上去才发现不对:它有独立的时钟线,四根线里两根走信号、两根送电,主站打拍子、从站跟着节拍收发,本质上是一条同步移位总线,一轮循环里写段 64 个帧时隙、读段 64 个帧时隙,每个时隙只有 5 个 bit。
这份文档给的是接口层契约——物理电平、帧时序、位映射怎么排,不涉及具体业务含义。它适合正在对接老式控制柜外呼/内召面板、楼层显示器、指示灯阵列的人:手上有一块面板或一块控制板,想知道它到底按什么节拍说话,这里能对上。不适合想找现成驱动库的人,因为公开的实现几乎没有,绝大多数工作得自己从时序图开始搭。
2. 四线制物理层:DL1/DL2/Return/VRS 怎么接,300 米线长怎么算
2.1 分层映射:为什么 3~6 层是空的
标准里有一张 OSI 七层对照表,是最该先看的一页。它明确写了:这条总线只实现物理层、链路层和应用层,中间四层标注为“Not Supported”。这不是偷懒,而是一条单主多从、点到点的位导向总线,本来就不需要路由、分段、会话管理和表示层转换。
| OSI 层 | 本标准的实现情况 | 承载内容 |
|---|---|---|
| 7 应用 | 有 | 位映射定义:Basic、CPI-11/NEL、Global Aesthetics 三套 |
| 6 表示 | 无 | — |
| 5 会话 | 无 | — |
| 4 传输 | 无 | 无差错重传、无流控,出错就靠链路层判据丢弃 |
| 3 网络 | 无 | 无地址路由,靠帧时隙位置区分从站 |
| 2 链路 | 有 | RSL 帧 |
| 1 物理 | 有 | RSL 位、电平、连接器 |
第三层缺失是这个协议最反直觉的地方:从站没有地址字节。主站在写周期里按固定顺序依次填 64 个帧时隙,第 N 个时隙就是第 N 号从站的位置;从站在读周期里同样按位置把自己的位塞进对应时隙。换句话说,寻址靠的是“你在第几个节拍上应答”,而不是报文里带 ID。这一点决定了后面所有的调试方式——你没法用串口助手发一条“读地址 0x05”的命令去试探,只能整轮整轮地抓。
对比一下常见的三种接口,差别就很清楚了:
| 维度 | RSL | RS-485 | RS-232 |
|---|---|---|---|
| 线数 | 4 根(2 信号 + 2 电源) | 2 根差分 | 3 根(TX/RX/GND) |
| 同步方式 | 独立时钟线同步 | 异步,靠双方波特率约定 | 异步 |
| 拓扑 | 单主多从,全部并联 | 多点半双工 | 点对点 |
| 标称距离 | 300 m | 1200 m(低速时) | 15 m 量级 |
| 寻址 | 帧时隙位置 | 报文里的地址字节 | 无 |
把 RSL 按 RS-485 去接,是最常见的翻车起点:RS-485 要方向控制引脚,RSL 没有;RS-485 靠波特率对齐,RSL 靠时钟线对齐;RS-485 的差分对是 A/B,RSL 的两根信号线一根是数据、一根是时钟。硬件上看着都是“两根线传数据”,协议行为完全不同。
2.2 四针连接器的定义与线对分工
从站侧(面板类设备)用的是一排 4 针连接器,一根线束同时把电和信号送过去:
| 引脚 | 信号名 | 文档里的描述 | 实际作用 |
|---|---|---|---|
| 1 | DL1 | 差分信号 | 数据线,主站写、从站读 |
| 2 | DL2 | 差分信号 | 时钟/同步线,由主站驱动 |
| 3 | Return | 电源回线 | 面板电子的回流路径 |
| 4 | VRS | +33 V | 面板供电正端 |
这里有个文档自身的不一致,我踩过:连接器表把 DL1、DL2 都写成“Differential – signal for RSL”,看着像一对差分信号;但正文 4.1.4.1 明确写了 L1 是数据线、L2 是时钟线用于同步。接线时以正文为准——它是两根独立功能的单端信号,共模判据是拿 L1 相对 L2 来做参考的,不是真正的差分对。
提示:如果你的板子上 DL1/DL2 接反了,现象不是完全不通,而是能收到一串毫无规律的位,因为时钟线被当成数据线在采。先拿示波器确认哪根上有 100 µs 的规律脉冲,那根就是 DL2。
2.3 电平判据:-6~35 V 是耐受,不是逻辑电平
物理层表格里最容易看错的一行是信号电压范围:-6.0 V 到 35.0 V。这是绝对最大耐受范围,不是判据。真正的判据是分两档的:
| 参数 | 最小 | 最大 | 单位 |
|---|---|---|---|
| 数据线绝对电压 | -6.0 | 35.0 | Vdc |
| 共模抑制电压 | — | +4.0 | Vdc |
| 逻辑 0 | -6.0 | 0.8 | Vdc |
| 逻辑 1 | 17.0 | 35.0 | Vdc |
| 供电电压 | 17.7 | 35.0 | Vdc |
| 供电压降 | — | 2.0 | Vdc |
注意逻辑 0 和逻辑 1 之间有一大段空白:0.8 V 到 17 V 之间是未定义区。这不是留的余量,是因为总线上挂着几十上百个从站,长线反射和负载差异会让边沿变缓,落在中间区的样点本来就应该被丢弃。
共模判据更值得琢磨:
- 高电平:VL1 ≥ VL2 + 0.8 V
- 低电平:VL1 ≤ VL2 + 0.3 V
也就是说,采样的时候不能拿一个固定的地参考去比,而要拿时钟线 L2 当时的电平作为浮动参考。0.3 V 和 0.8 V 之间那 0.5 V 是回滞带,用于抑制噪声引起的判据抖动。做接收电路时,比较器的一端接 L1,另一端接 L2 加一个偏置,而不是接 GND——这一点如果做错,长线上会因为地电位差随机丢位,而且这种丢位是“今天好明天坏”的玄学,非常难查。
2.4 线材、抽头与 300 米上限的算法
传输介质的要求写得很具体:两条数据线用双绞,端到端特性阻抗约 100 Ω,分布电容约 60 pF/m;最大串行线长 300 m,单条抽头长度 2 m。
60 pF/m × 300 m = 18 nF,这个容性负载和 100 Ω 阻抗配起来,边沿会被明显拉缓。100 µs 的位宽对边沿不敏感(这是它能跑 300 m 的根本原因,位宽太宽了),但对反射敏感——2 m 的抽头限制就是为了压反射。实际布线时会遇到的做法是:主干走双绞、总线型串到底,每个从站从主干上“摘”一小段出来,这段不超过 2 m;不要做成星形从一点发散出去。
注意:星形接法在实验室短线上经常能跑通,装到现场就出问题。我见过一条 200 m 的主干,从中间分出去四路各 1.5 m 的支线,静态测试全过,一上电就间歇性丢帧,最后改成菊花链才稳。
电源线那一对可以双绞也可以平行,要求没数据线那么严,但阻抗和电容是同一套指标。工程上一般直接把两对线放在同一根四芯护套里,用同一规格的线材,省得现场接错。
2.5 电源预算:17.7 V 下限怎么倒推线径
供电标称 33 V,允许范围 17.7~35 V,配电线路上的压降不超过 2.0 V。这三个数放在一起,其实是在给整条线的电阻定预算。
假设一个面板从站取 30 mA,一条主干上挂 20 个,最远端累计电流按 600 mA 估(这是最坏情况,全部集中到末端),2.0 V / 0.6 A ≈ 3.3 Ω 的往返电阻预算。300 m 往返是 600 m,铜线电阻率按 17.5 Ω·mm²/km 算,600 m 对应 10.5 Ω·mm²,除以 3.3 Ω 得到截面积约 3.2 mm²——远超常见的 0.5 mm² 信号线。
所以实际工程里几乎不会真的靠一根线供 20 个面板,常见做法是分段供电,或者面板侧电流做得极小。文档只给了电压窗口和压降上限,没给电流值(原文写的是“board specific”),这部分必须按你实际的板子去算。我心里的一条经验是:远端电压低于 20 V 就说明余量不够了,因为线上还有动态压降,静态测量合格不代表满载合格。
3. 链路层时序:800 µs 一帧、64 帧时隙,主站怎么打拍子
3.1 帧结构拆解:时钟脉冲、5 个位、100 µs 死区
链路段是整个协议的骨架。一帧标称 800 µs,由三部分组成:一个 100 µs 的时钟脉冲、5 个各 100 µs 的数据位、以及界定数据位的 100 µs “死区”时隙。
这里有个理解上的坑:100 + 5×100 只有 600 µs,剩下 200 µs 从哪来?文档的说法是“数据位被 100 µs 死区界定”,正确的读法是死区嵌在位时间内部,而不是额外叠加——每一位的 100 µs 窗口里,有效采样区在中间,两头留出保护带。如果按“600 + 死区另算”去推,怎么凑都凑不到 800 µs,我第一次就是在这个数字上卡了半天。
5 个 bit 的排布在时序图上给的是位 1 到 5,主站在时钟脉冲之后依次驱动,从站在同样的位置采样。一帧只有 5 位,意味着这条总线上每个从站每轮能交换的信息量是 5 bit 写 + 5 bit 读,够点几个灯、读几个按钮,但要传楼层显示就得靠 ELD 那套帧格式攒。
3.2 三种子周期:写、同步、读
主站驱动的一轮完整数据周期由三段顺序组成:写周期、同步周期、读周期。时序图上标得很清楚:写段 51.2 ms、同步段 1.6 ms、读段 51.2 ms。
51.2 ms ÷ 800 µs = 64,正好是 64 个帧时隙。同步段的 1.6 ms 是 2 个帧时隙,文档写的是“两个不带时钟脉冲的帧”,插在每一次写/读之间。这两个空帧的作用是给总线一个明确的段落分隔——从站靠它判断“写段结束了,接下来轮到我说话”,也是靠它做帧计数的重新对齐。
一轮总时长约 104 ms,也就是差不多每秒 9.6 轮。这个刷新率对外呼按钮和指示灯完全够用,对楼层显示的数字刷新也够,但如果要做动画类的东西就别想了。
注意:同步段的判定不能靠“数满 64 帧就切换”,因为上电时你可能从任意一帧开始收。正确做法是靠“连续两个无明显时钟脉冲的帧”这个特征去捕获同步段,然后从同步段之后重新计数。这是从站能长期稳定工作的关键。
3.3 公差窗口:788~835 µs 意味着什么
帧时序表给了三个参数的上下限:
| 参数 | 最小 | 典型 | 最大 | 单位 |
|---|---|---|---|---|
| 周期时间 | 788 | 800 | 835 | µs |
| 时钟宽度 | 80 | 100 | 146 | µs |
| 数据位宽 | 80 | 100 | 146 | µs |
把周期时间拆开看:标称 800,允许 -12 / +35。也就是说主站的拍子本身就有 1.5%~4% 的抖动空间。这个数字对实现方式有直接约束。
如果你用软件延时循环去“凑”100 µs,在 8 位机上误差很容易超过 10%,一轮 130 帧累计下来就是几百微秒的漂移,跑几分钟就会错位一帧,而且错位之后不会自己恢复,看起来像“偶发丢站”。必须用硬件定时器的捕获/比较,或者至少用定时器中断做基准。
时钟宽度上限 146 µs 这个数比典型值高了 46%,说明标准允许比较宽的时钟脉冲,接收侧不要用“必须精确 100 µs”的思路去做判据。判据应该是区间判断:落在 80~146 µs 之间就认为是一个有效位时隙,落在外面就准备重新对齐。
3.4 从站采样实现:定时器捕获加状态机
从站侧真正要写的代码不多,核心就是一个状态机加一个时间戳。下面是从站接收的骨架,用定时器捕获时钟上升沿,按半位宽偏移采样数据线:
/* rsl_rx.c —— 从站侧接收骨架:定时器捕获对齐帧边界 */ #include <stdint.h> #define CLK_MIN_US 80 /* 文档 5.3:时钟宽度下限 */ #define CLK_MAX_US 146 /* 文档 5.3:时钟宽度上限 */ #define BIT_PERIOD_US 100 /* 位标称宽度 */ #define BITS_IN_FRAME 5 #define FRAMES_PER_SEG 64 /* 写段/读段各 64 个帧时隙 */ typedef struct { uint8_t bits; /* 本帧已收位数 0~4 */ uint8_t frame; /* 当前帧号 0~63 */ uint16_t shadow; /* 本帧暂存,低 5 位有效 */ uint16_t table[FRAMES_PER_SEG]; } rsl_rx_t; static rsl_rx_t rx; /* L2 上升沿中断:进入一个位时隙 */ void RSL_CLK_ISR(void) { static uint16_t last_edge = 0; uint16_t now = TIM_GetCounter(); /* 1 us 分辨率 */ uint16_t dt = (uint16_t)(now - last_edge); last_edge = now; /* 间隔跳出公差带:可能是同步段空档,也可能是噪声 */ if (dt < CLK_MIN_US || dt > CLK_MAX_US) { if (rx.bits != 0) { /* 半途中断,本帧作废 */ rx.bits = 0; rx.shadow = 0; rx.frame = (uint8_t)((rx.frame + 1) % FRAMES_PER_SEG); } return; } /* 上升沿后半个位宽采样,避开边沿抖动区 */ delay_us(BIT_PERIOD_US / 2); rx.shadow = (uint16_t)((rx.shadow << 1) | (DL1_READ() ? 1u : 0u)); rx.bits++; if (rx.bits == BITS_IN_FRAME) { rx.table[rx.frame] = rx.shadow & 0x1F; /* 只保留 5 位 */ rx.shadow = 0; rx.bits = 0; rx.frame = (uint8_t)((rx.frame + 1) % FRAMES_PER_SEG); } }逻辑说明:dt做的是相邻两个时钟沿的间隔测量,落进公差带才认为这是一个有效位;半位宽偏移采样是因为边沿附近电平和反射最不稳定;凑满 5 位就落进该帧的表项,帧号自增,溢出自动回绕到 0。
参数说明:CLK_MIN_US/CLK_MAX_US直接来自 5.3 的时钟宽度区间,改小会让抗噪变差,改大容易把噪声脉宽当成有效沿;FRAMES_PER_SEG对应写段 64 个时隙,如果你的模块版本不同要按实际改;BIT_PERIOD_US/2是采样点偏移,在长线上可以适当往后挪到 0.6 倍位宽,因为长线的上升沿更缓。
提示:中断里调
delay_us只是骨架写法,量产必须换成硬件方案——定时器比较输出触发 DMA 连续采样,或者用输入捕获记录边沿时间戳、事后再算采样点。长阻塞会拖垮主循环,而且延时的精度本身也不够稳。
3.5 错误检测:别自己发明校验
文档在链路层末尾用一节讲错误检测。这里最容易犯的错是:因为流传的版本这一节内容不全,就自己拍脑袋加一个 CRC-8 上去。
不能这么干。原因很直接——主站侧不认。RSL 的帧结构里只有 5 个数据位,塞完业务信息基本没有空间放强校验;标准给出的检测手段大概率是基于时序、位计数、帧计数的轻量判据,而不是报文级校验。你额外加字节,会直接把帧长打乱。
正确的做法是把这一节原文逐字对着实现:判据是什么、在哪个环节判、判定失败后是丢弃还是重发,全部照抄。如果手上的版本文档这一节是缺的,那就不实现,只做“时序越界即丢弃本帧”这一条——比自作聪明加一个双方都不认的校验要安全得多。
4. 应用层三种位映射:Basic、ELD 与 Global Aesthetics 怎么选
4.1 Basic 格式:主写从读与主读从写
应用层最基础的一套叫 Basic RSL 数据格式,只有两个方向的定义:主站写/从站读,以及主站读/从站写。它规定的是那 5 个 bit 分别对应哪个物理点,不涉及任何编码转换。
这套格式是“够用就好”的典型:按钮、钥匙开关这类位输入,指示灯、蜂鸣器这类位输出,直接一位对一位。对接老式外呼面板用这一套就够了,寄存器里的 5 个 bit 一一映射到面板上的五个点位,不需要解码层。
选它的判断标准很简单:如果你的从站设备上的信息全是开关量,不要往 ELD 上凑。有些实现为了“统一”把开关量也套进 ELD 帧格式,结果每轮多绕一层编解码,调试难度翻倍,收益为零。
4.2 ELD 帧:7 段与 16 段位图加字符码表
ELD 是电致发光显示器这类字符型面板的数据格式,也是这份文档里内容最厚的一块。它给了几样东西:ELD 帧的结构定义、7 段和 16 段 LCD 的字符位图、7 位和 16 位的字符码表、帧描述、以及一组 ELD 命令。
这里的组织方式是“双表”:一张是字符码表,把业务语义(比如某个方向箭头、某个楼层位)映射到码值;另一张是位图,把码值映射到具体点亮哪几段。实现时的顺序是先用码表查到码值,再用位图查到段码,最后把段码塞进帧的对应位置。
7 段和 16 段的差别不只是段数。7 段只能显示数字和少量字母,16 段能显示更多字符形态,所以同一个面板型号可能两种都用。麻烦在于:段序(a 到 g 谁在前、额外段排在哪个 bit)是厂商自己定的,文档里给了定义,但网上的通用段码表大多按另一种排法。直接照抄通用表,现象是数字能对、字母全乱,或者反过来。
4.3 Global Aesthetics:帧 0 与帧 1 的分工
第三套格式叫 Global Aesthetics,文档里按帧切分:Frame 0 和 Frame 1。这套的定位是通用的面板外观件,比 ELD 更“宽”,因为它要适配不同形态的显示和指示设备,所以用了两个帧来做分工——一个帧承载一类信息,具体哪类按文档 7.3 的定义走。
工程上的选择建议:如果从站是明确型号的显示器,优先按它的模块文档走 ELD;如果从站是通用外观件、型号不固定,走 Global Aesthetics 更稳,因为它的帧定义本身就是为了兼容做的抽象。两套不要在一条总线上混用,写段和读段的 64 个时隙分配会打架。
4.4 位图反查:把段码翻译成人能读的字符
调试时最有用的一件事,是能把抓到的段码直接翻成字符,否则你面对的是一串十六进制。下面这个反查表可以当模板用:
# seg_decode.py —— 7 段位图反查字符 # 段序 a~g 对应 bit0~bit6,这是临时排法, # 落地必须按文档 7.2.2 的位图定义重排,不能照抄 SEG7 = { 0b0111111: "0", 0b0000110: "1", 0b1011011: "2", 0b1001111: "3", 0b1100110: "4", 0b1101101: "5", 0b1111101: "6", 0b0000111: "7", 0b1111111: "8", 0b1101111: "9", } def decode(bits: int) -> str: """输入一帧 7 位段码,输出对应字符;未知组合返回 '?'""" return SEG7.get(bits, "?") if __name__ == "__main__": for raw in (0b0111111, 0b0000110, 0b1101101): print(f"{raw:07b} -> {decode(raw)}")逻辑说明:字典的键是段码整型值,值是字符。用.get而不是下标,是为了让未定义的组合不至于抛异常中断整批解码——调试阶段你会遇到大量“本来就不该出现”的段码,这些恰恰是最有信息量的。
参数说明:位序(哪个 bit 对应 a 段)必须和你实际抓到的数据对齐。做法是先抓一个已知显示(比如面板上明确显示“8”),看它哪几位是 1,反推出位序,再去重排字典。这一步不能省,省了就会得出“协议解析错了”的错误结论。
16 段的表结构一样,只是键变成 16 位、条目更多。建议把它单独放一个文件,从设备配置文件里读,别硬编码在业务代码里——不同面板型号的段序可能不一样,换型号时改配置比改代码安全。
5. 避坑与排查:五条从现场带回来的记录
5.1 接线与电平类的三条
**第一条:上电后从站完全没反应,主站侧看
本文还有配套的精品资源,点击获取