chore(HDMI-Tool): package_release 平铺复制图案/说明到 release/ — 与 zip 内容一致, 文件夹开箱即用

This commit is contained in:
2026-09-11 00:40:48 +08:00
parent d9ecff4e49
commit 22d0969569
19 changed files with 251 additions and 16 deletions
+7 -4
View File
@@ -19,8 +19,10 @@ HDMI-Tool 工作在两端:HDMI **源端**PC 显卡侧,EDID/分辨率/图
HDMI-Tool/
├── src/ 源代码区 —— 只有这里可以手改(logos/ 改图标源后重跑 make_ico.py
├── assets/ 生成资产区 —— gen_*.py 的产物(PNG 图案),可随时再生成,改动需提交
├── release/ 产物区 —— 机器生成,禁止手改:HDMI-Tool.exe(入库)
pattern_slots.jsonexe 运行配置,入库)、*.zipgitignore,只上传 Release
├── release/ 产物区 —— 机器生成,禁止手改:HDMI-Tool.exe、pattern_slots.json
bw/solid 图案(package_release.py 从 assets/ 平铺复制来,均入库)、
│ *.zipgitignore,只上传 Release)。release/ 本身=完整可直接运行的
│ 发布文件夹(exe+附属平铺),内容与 zip 一致
├── README.md 人类文档
└── AGENTS.md 本文
```
@@ -36,7 +38,7 @@ HDMI-Tool/
| `src/dlpc_rs485.py` | 光机 RS485 协议模块: 帧构造/增量解析/译码表/预设命令/串口线程(DLPC485) |
| `src/selftest_rs485.py` | 光机协议自检(对照上游实测帧向量, 无硬件回归, 改协议代码后必跑) |
| `src/build_exe.py` | PyInstaller 打包 → `release/HDMI-Tool.exe`onefile/noconsole,内嵌图标+pyserial |
| `src/package_release.py` | exe+图案+说明`release/HDMI-Tool-v<ver>-win64.zip`(校验栏位引用完整性) |
| `src/package_release.py` | 复制图案到 release/(平铺)`release/HDMI-Tool-v<ver>-win64.zip`(校验栏位引用完整性; release/ 与 zip 内容一致 |
| `src/publish_gitea.py` | 建/复用 Gitea Release 并上传 ziptag 默认取 zip 文件名) |
| `src/hdmi_source.ps1/.bat` | 独立 PowerShell 命令行工具(自包含,与 GUI 无代码共享) |
| `src/gen_bw_patterns.py` | 生成 `assets/bw_*.png` ×5640x360 黑白,需 Pillow |
@@ -103,7 +105,8 @@ python publish_gitea.py HDMI-Tool-v<版本>-win64.zip --replace --insecure #
发布约定:
- tag/release 命名 `HDMI-Tool-vX.Y.Z`zip 命名 `HDMI-Tool-vX.Y.Z-win64.zip`publish_gitea.py
依赖此命名从文件名推 tag);
- **exepattern_slots.json 改动跟随源码提交入库;zip 永不入库**(.gitignore 已挡)
- **exepattern_slots.json 与 release/ 下平铺的图案 png 改动跟随源码/资产生成提交入库
zip 永不入库**.gitignore 已挡);
- publish_gitea.py 幂等:release 已存在则复用,同名资产默认跳过,`--replace` 删旧重传;
- token 读环境变量 `GITEA_TOKEN`:本机已用 setx 持久化为用户级变量(新开的会话/终端自动
继承,值不入库;在 setx 之前启动的进程读不到,需重开终端),急用时也可临时传 `--token`
+1
View File
@@ -192,6 +192,7 @@ HDMI-Tool/
├── release/ # 构建与发布产物(机器生成区, 不手改)
│ ├── HDMI-Tool.exe # 打包单文件版(改动跟随源码提交入库)
│ ├── pattern_slots.json # exe 运行配置(栏位预置 + 串口记忆)
│ ├── *.png / README.md # 附属文件平铺副本(由 package_release.py 从 assets/ 同步)
│ └── *.zip # 发布包(不入库, 上传 Gitea Release
├── README.md # 本说明
└── AGENTS.md # 面向 AI/Agent 的工程档案(结构/约定/构建规程)
+224
View File
@@ -0,0 +1,224 @@
# HDMI-Tool — HDMI 源端调试 + 光机控制工具
> LT8619CHDMI→RGB888)→ DLPC3421 投影转接板调试配套的 PC 端工具:
> HDMI **源端**(EDID 查看、分辨率切换、纯色/测试图案投屏)+ **光机控制**
> RS485 直连板上 GD32 网关,开关光机/设光强/查温度电流)。
>
> - **只想用工具**:到 [Gitea Releases](https://gitea.hulk.wang/hulk/LT8619C_Debug_Tools/releases)
> 下载 `HDMI-Tool-vX.Y.Z-win64.zip`,解压双击 `HDMI-Tool.exe` 即可(Windows 10/11 x64,免安装、无需 Python)。
> - **要改代码/重新打包**:见 §8、§9。
>
> 更新:2026-09-11
---
## 1. 功能总览
| 功能 | 说明 |
|---|---|
| EDID 查看 | 枚举各显示输出口,从注册表读取 EDID 原始数据,按 EDID 1.4/CEA-861 解算:厂商/型号/尺寸/Gamma/输入类型/支持模式(DTD/已建立/标准/CEA-VIC)/首选时序完整参数/校验和 |
| EDID 逐字节解析 | "EDID 原始数据"框右上"逐字节解析"按钮:按偏移逐字段展开基础块与 CTA-861 扩展块,每字段附原始字节 hex;命令行等价 `--edid-fields` |
| 分辨率切换 | "分辨率"下拉列出显卡按该屏 EDID 提供的全部模式;**应用**=临时切换(不写注册表,重启/恢复即还原),**恢复**=回到本次会话切改前的模式 |
| 测试图案投屏 | 全屏投到所选显示器,测试卡区域可选 `640x360 / 800x600 / full 铺满`;内置 纯红/纯绿/纯蓝/黑白棋盘格/网格线 + R/G/B 三行 55/AA/FF 纯色值按钮 + 3 个自定义图片栏位 |
| 光机控制 | RS485 → 板载 GD32E230 网关 → DLPC3421:开关光机(可定时)、设光强、查电流/温度/版本、TEC 控制、软件重启;收发日志带协议译码(见 §5) |
| 智能显示拓扑 | 仅当检测到转接板(EDID 厂商 "XLS")接入但未进入桌面(Windows 卡在"仅电脑屏幕")时才强制扩展把它拉回;你主动选的"仅第二屏幕"等拓扑不会被改动 |
| 命令行模式 | 全部功能可脚本化调用,供自动化测试(见 §6、§7) |
| 波形分析脚本 | 逻辑分析仪 CSV 波形按占空比签名分段,输出行结构/占空比/消隐明细(见 §7.3) |
## 2. 下载与安装
1. 打开 [Releases 页面](https://gitea.hulk.wang/hulk/LT8619C_Debug_Tools/releases),取最新的
`HDMI-Tool-vX.Y.Z-win64.zip`
2. 解压到**任意可写目录**(自定义图片与栏位配置会写到 exe 旁边);
3. 双击 `HDMI-Tool.exe`。首次启动比脚本慢一两秒(onefile 解压),属正常现象。
包内包含:
```
HDMI-Tool/
├── HDMI-Tool.exe 主程序(单文件自包含, 光机控制的 pyserial 已内置)
├── pattern_slots.json 预置自定义栏位 + 串口记忆
├── bw_*.png ×5 640x360 黑白测试图
├── solid_*.png ×10 800x600 纯色测试图
└── README.md 本说明
```
## 3. GUI 使用
主窗口自上而下四块区域:
```
┌ 显示器: [ ... ▼] [刷新] ┐
├ 分辨率: [800x600@60 ▼] [应用] [恢复] ├ 显示器/EDID
│ 支持的模式(EDID 解算) | EDID 信息 ┘
├ 测试图案 — 图案按钮 / R,G,B 0x55,0xAA,0xFF / 测试卡区域 / 自定义栏位 ┐
├ 光机控制 — 串口 [连接] / 开关光机 / 光强 / 命令下拉 / 自定义命令 ┘ 投屏+光机
│ └ 收发日志(带协议译码与原始 hex)
└ EDID 原始数据 — hex dump [逐字节解析] ┘
```
1. 顶部显示器下拉选择目标屏(多屏时注意别选成主屏),"刷新"重新枚举;
2. **切分辨率**:选模式 → "应用";测完点"恢复"
3. **投测试图**:先选**测试卡区域**640x360 / 800x600 / full)→ 点图案按钮全屏投放,
**ESC** 或点击退出;R/G/B 三行纯色按钮(55/AA/FF)用于 RGB 位交换类测试
(55 与 AA 互为字节镜像对,FF 全高基准);自定义图片点"⚙"配置(图片自动复制到 exe 旁);
4. 区域与自定义栏位配置自动保存在 exe 旁的 `pattern_slots.json`
## 4. 典型测试场景
```
1. HDMI 接转接板 → 启动 HDMI-Tool → 刷新 → 选中板卡显示器
2. 板卡 shadow EDID 首选 640x360@60 → 测试卡区域选 640x360(与光机面板原生 1:1)
3. "光机控制"连接串口 → M999 查版本确认链路通 → M730S0 开光机
4. 点 R/G/B 行纯色按钮投图 → 目视/逻辑分析仪核对 RGB888 输出
- 纯红/绿/蓝:验证通道映射(R=D[23:16], G=D[15:8], B=D[7:0]
- 55/AA 对:验证位交换(0x606E)——图像应为 50% 灰阶占空比
5. M731S100 设光强 → M732 查电流读回复核 → M730S1 关光机
```
寄存器语义(0x606E 位交换 / 0x606D 整组顺序)与光机协议的判读基线见上游主仓库
[LT8619C_DLPC3421_HDMI](https://gitea.hulk.wang/hulk/LT8619C_DLPC3421_HDMI) 的 `doc/architecture/`
## 5. 光机控制(RS485 → GD32 网关 → DLPC3421
物理链路:PC 经 **USB-RS485 转换器**接板卡 A/B 端子,板上 GD32E230 做 RS485→I2C 网关
控制 DLPC3421I2C 地址 0x1B)。串口参数 **115200, 8N1**,协议细节以上游固件仓
`protocols.md` 为准:命令帧 `D5 01 LEN <ASCII命令> CRC`、响应帧 `B5 TYPE LEN <payload> CRC`
CRC 为第 2 字节起累加和取低 8 位。
GUI"光机控制"区:选串口 → 连接 → 点快捷按钮或命令下拉/自定义输入发送;日志区实时显示
`→ 命令` / `← 译码结果 (原始 hex)`,错误自动附释义(如 `err:3I2C NACK`)。
| 命令 | 功能 | 响应示例 |
|---|---|---|
| `M730S0` / `M730S1` | 开 / 关光机 | `ok` |
| `M730S0T2000` | 开光机并定时 2000ms 自动关(≤32700 | `ok`(期间 M737 延迟应答) |
| `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 |
| `M999` | 查固件版本 | `okv20.0.0.1` |
| `M737` | 查投光是否结束(投光中延迟应答) | `ok` |
| `M7S0` / `M7S1` | TEC 自动控温开 / 关(注意 S0=开) | `ok` |
| `M8S330` | TEC 凝露点 33.0°C200-3500.1°C | `ok` / `err:OL` |
| `M1` | 协议握手 | `ok` |
| `M888` | 软件重启 | `ok` 后 100ms 复位 |
注意:**DLPC3421 启动完成前 M730/M731/M732 被就绪门禁拒绝**(回 0xFF),上电后稍等几秒再发。
命令行等价(源码方式):
```
python hdmi_gui.py --port COM9 --dlpc-cmd M730S0 --dlpc-cmd M999
逐条发送并打印译码响应
```
## 6. 命令行模式(hdmi_gui.py
exe 版无控制台,`--dump/--edid-fields` 的输出不可见(`--dlpc-cmd` 仍可执行,靠退出码判断),
调试请用源码方式:
```
python hdmi_gui.py --dump 控制台输出 EDID 解算
python hdmi_gui.py --edid-fields 控制台输出 EDID 逐字节字段解析表
python hdmi_gui.py --test-pattern Blue --area 800x600 --seconds 5
出图 5 秒自动退出
python hdmi_gui.py --test-pattern Custom1 投自定义栏位 1
python hdmi_gui.py --port COM9 --dlpc-cmd M730S0 RS485 发光机命令(可多次)
python hdmi_gui.py --seconds 5 GUI 冒烟测试, 5 秒自动关
--test-pattern {Red,Green,Blue,Checker,Grid,Custom1..3}
--area {640x360,800x600,full} --monitor N --seconds S
--port COMx --baud 115200 --dlpc-cmd CMD(可重复; 任一命令失败退出码 1)
```
## 7. 辅助脚本(src/ 下)
### 7.1 hdmi_source.bat / .ps1 — 源端命令行工具
与 GUI 互补,适合脚本串联:
```
tools\hdmi_source.bat -List 枚举显示器与支持分辨率(验证 EDID)
tools\hdmi_source.bat -Monitor 1 -Mode 800x600 切模式
tools\hdmi_source.bat -Mode 800x600 -Pattern -Restore
切模式→出图案→退出后自动恢复
```
### 7.2 gen_bw_patterns.py / gen_solid_colors.py — 测试图生成
需 Pillow`py -m pip install pillow`)。重新生成 `assets/` 下全部图案:
- `gen_bw_patterns.py` → 640x360 黑白图 ×5
`bw_star.png` 西门子星(极限分辨率)、`bw_sweep.png` 频率扫描条纹(24px→3px)、
`bw_checker_sweep.png` 渐变棋盘(2→32px)、`bw_zoneplate.png` 波带片(各向混叠)、
`bw_testcard.png` 综合测试卡(灰阶/刻度/细棋盘/线宽组);
- `gen_solid_colors.py` → 800x600 纯色图 ×10R/G/B 各 55/AA/FF + 三原色),
与 GUI 内置纯色按钮同源,供自定义栏位或其他工具加载。
### 7.3 la_wave_analyzer.py / la_wave_analyzer3.py — 逻辑分析仪波形判读
分析 Kingst VIS 导出的边沿流 CSV(列:`Time[s], 通道A, 通道B[, 通道C]`
10ns 分辨率),按 50ms 窗口占空比签名自动分段,输出每段的签名(FF/55/AA/00)、
时长、行周期/有效区宽度/消隐(按 40MHz 像素时钟折算):
```
python la_wave_analyzer.py <capture.csv> # 2 通道(默认判读 B 口 D0/D1)
python la_wave_analyzer3.py <capture.csv> # 3 通道(RGB 三组同拍, ch8/9/10
```
### 7.4 selftest_rs485.py — 光机协议自检(无硬件)
对照上游 `protocols.md` 实测帧向量回归帧构造/解析/译码:`python selftest_rs485.py`
全部通过退出码 0。改 `dlpc_rs485.py` 后必须跑一遍。
## 8. 目录结构与从源码运行
```
HDMI-Tool/
├── src/ # 源代码(人工维护区)
│ ├── hdmi_gui.py # 主程序:GUI + 命令行(标准库; pyserial/Pillow 可选)
│ ├── dlpc_rs485.py # 光机 RS485 协议与串口链路(帧/解析/译码/线程)
│ ├── selftest_rs485.py # 光机协议自检(无硬件回归)
│ ├── build_exe.py # PyInstaller 打包 → release/
│ ├── package_release.py # 组装发布 zipexe+图案+说明)
│ ├── publish_gitea.py # 上传到 Gitea Release
│ ├── hdmi_source.ps1/.bat # 源端分辨率/图案命令行工具
│ ├── gen_bw_patterns.py # 生成 assets/bw_*.png
│ ├── gen_solid_colors.py # 生成 assets/solid_*.png
│ ├── la_wave_analyzer*.py # 逻辑分析仪 CSV 波形判读
│ └── logos/ # 图标源(svg/png/ico + make_ico.py
├── assets/ # 生成的测试图案(gen_*.py 产物, 随发布包分发)
│ ├── bw_*.png ×5 # 640x360 黑白系列
│ └── solid_*.png ×10 # 800x600 纯色系列
├── release/ # 构建与发布产物(机器生成区, 不手改)
│ ├── HDMI-Tool.exe # 打包单文件版(改动跟随源码提交入库)
│ ├── pattern_slots.json # exe 运行配置(栏位预置 + 串口记忆)
│ └── *.zip # 发布包(不入库, 上传 Gitea Release
├── README.md # 本说明
└── AGENTS.md # 面向 AI/Agent 的工程档案(结构/约定/构建规程)
```
从源码运行:`python src/hdmi_gui.py`,需 Python ≥3.10,标准库即可跑 EDID/投屏;
光机控制需 pyserial`py -m pip install pyserial`),自定义图片要 JPG/BMP/WebP 需可选 Pillow。
## 9. 构建与发布
```powershell
cd src
python selftest_rs485.py # 光机协议回归(改 dlpc_rs485.py 后必跑)
python build_exe.py # 打包 → ../release/HDMI-Tool.exe(自动装 PyInstaller; 内置 pyserial
python build_exe.py --debug # 额外出一个带控制台的调试变体(--dump 可见输出)
python package_release.py 0.2.0 # 组装 release/HDMI-Tool-v0.2.0-win64.zip
python publish_gitea.py HDMI-Tool-v0.2.0-win64.zip --insecure # 上传 Gitea Release(需 GITEA_TOKEN)
```
## 10. 注意事项 / FAQ
- **光机没反应排查**:① 串口/波特率(115200 8N1)与 A/B 接线;② 先发 `M999` 验证链路;
③ 开光机回 0xFF 是 DLPC 就绪门禁——上电后等几秒;④ USB-RS485 转换器半双工,
发送时板上回包不会回环(正常);
- **exe 放只读目录会怎样**:自定义图片与 `pattern_slots.json` 写在 exe 旁边,只读目录会导致配置存不下;
- **重新打包后图标没变**:Windows 图标缓存,`ie4uinit -show` 或改个名即可;
- **多屏序号**:切分辨率前先确认显示器序号(GUI 下拉框与 `hdmi_source.bat -List` 输出一致);
- **残留进程**:修改源码重新打包前先结束残留的 HDMI-Tool 进程(`build_exe.py` 会自动 taskkill);
- **改 logo**:改 `src/logos/logo_ph_orange.svg``python src/logos/make_ico.py` → 重新打包;
- **配置互不影响**:exe 与源码脚本各自按所在目录保存 `pattern_slots.json`,两份安装互不干扰。
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

