diff --git a/.gitignore b/.gitignore index f6ff9ac..3fbb6fe 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,6 @@ __pycache__/ build/ dist/ *.spec + +# 发布 zip: 可由 src/package_release.py 重新生成, 只上传 Gitea Release +HDMI-Tool/release/*.zip diff --git a/HDMI-Tool/AGENTS.md b/HDMI-Tool/AGENTS.md new file mode 100644 index 0000000..797e15e --- /dev/null +++ b/HDMI-Tool/AGENTS.md @@ -0,0 +1,105 @@ +# AGENTS.md — HDMI-Tool 工程档案(供 AI/Agent 使用) + +> **本文档面向 AI/Agent**,是人类文档 [README.md](README.md) 的姊妹篇,两者不重叠: +> README 回答"这是什么、怎么用";本文回答"代码怎么组织、怎么构建发布、改动时哪些约束不能破坏"。 +> 任何 Agent 在修改本目录代码前应先读完本文。 + +## 1. 项目定位 + +仓库 `LT8619C_Debug_Tools` 是 LT8619C(HDMI→RGB888)→ DLPC3421 投影转接板的**调试上位机工具集**, +上游主仓库(固件)为 `hulk/LT8619C_DLPC3421_HDMI`(同 Gitea 实例 `gitea.hulk.wang`)。 +本工具工作在 HDMI **源端**(PC 显卡侧),与固件侧寄存器调试(I2C-Inject 工具)配合。 +寄存器语义判读基线(0x606E 位交换 / 0x606D 整组顺序)在上游仓库 `doc/architecture/`,不在本仓库。 + +## 2. 目录契约(重要) + +``` +HDMI-Tool/ +├── src/ 源代码区 —— 只有这里可以手改(logos/ 改图标源后重跑 make_ico.py) +├── assets/ 生成资产区 —— gen_*.py 的产物(PNG 图案),可随时再生成,改动需提交 +├── release/ 产物区 —— 机器生成,禁止手改:HDMI-Tool.exe(入库)、 +│ pattern_slots.json(exe 运行配置,入库)、*.zip(gitignore,只上传 Release) +├── README.md 人类文档 +└── AGENTS.md 本文 +``` + +划分原则:**"人写的"在 src/,"生成的"按去向分 assets/(随包分发)与 release/(直接交付)**。 +新文件必须落对区域;不要在 src/ 外新建源码,不要手改 assets/ 下的 PNG(改生成脚本再跑)。 + +### 文件角色速查 + +| 文件 | 角色 | +|---|---| +| `src/hdmi_gui.py` | 单文件主程序:GUI(tkinter) + 命令行,约 1500 行,纯标准库 | +| `src/build_exe.py` | PyInstaller 打包 → `release/HDMI-Tool.exe`(onefile/noconsole,内嵌图标) | +| `src/package_release.py` | exe+图案+说明 → `release/HDMI-Tool-v-win64.zip`(校验栏位引用完整性) | +| `src/publish_gitea.py` | 建/复用 Gitea Release 并上传 zip(tag 默认取 zip 文件名) | +| `src/hdmi_source.ps1/.bat` | 独立 PowerShell 命令行工具(自包含,与 GUI 无代码共享) | +| `src/gen_bw_patterns.py` | 生成 `assets/bw_*.png` ×5(640x360 黑白,需 Pillow) | +| `src/gen_solid_colors.py` | 生成 `assets/solid_*.png` ×10(800x600 纯色,需 Pillow) | +| `src/la_wave_analyzer*.py` | 逻辑分析仪 CSV 波形判读(2/3 通道,40MHz 像素时钟假设) | +| `src/logos/make_ico.py` | svg/png → ico(写在其自身目录) | +| `release/pattern_slots.json` | 预置栏位:西门子星/波带片/综合测试卡,area=640x360 | + +## 3. 代码不变量(改 hdmi_gui.py 必须维持) + +1. **纯标准库**:hdmi_gui.py 只准 import 标准库(ctypes/winreg/tkinter/…)。Pillow 是 + *可选*加速(仅扩展自定义图片格式 PNG/GIF 之外的 JPG/BMP/WebP),缺失时必须照常工作。 +2. **BASE_DIR 语义**:frozen(PyInstaller onefile)时 `BASE_DIR=exe 所在目录`(`sys.executable`), + 源码运行时 `=hdmi_gui.py 所在目录`(`__file__`)。所有用户数据(pattern_slots.json、 + 自定义图片)都写/读自 BASE_DIR **平铺**存放,不建子目录。 +3. **自定义栏位 `file` 字段是裸文件名**:加载时 `os.path.join(BASE_DIR, file)` 解析, + 因此 pattern_slots.json 引用的图片必须与 exe 同目录 —— package_release.py 正是按此平铺打包。 +4. **exe 自包含**:图标经 `--add-data` 内嵌(运行时从 `_MEIPASS` 读);内置五种图案 + (红/绿/蓝/棋盘格/网格线)由程序绘制,运行时**不依赖** assets/;solid/bw PNG 只服务自定义栏位。 +5. **DPI 感知**:进程启动即 `SetProcessDpiAwareness(2)`,图案窗口按物理像素定位,勿移除。 +6. **启动强制扩展拓扑**:`SetDisplayConfig(SDC_TOPOLOGY_EXTEND|SDC_APPLY)`,静默容错 + (失败不阻断启动)。动机:LT8619C 热插拔后 Windows 可能停留在"仅电脑屏幕"。 +7. **分辨率切换是临时的**:走 `CHANGEDISPLAYSETTINGS`(不写注册表),"恢复"用会话开始时快照。 +8. **EDID 来源是注册表**(`HKLM\SYSTEM\CurrentControlSet\Enum\DISPLAY\...\Device Parameters\EDID`), + 不是 WMI/DDC;exe 的 noconsole 版无 stdout,`--dump/--edid-fields` 只在源码/控制台版可用。 + +## 4. 领域语义(测试值都有含义,勿"顺手改") + +- 纯色值 55/AA/FF:55(01010101) 与 AA(10101010) 互为字节镜像对(检出位序颠倒/位交换), + FF(11111111) 全高基准;R/G/B 三行对应 RGB888 三字节组(R=D[23:16]/G=D[15:8]/B=D[7:0])。 +- 800x600@60:demo 板 shadow EDID 首选时序(solid 系列分辨率与之一致)。 +- 640x360:测试卡区域默认值(16:9 半分辨率,点对点检查用);full=铺满所选屏。 +- bw 系列图案各自目的见 `src/gen_bw_patterns.py` 顶部清单注释。 + +## 5. 构建 / 发布流水线(全部在 src/ 下执行) + +```powershell +python gen_bw_patterns.py ; python gen_solid_colors.py # 仅当改了图案生成逻辑 +python build_exe.py # → ../release/HDMI-Tool.exe(缺 PyInstaller 会自动 pip install) +python build_exe.py --debug # 附加带控制台的 HDMI-Tool-console.exe(调试 --dump 用) +python package_release.py <版本> # → ../release/HDMI-Tool-v<版本>-win64.zip +python publish_gitea.py HDMI-Tool-v<版本>-win64.zip --replace # 上传(需 GITEA_TOKEN 环境变量或 --token) +``` + +发布约定: +- tag/release 命名 `HDMI-Tool-vX.Y.Z`,zip 命名 `HDMI-Tool-vX.Y.Z-win64.zip`(publish_gitea.py + 依赖此命名从文件名推 tag); +- **exe 与 pattern_slots.json 改动跟随源码提交入库;zip 永不入库**(.gitignore 已挡); +- publish_gitea.py 幂等:release 已存在则复用,同名资产默认跳过,`--replace` 删旧重传; +- Gitea 实例 `gitea.hulk.wang`,API 走 https(443),git 走 ssh(1234)——从 origin 远程自动解析; + 自签证书报 SSL 错时加 `--insecure`。 + +## 6. 已知坑(历史踩过,勿重蹈) + +- onefile 运行时 `__file__` 指向临时解压目录 `_MEIPASS`,定位 exe 必须用 `sys.executable`(§3.2); +- 重新打包前旧 exe 可能被残留进程占用 → build_exe.py 会 taskkill 同名进程,手改打包脚本时保留; +- 打包后本机图标缓存不刷新:`ie4uinit -show`,不是产物问题; +- 改图标流程:改 svg → `make_ico.py` 生成 ico → 重新打包(ico 同时是文件图标与运行时窗口图标); +- GUI 与 hdmi_source.ps1 各自独立实现显示器枚举,行为可能微差(GUI 与 `-List` 序号一致, + 但与"设置"面板序号无保证);多屏操作前先确认序号; +- `pattern_slots.json` 兼容两种格式:新 `{area, slots[]}`,旧纯数组(无 area)——load_slots 已兼容, + 但写出只应写新格式。 + +## 7. 验证清单(改完代码后) + +1. `python hdmi_gui.py --seconds 5` —— GUI 冒烟(自动开关); +2. `python hdmi_gui.py --test-pattern Checker --area 800x600 --seconds 3` —— 投图链路; +3. `python hdmi_gui.py --dump` —— EDID 解算不抛异常; +4. `python build_exe.py` → 双击 release/HDMI-Tool.exe 确认图标/启动/投图; +5. `python package_release.py <版本>` —— 不报"引用图片缺失"即栏位完整性 OK。 diff --git a/HDMI-Tool/README.md b/HDMI-Tool/README.md index 6d86b64..8871579 100644 --- a/HDMI-Tool/README.md +++ b/HDMI-Tool/README.md @@ -1,7 +1,13 @@ -# HDMI-Tool — HDMI 源端测试工具(LT8619C/DLPC3421 转接板调试) +# HDMI-Tool — HDMI 源端调试工具 -> 更新:2026-09-04。单文件 Python GUI(`hdmi_gui.py`),另附命令行分辨率/图案工具 -> `hdmi_source.bat`。已打包版直接双击 `HDMI-Tool.exe`,无需安装 Python。 +> LT8619C(HDMI→RGB888)→ DLPC3421 投影转接板调试配套的 PC 端工具: +> 在 HDMI **源端**(电脑显卡输出)完成 EDID 查看、分辨率切换、纯色/测试图案全屏投屏。 +> +> - **只想用工具**:到 [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)。 +> - **要改代码/重新打包**:见 §7、§8。 +> +> 更新:2026-09-10 --- @@ -9,45 +15,94 @@ | 功能 | 说明 | |---|---| -| EDID 查看 | 枚举各显示输出口,从注册表读取 EDID 原始数据,按 EDID 1.4/CEA-861 解算:厂商/型号/尺寸/支持模式(DTD/已建立/标准/CEA-VIC)/首选时序完整参数/校验和 | -| EDID 逐字节解析 | "EDID 原始数据"框右上"逐字节解析"按钮:按偏移逐字段展开基础块(同步头/厂商/产品/序列/版本/输入类型/尺寸/Gamma/特性位/色度/已建立+标准时序/4×描述符/扩展块数/校验和)与 CTA-861 扩展块(tag/版本/DTD 偏移/标志位/数据块链/扩展 DTD 区/校验和),每字段附原始字节 hex,自动换行;命令行等价 `--edid-fields` | -| 分辨率切换 | 第二行"分辨率"下拉列出显卡按该屏 EDID 提供的全部模式;**应用**=临时切换(不写注册表,重启还原),**恢复**=回到本次会话切改前的模式 | -| 测试图案 | 全屏投到所选显示器,测试卡区域可选 `640x360 / 800x600 / full 铺满`;内置 纯红/纯绿/纯蓝/棋盘格/网格线 + R/G/B 三行纯色值按钮(55/AA/FF,与 dist 下 solid_*.png 对应)+ 3 个自定义图片栏位 | -| 命令行模式 | 自动化测试脚本可直接调用(见 §4) | +| 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 个自定义图片栏位 | +| 显示拓扑控制 | 启动时强制扩展显示拓扑(复制/仅第二屏等模式下保证 demo 板屏幕独立可控) | +| 命令行模式 | 全部功能可脚本化调用,供自动化测试(见 §5、§6) | +| 波形分析脚本 | 逻辑分析仪 CSV 波形按占空比签名分段,输出行结构/占空比/消隐明细(见 §6.3) | -## 2. 快速上手(GUI) +## 2. 下载与安装 -1. 双击 `HDMI-Tool.exe`(或 `python hdmi_gui.py`,需 Python ≥3.10 + pyserial 不需要,纯标准库;自定义图片 JPG/BMP 需 Pillow) -2. 顶部显示器下拉选择目标屏(多屏时注意别选成主屏),"刷新"重新枚举 -3. **切分辨率**:第二行选模式 → 点"应用";测完点"恢复" -4. **投测试图**: - - 先在图案按钮行右侧选**测试卡区域**(640x360 / 800x600 / full) - - 点图案按钮即全屏投放;按 **ESC** 退出 - - R/G/B 三行纯色按钮(55/AA/FF)用于位交换类测试:55 与 AA 互为字节镜像对,FF 为全高基准 - - 自定义图片:点"⚙"配置名称与图片文件(图片会复制到本目录),点栏位名投放 -5. 区域与自定义栏位配置自动保存在本目录 `pattern_slots.json` +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 解压),属正常现象。 -## 3. 典型场景(LT8619C demo 板测试) +包内包含: + +``` +HDMI-Tool/ +├── HDMI-Tool.exe 主程序(单文件,自包含) +├── pattern_slots.json 预置自定义栏位(指向下面的 bw 图案) +├── bw_*.png ×5 640x360 黑白测试图 +├── solid_*.png ×10 800x600 纯色测试图 +└── README.md 本说明 +``` + +## 3. GUI 使用 + +主窗口自上而下三块区域: + +``` +┌ 显示器: [GDI ... ▼] [刷新] [拓扑: 扩展 ▼] ┐ +├ 分辨率: [800x600@60 ▼] [应用] [恢复] ┐ +├ ── EDID 原始数据 ──────────── [逐字节解析] ─┤ +│ (hex 文本框) │ +├ 测试卡区域: (○640x360 ○800x600 ○full) ┤ +├ [纯红][纯绿][纯蓝][棋盘格][网格线] │ ← 图案按钮 +├ R: [55][AA][FF] G: [55][AA][FF] B: [55][AA][FF] │ ← 纯色值按钮 +├ [自定义1][自定义2][自定义3] [⚙] │ ← 自定义栏位 +└─────────────────────────────────────────────┘ +``` + +1. 顶部显示器下拉选择目标屏(多屏时注意别选成主屏),"刷新"重新枚举; +2. **切分辨率**:第二行选模式 → "应用";测完点"恢复"; +3. **投测试图**: + - 先在图案按钮行上方选**测试卡区域**(640x360 / 800x600 / full); + - 点任意图案按钮即全屏投放;按 **ESC** 或点击退出; + - R/G/B 三行纯色按钮(55/AA/FF)用于 RGB 位交换类测试:55(01010101) 与 + AA(10101010) 互为字节镜像对,FF(11111111) 为全高基准; + - 自定义图片:点"⚙"配置名称与图片文件(图片会自动复制到 exe 旁边),点栏位名投放; +4. 测试卡区域与自定义栏位配置自动保存在 exe 旁的 `pattern_slots.json`。 + +## 4. 典型测试场景 ``` 1. HDMI 接 demo 板 → 启动 HDMI-Tool → 刷新 → 选中 demo 板显示器 2. 分辨率切 800x600@60(demo 板 shadow EDID 首选)→ 测试卡区域选 800x600 -3. 点 R/G/B 行的纯色按钮投图 → 配合逻辑分析仪/万用表测 RGB 输出 -4. 测完"恢复"分辨率 +3. 点 R/G/B 行纯色按钮投图 → 配合逻辑分析仪/万用表测转接板 RGB888 输出 + - 纯红/绿/蓝:验证通道映射(R=D[23:16], G=D[15:8], B=D[7:0]) + - 55/AA 对:验证位交换(0x606E)——图像应为 50% 灰阶占空比 +4. 换 bw 图案(西门子星/波带片)看极限分辨率与混叠 +5. 测完"恢复"分辨率 ``` -## 4. 命令行模式 +寄存器语义(0x606E 位交换 / 0x606D 整组顺序)的判读基线见上游主仓库 +[LT8619C_DLPC3421_HDMI](https://gitea.hulk.wang/hulk/LT8619C_DLPC3421_HDMI) 的 +`doc/architecture/`。 + +## 5. 命令行模式(hdmi_gui.py) + +exe 版不支持 `--dump/--edid-fields`(noconsole 无控制台输出),请用源码方式跑: ``` -python hdmi_gui.py --dump 控制台输出 EDID 解算(exe 版不支持) -python hdmi_gui.py --edid-fields 控制台输出 EDID 逐字节字段解析表(exe 版不支持) +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 - --area {640x360,800x600,full} --monitor N --seconds S +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 ``` -**hdmi_source.bat**(PowerShell 源端工具,功能与 GUI 互补): +## 6. 辅助脚本(src/ 下) + +### 6.1 hdmi_source.bat / .ps1 — 源端命令行工具 + +与 GUI 互补,适合脚本串联: ``` tools\hdmi_source.bat -List 枚举显示器与支持分辨率(验证 EDID) @@ -56,23 +111,71 @@ tools\hdmi_source.bat -Mode 800x600 -Pattern -Restore 切模式→出图案→退出后自动恢复 ``` -## 5. 目录内容 +### 6.2 gen_bw_patterns.py / gen_solid_colors.py — 测试图生成 -| 文件 | 用途 | -|---|---| -| `HDMI-Tool.exe` | 打包好的单文件版(--noconsole;`--dump` 请用源码方式跑) | -| `hdmi_gui.py` | 主程序源码 | -| `build_exe.py` | 打包脚本(`python build_exe.py`,自动装 PyInstaller;`--debug` 额外出带控制台版) | -| `hdmi_source.ps1 / .bat` | 源端分辨率/图案命令行工具 | -| `gen_bw_patterns.py` / `gen_solid_colors.py` | 测试图生成脚本(bw 黑白系列 640x360 / solid 纯色系列 800x600) | -| `solid_*.png` | 9 张 800x600 纯色图(R/G/B 各 55/AA/FF,供自定义栏位或脚本使用) | -| `bw_*.png` | 5 张 640x360 黑白测试图(西门子星/频率条纹/渐变棋盘/波带/综合卡) | -| `logos/` | 图标源文件与 `make_ico.py`(改 logo 后重跑再打包) | -| `pattern_slots.json` | 自定义栏位 + 测试卡区域配置(运行后自动生成/更新) | +需 Pillow(`py -m pip install pillow`)。重新生成 `assets/` 下全部图案: -## 6. 注意事项 +- `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 纯色图 ×10(R/G/B 各 55/AA/FF + 三原色), + 与 GUI 内置纯色按钮同源,供自定义栏位或其他工具加载。 -- 测试卡区域与自定义栏位配置按 exe/脚本所在目录各自保存,两份安装互不影响 -- 重打包后 Windows 图标缓存可能仍显示旧图标:`ie4uinit -show` 或改名即可 -- 多屏环境切分辨率前先确认显示器序号(下拉框中名称与 `-List` 输出一致) -- 修改源码后重新打包前,先结束残留的 HDMI-Tool 进程(build_exe.py 会自动处理) +### 6.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 # 2 通道(默认判读 B 口 D0/D1) +python la_wave_analyzer3.py # 3 通道(RGB 三组同拍, ch8/9/10) +``` + +## 7. 目录结构与从源码运行 + +``` +HDMI-Tool/ +├── src/ # 源代码(人工维护区) +│ ├── hdmi_gui.py # 主程序:GUI + 命令行, 纯标准库 +│ ├── build_exe.py # PyInstaller 打包 → release/ +│ ├── package_release.py # 组装发布 zip(exe+图案+说明) +│ ├── 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 运行配置(默认栏位预置 bw 图案) +│ └── *.zip # 发布包(不入库, 上传 Gitea Release) +├── README.md # 本说明 +└── AGENTS.md # 面向 AI/Agent 的工程档案(结构/约定/构建规程) +``` + +从源码运行:`python src/hdmi_gui.py`,需 Python ≥3.10,纯标准库无第三方依赖 +(自定义图片要 JPG/BMP/WebP 时需可选 Pillow)。 + +## 8. 构建与发布 + +```powershell +cd src +python build_exe.py # 打包 → ../release/HDMI-Tool.exe(自动装 PyInstaller) +python build_exe.py --debug # 额外出一个带控制台的调试变体(--dump 可用) +python package_release.py 0.1.0 # 组装 release/HDMI-Tool-v0.1.0-win64.zip +python publish_gitea.py HDMI-Tool-v0.1.0-win64.zip # 上传 Gitea Release(需 GITEA_TOKEN) +``` + +## 9. 注意事项 / FAQ + +- **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`,两份安装互不干扰。 diff --git a/HDMI-Tool/bw_checker_sweep.png b/HDMI-Tool/assets/bw_checker_sweep.png similarity index 100% rename from HDMI-Tool/bw_checker_sweep.png rename to HDMI-Tool/assets/bw_checker_sweep.png diff --git a/HDMI-Tool/bw_star.png b/HDMI-Tool/assets/bw_star.png similarity index 100% rename from HDMI-Tool/bw_star.png rename to HDMI-Tool/assets/bw_star.png diff --git a/HDMI-Tool/bw_sweep.png b/HDMI-Tool/assets/bw_sweep.png similarity index 100% rename from HDMI-Tool/bw_sweep.png rename to HDMI-Tool/assets/bw_sweep.png diff --git a/HDMI-Tool/bw_testcard.png b/HDMI-Tool/assets/bw_testcard.png similarity index 100% rename from HDMI-Tool/bw_testcard.png rename to HDMI-Tool/assets/bw_testcard.png diff --git a/HDMI-Tool/bw_zoneplate.png b/HDMI-Tool/assets/bw_zoneplate.png similarity index 100% rename from HDMI-Tool/bw_zoneplate.png rename to HDMI-Tool/assets/bw_zoneplate.png diff --git a/HDMI-Tool/solid_000055.png b/HDMI-Tool/assets/solid_000055.png similarity index 100% rename from HDMI-Tool/solid_000055.png rename to HDMI-Tool/assets/solid_000055.png diff --git a/HDMI-Tool/solid_0000AA.png b/HDMI-Tool/assets/solid_0000AA.png similarity index 100% rename from HDMI-Tool/solid_0000AA.png rename to HDMI-Tool/assets/solid_0000AA.png diff --git a/HDMI-Tool/solid_0000FF.png b/HDMI-Tool/assets/solid_0000FF.png similarity index 100% rename from HDMI-Tool/solid_0000FF.png rename to HDMI-Tool/assets/solid_0000FF.png diff --git a/HDMI-Tool/solid_005500.png b/HDMI-Tool/assets/solid_005500.png similarity index 100% rename from HDMI-Tool/solid_005500.png rename to HDMI-Tool/assets/solid_005500.png diff --git a/HDMI-Tool/solid_00AA00.png b/HDMI-Tool/assets/solid_00AA00.png similarity index 100% rename from HDMI-Tool/solid_00AA00.png rename to HDMI-Tool/assets/solid_00AA00.png diff --git a/HDMI-Tool/solid_00FF00.png b/HDMI-Tool/assets/solid_00FF00.png similarity index 100% rename from HDMI-Tool/solid_00FF00.png rename to HDMI-Tool/assets/solid_00FF00.png diff --git a/HDMI-Tool/solid_550000.png b/HDMI-Tool/assets/solid_550000.png similarity index 100% rename from HDMI-Tool/solid_550000.png rename to HDMI-Tool/assets/solid_550000.png diff --git a/HDMI-Tool/solid_AA0000.png b/HDMI-Tool/assets/solid_AA0000.png similarity index 100% rename from HDMI-Tool/solid_AA0000.png rename to HDMI-Tool/assets/solid_AA0000.png diff --git a/HDMI-Tool/solid_F00000.png b/HDMI-Tool/assets/solid_F00000.png similarity index 100% rename from HDMI-Tool/solid_F00000.png rename to HDMI-Tool/assets/solid_F00000.png diff --git a/HDMI-Tool/solid_FF0000.png b/HDMI-Tool/assets/solid_FF0000.png similarity index 100% rename from HDMI-Tool/solid_FF0000.png rename to HDMI-Tool/assets/solid_FF0000.png diff --git a/HDMI-Tool/HDMI-Tool.exe b/HDMI-Tool/release/HDMI-Tool.exe similarity index 97% rename from HDMI-Tool/HDMI-Tool.exe rename to HDMI-Tool/release/HDMI-Tool.exe index 426e70f..d22e8aa 100644 Binary files a/HDMI-Tool/HDMI-Tool.exe and b/HDMI-Tool/release/HDMI-Tool.exe differ diff --git a/HDMI-Tool/pattern_slots.json b/HDMI-Tool/release/pattern_slots.json similarity index 100% rename from HDMI-Tool/pattern_slots.json rename to HDMI-Tool/release/pattern_slots.json diff --git a/HDMI-Tool/build_exe.py b/HDMI-Tool/src/build_exe.py similarity index 84% rename from HDMI-Tool/build_exe.py rename to HDMI-Tool/src/build_exe.py index b729cbf..a28331d 100644 --- a/HDMI-Tool/build_exe.py +++ b/HDMI-Tool/src/build_exe.py @@ -10,7 +10,7 @@ python build_exe.py 自动安装 PyInstaller(如缺)并打包 python build_exe.py --debug 额外打一个带控制台的变体(--dump 可用) -产物: tools/dist/HDMI-Tool.exe(及 --debug 时的 HDMI-Tool-console.exe) +产物: release/HDMI-Tool.exe(及 --debug 时的 HDMI-Tool-console.exe) 首次运行 onefile exe 会先解压到临时目录, 启动比脚本慢一两秒, 属正常现象。 """ @@ -18,8 +18,10 @@ import os import subprocess import sys -BASE = os.path.dirname(os.path.abspath(__file__)) -ICO = os.path.join(BASE, 'logos', 'logo_ph_orange.ico') +# 本脚本位于 src/, 打包产物输出到工具根的 release/(与 pattern_slots.json 同目录) +SRC = os.path.dirname(os.path.abspath(__file__)) +BASE = os.path.dirname(SRC) +ICO = os.path.join(SRC, 'logos', 'logo_ph_orange.ico') def ensure_pyinstaller(): @@ -50,13 +52,13 @@ def remove_stale(exe_path): def build(name, extra_args): - dist = os.path.join(BASE, 'dist') - work = os.path.join(BASE, 'build') + dist = os.path.join(BASE, 'release') + work = os.path.join(SRC, 'build') remove_stale(os.path.join(dist, name + '.exe')) cmd = [sys.executable, '-m', 'PyInstaller', '--onefile', '--clean', '--name', name, '--distpath', dist, '--workpath', work, '--specpath', work, - ] + extra_args + [os.path.join(BASE, 'hdmi_gui.py')] + ] + extra_args + [os.path.join(SRC, 'hdmi_gui.py')] if os.path.exists(ICO): # exe 文件图标 + 运行时窗口图标资源(从 _MEIPASS 读取) cmd += ['--icon', ICO, '--add-data', f'{ICO};.'] diff --git a/HDMI-Tool/gen_bw_patterns.py b/HDMI-Tool/src/gen_bw_patterns.py similarity index 88% rename from HDMI-Tool/gen_bw_patterns.py rename to HDMI-Tool/src/gen_bw_patterns.py index 0669689..6556a5d 100644 --- a/HDMI-Tool/gen_bw_patterns.py +++ b/HDMI-Tool/src/gen_bw_patterns.py @@ -3,8 +3,8 @@ """ 生成 640x360 黑白测试图, 供 HDMI 源端工具的自定义栏位使用 ================================================================ -输出到 exe 目录(tools/dist/), 并把前 3 张写入 pattern_slots.json, -启动 hdmi_gui 后自定义栏位即可直接点用; 其余两张可用 "⚙" 换上。 +输出到 ../assets/(仓库资产目录, 发布打包时随 zip 分发); 预置栏位配置 +release/pattern_slots.json 指向前 3 张, 其余两张可用 "⚙" 换上。 图案清单(均为 640x360 黑白, 1:1 投屏无缩放): bw_star.png 西门子星: 60 楔放射星, 看极限分辨率与中心偏移 @@ -16,15 +16,14 @@ 用法: python gen_bw_patterns.py """ -import json import math import os from PIL import Image, ImageDraw W, H = 640, 360 -BASE = os.path.dirname(os.path.abspath(__file__)) -OUT = os.path.join(BASE, 'dist') +SRC = os.path.dirname(os.path.abspath(__file__)) +OUT = os.path.join(os.path.dirname(SRC), 'assets') def save(img, name): @@ -164,22 +163,9 @@ def gen_testcard(): save(img, 'bw_testcard.png') -def write_slots(): - slots = [ - {'name': '西门子星', 'file': 'bw_star.png'}, - {'name': '频率条纹', 'file': 'bw_sweep.png'}, - {'name': '渐变棋盘', 'file': 'bw_checker_sweep.png'}, - ] - path = os.path.join(OUT, 'pattern_slots.json') - with open(path, 'w', encoding='utf-8') as f: - json.dump(slots, f, ensure_ascii=False, indent=2) - print(f'写出 {path}(前 3 张已配入自定义栏位, 其余两张可用 "⚙" 换上)') - - if __name__ == '__main__': gen_star() gen_sweep() gen_checker_sweep() gen_zoneplate() gen_testcard() - write_slots() diff --git a/HDMI-Tool/gen_solid_colors.py b/HDMI-Tool/src/gen_solid_colors.py similarity index 84% rename from HDMI-Tool/gen_solid_colors.py rename to HDMI-Tool/src/gen_solid_colors.py index b6cc4d8..960b697 100644 --- a/HDMI-Tool/gen_solid_colors.py +++ b/HDMI-Tool/src/gen_solid_colors.py @@ -4,8 +4,8 @@ 生成 800x600 纯色测试图, 供 RGB 位交换(0x606E)验证使用 ================================================================ 配合 demo 板当前 EDID 首选时序 800x600@60, 全屏纯色输出。 -用法: python gen_solid_colors.py → 输出到 tools/dist/solid_*.png -在 HDMI-Tool 中通过自定义栏位(⚙ 换图)加载。 +用法: python gen_solid_colors.py → 输出到 ../assets/solid_*.png +在 HDMI-Tool 中通过自定义栏位(⚙ 换图)加载, 发布包随附。 """ import os @@ -13,8 +13,8 @@ import os from PIL import Image W, H = 800, 600 # 与 demo 板 EDID 首选时序一致, 全屏铺满 -BASE = os.path.dirname(os.path.abspath(__file__)) -OUT = os.path.join(BASE, 'dist') +SRC = os.path.dirname(os.path.abspath(__file__)) +OUT = os.path.join(os.path.dirname(SRC), 'assets') # (文件名后缀, RGB 值, 用途) COLORS = [ diff --git a/HDMI-Tool/hdmi_gui.py b/HDMI-Tool/src/hdmi_gui.py similarity index 99% rename from HDMI-Tool/hdmi_gui.py rename to HDMI-Tool/src/hdmi_gui.py index 3db454d..6204229 100644 --- a/HDMI-Tool/hdmi_gui.py +++ b/HDMI-Tool/src/hdmi_gui.py @@ -882,7 +882,7 @@ AREA_OPTIONS = ('640x360', '800x600', 'full') DEFAULT_AREA = AREA_OPTIONS[0] PATTERN_NAMES = {'Red': '纯红', 'Green': '纯绿', 'Blue': '纯蓝', 'Checker': '黑白棋盘格', 'Grid': '网格线'} -# 纯色测试值(位交换测试用, 与 tools/dist/solid_*.png 对应): +# 纯色测试值(位交换测试用, 与 assets/solid_*.png 对应): # 每通道 55/AA 互为镜像对(检测对), FF 全高基准 SOLID_ROWS = ( ('R', (('55', '#550000'), ('AA', '#AA0000'), ('FF', '#FF0000'))), diff --git a/HDMI-Tool/hdmi_source.bat b/HDMI-Tool/src/hdmi_source.bat similarity index 100% rename from HDMI-Tool/hdmi_source.bat rename to HDMI-Tool/src/hdmi_source.bat diff --git a/HDMI-Tool/hdmi_source.ps1 b/HDMI-Tool/src/hdmi_source.ps1 similarity index 100% rename from HDMI-Tool/hdmi_source.ps1 rename to HDMI-Tool/src/hdmi_source.ps1 diff --git a/HDMI-Tool/la_wave_analyzer.py b/HDMI-Tool/src/la_wave_analyzer.py similarity index 100% rename from HDMI-Tool/la_wave_analyzer.py rename to HDMI-Tool/src/la_wave_analyzer.py diff --git a/HDMI-Tool/la_wave_analyzer3.py b/HDMI-Tool/src/la_wave_analyzer3.py similarity index 100% rename from HDMI-Tool/la_wave_analyzer3.py rename to HDMI-Tool/src/la_wave_analyzer3.py diff --git a/HDMI-Tool/logos/logo_ph_orange.ico b/HDMI-Tool/src/logos/logo_ph_orange.ico similarity index 100% rename from HDMI-Tool/logos/logo_ph_orange.ico rename to HDMI-Tool/src/logos/logo_ph_orange.ico diff --git a/HDMI-Tool/logos/logo_ph_orange.png b/HDMI-Tool/src/logos/logo_ph_orange.png similarity index 100% rename from HDMI-Tool/logos/logo_ph_orange.png rename to HDMI-Tool/src/logos/logo_ph_orange.png diff --git a/HDMI-Tool/logos/logo_ph_orange.svg b/HDMI-Tool/src/logos/logo_ph_orange.svg similarity index 100% rename from HDMI-Tool/logos/logo_ph_orange.svg rename to HDMI-Tool/src/logos/logo_ph_orange.svg diff --git a/HDMI-Tool/logos/make_ico.py b/HDMI-Tool/src/logos/make_ico.py similarity index 100% rename from HDMI-Tool/logos/make_ico.py rename to HDMI-Tool/src/logos/make_ico.py diff --git a/HDMI-Tool/src/package_release.py b/HDMI-Tool/src/package_release.py new file mode 100644 index 0000000..abeaf0e --- /dev/null +++ b/HDMI-Tool/src/package_release.py @@ -0,0 +1,77 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +组装 HDMI-Tool 发布包: exe + 附属资产 → 一个 zip(解压即用) +================================================================ +发布内容(zip 解压后根目录为 HDMI-Tool/, 与 exe 运行目录结构一致): + HDMI-Tool.exe 主程序(先运行 build_exe.py 打包) + pattern_slots.json 预置自定义栏位(指向前 3 张 bw 图案) + bw_*.png 640x360 黑白测试图 ×5(自定义栏位用) + solid_*.png 800x600 纯色测试图 ×10(RGB 位交换测试用) + README.md 使用说明 + +用法: + python package_release.py 版本号自动取最近 git tag(HDMI-Tool-v*) + python package_release.py 0.1.0 手动指定版本号 +产物: release/HDMI-Tool-v<版本>-win64.zip(zip 不入库, 上传到 Gitea Release) +""" + +import os +import re +import subprocess +import sys +import zipfile + +SRC = os.path.dirname(os.path.abspath(__file__)) +BASE = os.path.dirname(SRC) +RELEASE = os.path.join(BASE, 'release') +ASSETS = os.path.join(BASE, 'assets') + +PATTERN_GLOBS = ('bw_*.png', 'solid_*.png') + + +def detect_version(): + """从最近的 HDMI-Tool-v* tag 推断版本号, 无 tag 则报错提示手动指定""" + try: + out = subprocess.check_output( + ['git', 'describe', '--tags', '--abbrev=0', '--match', 'HDMI-Tool-v*'], + cwd=BASE, stderr=subprocess.DEVNULL) + return out.decode().strip().removeprefix('HDMI-Tool-v') + except Exception: + sys.exit('未能从 git tag 推断版本号, 请手动指定: python package_release.py <版本号>') + + +def main(): + version = (sys.argv[1] if len(sys.argv) > 1 else detect_version()).lstrip('v') + exe = os.path.join(RELEASE, 'HDMI-Tool.exe') + slots = os.path.join(RELEASE, 'pattern_slots.json') + readme = os.path.join(BASE, 'README.md') + if not os.path.exists(exe): + sys.exit('缺少 release/HDMI-Tool.exe, 先运行: python build_exe.py') + for must in (slots, readme): + if not os.path.exists(must): + sys.exit(f'缺少 {must}') + + # 依 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)) + 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)} 张图案') + print('下一步上传: python publish_gitea.py', zip_name) + + +if __name__ == '__main__': + main() diff --git a/HDMI-Tool/src/publish_gitea.py b/HDMI-Tool/src/publish_gitea.py new file mode 100644 index 0000000..d14eb55 --- /dev/null +++ b/HDMI-Tool/src/publish_gitea.py @@ -0,0 +1,143 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +创建/更新 Gitea Release 并上传发布 zip(纯标准库, 无需安装依赖) +================================================================ +用法: + python publish_gitea.py HDMI-Tool-v0.1.0-win64.zip + python publish_gitea.py --notes-file release/notes-v0.1.0.md + python publish_gitea.py --tag HDMI-Tool-v0.1.0 --replace + +行为: + - tag 与版本默认取 zip 文件名(HDMI-Tool-vX.Y.Z-win64.zip → HDMI-Tool-vX.Y.Z) + - release 不存在则创建(tag 不存在时 Gitea 在默认分支头自动建 tag); + 已存在则复用, 仅上传/更新资产 + - release 上已有同名资产时: 默认跳过, --replace 删旧再传 + - API token: --token 参数或环境变量 GITEA_TOKEN(需该仓库写权限) +""" + +import argparse +import json +import os +import re +import ssl +import subprocess +import sys +import urllib.error +import urllib.request +import uuid + +DEFAULT_NOTES = """## HDMI-Tool {version} + +HDMI 源端调试工具(LT8619C → DLPC3421 转接板配套): EDID 查看/逐字节解析、 +分辨率切换、纯色与测试图案全屏投屏。 + +### 安装 +下载 `HDMI-Tool-v{version}-win64.zip`, 解压后双击 `HDMI-Tool.exe`(Windows 10/11 x64, 免安装)。 + +### 包内容 +- `HDMI-Tool.exe` — 主程序 +- `pattern_slots.json` — 预置自定义栏位(指向 bw_*.png) +- `bw_*.png` ×5 / `solid_*.png` ×10 — 测试图案 +- `README.md` — 完整使用说明 +""" + + +def git_remote_info(): + """从 origin 远程地址解析 API base 与 owner/repo(ssh 端口不影响 https API)""" + url = subprocess.check_output(['git', 'remote', 'get-url', 'origin']).decode().strip() + m = re.match(r'(?:ssh://git@|git@)([\w.-]+)(?::\d+)?[:/]([\w.-]+)/([\w.-]+?)(?:\.git)?$', url) + if not m: + sys.exit(f'无法从 origin 解析 Gitea 地址: {url}') + host, owner, repo = m.groups() + return f'https://{host}', owner, repo + + +class Api: + def __init__(self, base, owner, repo, token, insecure): + self.api = f'{base}/api/v1/repos/{owner}/{repo}' + self.headers = {'Authorization': f'token {token}'} + self.ctx = ssl._create_unverified_context() if insecure else None + + def request(self, method, path, data=None, headers=None): + req = urllib.request.Request(self.api + path, data=data, method=method, + headers={**self.headers, **(headers or {})}) + try: + with urllib.request.urlopen(req, context=self.ctx) as resp: + body = resp.read() + return resp.status, json.loads(body) if body else None + except urllib.error.HTTPError as e: + return e.code, json.loads(e.read() or b'{}') + + def get_json(self, path): + status, data = self.request('GET', path) + return data if status == 200 else None + + def upload_asset(self, release_id, filepath, replace): + name = os.path.basename(filepath) + status, rel = self.request('GET', f'/releases/{release_id}') + if status != 200: + sys.exit(f'读取 release {release_id} 失败: {status} {rel}') + for asset in (rel.get('assets') or {}).get('items', []): + if asset['name'] == name: + if not replace: + print(f'资产 {name} 已存在, 跳过(覆盖请加 --replace)') + return + self.request('DELETE', f"/releases/{release_id}/assets/{asset['id']}") + print(f'已删除旧资产 {name}') + boundary = uuid.uuid4().hex + payload = open(filepath, 'rb').read() + body = (f'--{boundary}\r\n' + f'Content-Disposition: form-data; name="attachment"; filename="{name}"\r\n' + f'Content-Type: application/octet-stream\r\n\r\n').encode() + payload + \ + f'\r\n--{boundary}--\r\n'.encode() + status, data = self.request( + 'POST', f'/releases/{release_id}/assets?name={name}', body, + {'Content-Type': f'multipart/form-data; boundary={boundary}'}) + if status != 201: + sys.exit(f'上传资产失败: {status} {data}') + print(f'已上传资产: {name} ({len(payload) / 1024 / 1024:.1f} MB)') + + +def main(): + ap = argparse.ArgumentParser() + ap.add_argument('zipfile_path', help='发布 zip(release/ 下)') + ap.add_argument('--tag', help='release 标签, 默认取 zip 文件名去掉 -win64.zip') + ap.add_argument('--notes-file', help='release 说明 markdown 文件, 默认用内置模板') + ap.add_argument('--token', default=os.environ.get('GITEA_TOKEN'), help='API token') + ap.add_argument('--replace', action='store_true', help='覆盖已存在的同名资产') + ap.add_argument('--insecure', action='store_true', help='跳过 TLS 证书校验(自签证书)') + args = ap.parse_args() + + if not os.path.exists(args.zipfile_path): + sys.exit(f'找不到 {args.zipfile_path}') + if not args.token: + sys.exit('缺少 API token: 传 --token 或设环境变量 GITEA_TOKEN') + + tag = args.tag or re.sub(r'-win64\.zip$', '', os.path.basename(args.zipfile_path)) + version = tag.removeprefix('HDMI-Tool-v') + notes = open(args.notes_file, encoding='utf-8').read() if args.notes_file \ + else DEFAULT_NOTES.format(version=version) + + base, owner, repo = git_remote_info() + api = Api(base, owner, repo, args.token, args.insecure) + print(f'目标: {base}/{owner}/{repo} tag={tag}') + + rel = api.get_json(f'/releases/tags/{tag}') + if rel: + print(f'release 已存在(id={rel["id"]}), 复用') + else: + status, rel = api.request('POST', '/releases', json.dumps({ + 'tag_name': tag, + 'name': f'HDMI-Tool v{version}', + 'body': notes, + }).encode(), {'Content-Type': 'application/json'}) + if status != 201: + sys.exit(f'创建 release 失败: {status} {rel}') + print(f'release 已创建(id={rel["id"]})') + api.upload_asset(rel['id'], args.zipfile_path, args.replace) + print(f'完成: {rel.get("html_url", "") or f"{base}/{owner}/{repo}/releases/tag/{tag}"}') + + +if __name__ == '__main__': + main() diff --git a/README.md b/README.md index c436b3c..6df6aea 100644 --- a/README.md +++ b/README.md @@ -5,31 +5,48 @@ LT8619C(HDMI→RGB888)→ DLPC3421 投影转接板调试配套上位机工 ## 工具一览 -| 工具 | 用途 | 快速开始 | +| 工具 | 用途 | 获取 | |---|---|---| -| [HDMI-Tool](HDMI-Tool/) | HDMI 源端:EDID 查看/解算、分辨率切换、纯色与测试图案全屏投屏(R/G/B 纯色值按钮用于位交换类测试) | 双击 `HDMI-Tool.exe`;详见 [HDMI-Tool/README.md](HDMI-Tool/README.md) | +| [HDMI-Tool](HDMI-Tool/) | HDMI 源端:EDID 查看/逐字节解析、分辨率切换、纯色与测试图案全屏投屏 | 到 [Releases](https://gitea.hulk.wang/hulk/LT8619C_Debug_Tools/releases) 下 `HDMI-Tool-vX.Y.Z-win64.zip`,解压即用;详见 [HDMI-Tool/README.md](HDMI-Tool/README.md) | | [I2C-Inject](I2C-Inject/) | LT8619C 寄存器在线读写:RP2040 注入器在 demo 板 I2C 总线上择机注入(避让主控轮询,写后自动读回校验) | 需配合 RP2040 固件 `rp2040_inject.py`;详见 [I2C-Inject/README.md](I2C-Inject/README.md) | ## 环境要求 -- 打包版 exe 开箱即用(Windows 10/11 x64,无需安装 Python) -- 源码运行:Python ≥3.10;HDMI-Tool 为纯标准库(自定义图片 JPG/BMP 需 Pillow); +- 发布版 zip 开箱即用(Windows 10/11 x64,无需安装 Python) +- 源码运行:Python ≥3.10;HDMI-Tool 为纯标准库(自定义图片 JPG/BMP 需可选 Pillow); I2C-Inject 需 `py -m pip install pyserial` -- 重新打包:各工具目录内 `python build_exe.py`(自动安装 PyInstaller) +- 重新打包:`HDMI-Tool/src/build_exe.py`(自动安装 PyInstaller);I2C-Inject 目录内 `python build_exe.py` ## 目录结构 +每个工具内统一三区:**src/ 源代码(人写)** → **assets/ 生成资产(可再生成)** → +**release/ 打包与发布产物(机器生成)**;README.md 面向使用者,AGENTS.md 面向 AI/Agent(工程约定与构建规程)。 + ``` LT8619C_Debug_Tools/ -├── HDMI-Tool/ # 源码 + 打包 exe + 测试图资产 + 图标 + README -├── I2C-Inject/ # PC 端 GUI + RP2040 固件 + 打包 exe + 图标 + README +├── HDMI-Tool/ +│ ├── src/ # 源码 + 打包/发布脚本 + 图标源 +│ ├── assets/ # 生成的测试图案 PNG(gen_*.py 产物) +│ ├── release/ # 打包 exe + 运行配置(入库);发布 zip(不入库) +│ ├── README.md # 使用说明 +│ └── AGENTS.md # AI/Agent 工程档案 +├── I2C-Inject/ # PC 端 GUI + RP2040 固件 + 打包 exe + 图标 + README ├── LICENSE └── README.md ``` +## 发布流程(HDMI-Tool) + +```powershell +cd HDMI-Tool/src +python build_exe.py # 打包 → ../release/HDMI-Tool.exe +python package_release.py <版本> # → ../release/HDMI-Tool-v<版本>-win64.zip +python publish_gitea.py HDMI-Tool-v<版本>-win64.zip # 上传 Gitea Release(需 GITEA_TOKEN) +``` + ## 备注 -- 源码与打包产物均入库:exe 改动跟随源码提交更新 +- 源码与打包产物(exe、pattern_slots.json)均入库;发布 zip 不入库,只上传 Gitea Release - 两件工具的寄存器语义(0x606E 位交换 / 0x606D 整组顺序)均经 demo 板逻辑分析仪 双色交叉实测,过程数据与规程存于上游主仓库 `doc/architecture/` -- 更新:2026-09-03 +- 更新:2026-09-10