From 5d06e69a41a4d37d12101130452ffaa4ce746966 Mon Sep 17 00:00:00 2001 From: hulk Date: Fri, 18 Sep 2026 00:41:09 +0800 Subject: [PATCH] =?UTF-8?q?feat(DLPilot):=20M303/M304=20SQ52205=20?= =?UTF-8?q?=E7=94=B5=E6=BA=90=E5=AE=9E=E6=B5=8B=E5=8D=8F=E8=AE=AE=E5=AF=B9?= =?UTF-8?q?=E6=8E=A5(=E8=A7=84=E5=88=99=E5=85=88=E8=A1=8C,=20=E6=8C=89?= =?UTF-8?q?=E9=92=AE=E5=90=8E=E7=BB=AD=E6=8E=92=E7=89=88)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DLPilot/AGENTS.md | 7 +++++- DLPilot/README.md | 2 ++ DLPilot/release/pattern_slots.json | 2 +- DLPilot/src/dlpc_rs485.py | 40 +++++++++++++++++++++++++++++- DLPilot/src/selftest_rs485.py | 14 +++++++++++ 5 files changed, 62 insertions(+), 3 deletions(-) diff --git a/DLPilot/AGENTS.md b/DLPilot/AGENTS.md index 447a7f6..59cf28a 100644 --- a/DLPilot/AGENTS.md +++ b/DLPilot/AGENTS.md @@ -101,11 +101,16 @@ DLPilot/ **同时是转接板光机 DLPC3421 的 nHD 面板原生分辨率**(该板 shadow EDID 只声明 640x360@60), 投 640x360 图案 + RS485 开光机 = 整链路 1:1 验证。 - bw 系列图案各自目的见 `src/gen_bw_patterns.py` 顶部清单注释。 -- 光机 RS485 命令集(M730/M731/M732/M201/M202/M737/M999/M888/M7/M8/M1)语义、 +- 光机 RS485 命令集(M730/M731/M732/M201/M202/M737/M999/M888/M7/M8/M1/**M303/M304**)语义、 帧格式 `D5 01 LEN CRC`(累加和校验)、错误码与**就绪门禁** (DLPC 未就绪时 M730/M731/M732 被拒回 0xFF)以上游 `protocols.md` 为唯一权威, 本仓不维护协议文档副本;注意 **M7S0 是"开启"TEC 自动控温**(方向反直觉)、 M737 在定时投光中延迟应答(CLI 等待上限 35s)。 +- **M303/M304 载荷是二进制例外**(SQ52205 电源实测):成功载荷 2 字节大端原始值, + **不是 ASCII**——Frame.payload 解出来是乱码,必须走 `payload_bytes()` 取原始字节; + M303=u16 mV(12V 轨窗口 11400~12600),M304=s16 补码 mA(正=正向,固件原样上总线, + 上位机按有符号解析,FF 06=−250)。封装在 `payload_u16_be/payload_s16_be/format_power`, + 回归向量在 selftest_rs485.py(真机抓取帧)。 ## 5. 构建 / 发布流水线(全部在 src/ 下执行) diff --git a/DLPilot/README.md b/DLPilot/README.md index ff08bf9..b98b4b6 100644 --- a/DLPilot/README.md +++ b/DLPilot/README.md @@ -106,6 +106,8 @@ GUI"光机控制"区:选串口 → 连接 → 点快捷按钮或命令下拉/ | `M731S100` | 设光强 54-800(RGB 同值,写后读回复核) | `ok` / `err:R..G..B..` | | `M732` | 查三路电流设置值(0-65535) | `okR110G110B110` | | `M201` / `M202` | 查 DMD / LAMP 温度(0.1°C) | `okTL:246` = 24.6°C | +| `M303` | 查母线电压(SQ52205 实测,**2 字节大端二进制载荷**) | 0x2EF4 = 12020 mV = 12.02 V(12V 轨窗口 11400~12600) | +| `M304` | 查负载电流(SQ52205 实测,**2 字节大端二进制载荷**) | s16 补码 mA:0x0034=+52;FF 06=−250(正=正向) | | `M999` | 查固件版本 | `okv20.0.0.1` | | `M737` | 查投光是否结束(投光中延迟应答) | `ok` | | `M7S0` / `M7S1` | TEC 自动控温开 / 关(注意 S0=开) | `ok` | diff --git a/DLPilot/release/pattern_slots.json b/DLPilot/release/pattern_slots.json index a4bb7b5..5fc7ecf 100644 --- a/DLPilot/release/pattern_slots.json +++ b/DLPilot/release/pattern_slots.json @@ -43,7 +43,7 @@ } ], "serial": { - "port": "COM5", + "port": "COM15", "baud": 115200, "bytesize": 8, "parity": "N", diff --git a/DLPilot/src/dlpc_rs485.py b/DLPilot/src/dlpc_rs485.py index e369f14..4dab529 100644 --- a/DLPilot/src/dlpc_rs485.py +++ b/DLPilot/src/dlpc_rs485.py @@ -7,7 +7,9 @@ DLPC3421 光机 RS485 控制链路(板上 GD32E230 作 RS485→I2C 网关) 响应语义/错误码/就绪门禁与实测帧示例): 物理层 RS485 半双工 115200 8N1(PC 经 USB-RS485 转换器接板, DE 由 MCU 硬件换向) 命令帧 D5 01 LEN CRC 01=板类型, LEN=命令字节数 - 响应帧 B5 TYPE LEN CRC payload 为 ASCII(ok / err:OL / okTL:246 ...) + 响应帧 B5 TYPE LEN CRC payload 为 ASCII(ok / err:OL / okTL:246 ...), + 例外: M303/M304 载荷为 2 字节大端二进制 + (SQ52205 电源实测, 见 POWER_COMMANDS) CRC 帧内第 2 字节(板类型)起累加到最后一个 payload 字节, 取低 8 位 帧长 最小 6 字节, 最大 32 字节 就绪门禁: DLPC3421 启动完成前 M730/M731/M732 被拒(回 0xFF), 属预期。 @@ -81,6 +83,42 @@ PRESETS = ( Frame = collections.namedtuple('Frame', 'type_code payload raw') +# --- M303/M304(SQ52205 电源实测值查询): 成功载荷为 2 字节原始二进制(非 ASCII) --- +# M303 母线电压: u16 大端, 单位 mV; 12V 轨正常窗口 11400~12600(标称 ±5%) +# M304 负载电流: s16 大端补码, 单位 mA; 正=正向, 负=反向(固件原样上总线, +# 上位机必须按有符号解析)。例: 载荷 FF 06 = -250 mA +# 错误时 TYPE=0xFF, 载荷为错误信息字节串(走通用译码路径) +POWER_COMMANDS = ('M303', 'M304') + + +def payload_bytes(fr): + """响应帧的原始载荷字节(Frame.payload 是 ASCII 解码视图, 二进制载荷请用本函数)""" + return fr.raw[3:-1] + + +def payload_u16_be(raw): + return (raw[0] << 8) | raw[1] + + +def payload_s16_be(raw): + v = payload_u16_be(raw) + return v - 0x10000 if v & 0x8000 else v + + +def format_power(cmd, fr): + """M303/M304 成功应答 -> 人读文本; 非 POWER 命令/失败/载荷异常返回 None""" + if cmd not in POWER_COMMANDS: + return None + raw = payload_bytes(fr) + if fr.type_code != 0xF0 or len(raw) != 2: + return None + if cmd == 'M303': + mv = payload_u16_be(raw) + return f'{mv} mV ({mv / 1000:.2f} V)' + ma = payload_s16_be(raw) + return f'{ma:+d} mA' + + def sum_crc(data): """帧内第 2 字节(板类型)起累加到末 payload 字节, 取低 8 位(不含包头与 CRC)""" return sum(data) & 0xFF diff --git a/DLPilot/src/selftest_rs485.py b/DLPilot/src/selftest_rs485.py index 8c3d75f..4f52e82 100644 --- a/DLPilot/src/selftest_rs485.py +++ b/DLPilot/src/selftest_rs485.py @@ -73,5 +73,19 @@ for _name, cmd, _note in d.PRESETS: d.build_frame(cmd) print(f'预设 {len(d.PRESETS)} 条全部可组帧: PASS') +# 8) M303/M304(SQ52205 电源实测)回归向量: 上游真机抓取帧 +check('build_frame(M303)', d.build_frame('M303').hex(' ').upper(), + 'D5 01 04 4D 33 30 33 E8') +check('build_frame(M304)', d.build_frame('M304').hex(' ').upper(), + 'D5 01 04 4D 33 30 34 E9') +frames = d.FrameParser().feed(bytes.fromhex('B5 F0 02 2E F4 14')) +check('M303 应答帧', len(frames), 1) +check('M303 电压(0x2EF4=12020mV)', d.format_power('M303', frames[0]), + '12020 mV (12.02 V)') +frames = d.FrameParser().feed(bytes.fromhex('B5 F0 02 00 34 26')) +check('M304 应答帧', len(frames), 1) +check('M304 电流(0x0034=+52mA)', d.format_power('M304', frames[0]), '+52 mA') +check('M304 负值(FF06=-250mA 补码)', d.payload_s16_be(bytes.fromhex('FF 06')), -250) + print('RESULT:', 'FAIL:' + ','.join(fails) if fails else 'ALL PASS') sys.exit(1 if fails else 0)