+19 -12
View File
@@ -1,9 +1,12 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
组装 HDMI-Tool 发布包: exe + 附属资产 → 一个 zip(解压即用)
组装 HDMI-Tool 发布包: exe + 附属资产 → 一个文件夹 + 一个 zip(解压即用)
================================================================
发布内容(zip 解压后根目录为 HDMI-Tool/, 与 exe 运行目录结构一致):
先把 assets/ 的图案复制进 release/(与 exe 平铺), 使 release/ 本身就是
可直接使用的完整文件夹; 再原样压成 zip —— 两者内容始终一致。
zip 解压后根目录为 HDMI-Tool/, 与 exe 运行目录结构一致:
HDMI-Tool.exe 主程序(先运行 build_exe.py 打包)
pattern_slots.json 预置自定义栏位(指向前 3 张 bw 图案)
bw_*.png 640x360 黑白测试图 ×5(自定义栏位用)
@@ -18,6 +21,7 @@
import os
import re
import shutil
import subprocess
import sys
import zipfile
@@ -54,22 +58,25 @@ def main():
# 依 pattern_slots.json 校验栏位引用的图片都在 assets/ 里
slot_files = re.findall(r'"file":\s*"([^"]+)"', open(slots, encoding='utf-8').read())
for f in slot_files:
if not os.path.exists(os.path.join(ASSETS, f)):
sys.exit(f'pattern_slots.json 引用的 {f} 不在 assets/ 中')
patterns = sorted(p for g in PATTERN_GLOBS
for p in os.listdir(ASSETS)
if re.fullmatch(g.replace('*', '.*'), p))
for f in slot_files:
if f not in patterns:
sys.exit(f'pattern_slots.json 引用的 {f} 不在 assets/ 中')
# 图案复制进 release/, 与 exe 平铺 -> release/ 即完整可用的发布文件夹
for p in patterns:
shutil.copy2(os.path.join(ASSETS, p), os.path.join(RELEASE, p))
shutil.copy2(readme, os.path.join(RELEASE, 'README.md'))
zip_name = f'HDMI-Tool-v{version}-win64.zip'
zip_path = os.path.join(RELEASE, zip_name)
with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as z:
for src, arc in ([(exe, 'HDMI-Tool.exe'), (slots, 'pattern_slots.json'),
(readme, 'README.md')]
+ [(os.path.join(ASSETS, p), p) for p in patterns]):
z.write(src, f'HDMI-Tool/{arc}')
print(f'发布包: {zip_path} ({os.path.getsize(zip_path) / 1024 / 1024:.1f} MB)')
print('内容: HDMI-Tool.exe, pattern_slots.json, README.md +', f'{len(patterns)} 张图案')
for p in ['HDMI-Tool.exe', 'pattern_slots.json', 'README.md'] + patterns:
z.write(os.path.join(RELEASE, p), f'HDMI-Tool/{p}')
print(f'发布文件夹: {RELEASE} (exe+配置+说明+{len(patterns)} 张图案, 平铺即用)')
print(f'发布包: {zip_path} ({os.path.getsize(zip_path) / 1024 / 1024:.1f} MB)')
print('下一步上传: python publish_gitea.py', zip_name)