Files
FilmForge/AGENTS.md
T
hulk 8900240c65 Heater: PID v0.1.14 - banded controller + sensor glitch guards
Rebuild PID as three-zone banded controller: far field P/D full power,
2026-10-15 20:55:02 +08:00

36 KiB
Raw Permalink Blame History

FilmForge — AGENTS.md

项目约定

  • 用途:自制热压模具温控固件——塑型 PC 膜,负责加热驱动与测温(NTC), 对外走 RS485 数据口。基于 GD32F4xx 工程模板 (https://gitea.hulk.wang/hulk/gd32f4xx_template_cmake_vscode,源自 celalkismiroglu/gd32e23x-demo-suites-cmake 体系)。
  • 命名:仓库 FilmForge,固件工程 FilmForge_FW(产物自动命名 FilmForge_FW_V<版本>_APP_<日期>.bin),协议板卡类型码 BOARD_TYPE_CODE 41。
  • 目标芯片:GD32F405RET6(设备宏 GD32F405,IRC16M PLL 168MHz——板上无外部 晶振;硬件 FPU fpv4-sp-d16 / hard ABI;512K Flash,链接脚本 xE)。
  • 串口角色与 PC 端映射:
    • debug 串口 = USART1(PA2 TX / PA3 RX,AF7,纯串口)→ PC 端 COM4; printf 重定向到 USART1(DEBUG_UART 宏),DEBUG_MODE 打开后输出。
    • 数据口 RS485 = USART0(PA9 TX / PA10 RX,AF7)+ DE 引脚 PA8(GPIO 手动切换 收发)→ PC 端 COM5(115200-8-N-1)。拉通阶段主循环回显。
    • 注意 GD 外设编号比 STM32 小 1:PA9/PA10=USART0、PA2/PA3=USART1。
  • NTC 测温:PC5(ADC0_IN15),硬件 10K 上拉、NTC 接 GND 侧。B 值公式换算, NTC_B_VALUE 宏(3950/3435 未定,默认 3950,确认后修改);R_PULL/NTC_R25 均为宏,换料只改宏。
  • 语言/工具链:C(gnu11)+ ARM GCC(xpack 15.2.1 已验证)、CMake ≥ 3.20 + Ninja、VSCode + CMake Tools + cortex-debug;工具链路径经 GD32_GCC_BIN。
  • 目录约定(沿用模板):
    • Build/ —— 构建树与产物(gitignore);
    • SDK/ —— CMSIS、标准外设库 V3.3.3、SEGGER_RTT、USB 库(GD32_ENABLE_USB 开关,默认 OFF,当前未用);
    • LD/ —— 链接脚本(默认 xE 512K);
    • Inc/+Src/ —— 应用层,板级引脚/开关集中在 Inc/board_config.h;
    • OpenOCD/ —— 工程内会话配置(GD-LINK/CMSIS-DAP + stm32f4x);
    • doc/ —— 芯片文档(数据手册/用户手册/固件库指南/AN,已齐备);
    • GUI/ —— 温控上位机(PySide6-Fluent,详见其 README.md,运行态 filmforge_gui.json/logs//build//release/ 由 GUI/.gitignore 挡)。
  • 编译方法:
    cmake --preset Debug && cmake --build Build/Debug
    cmake --preset Release && cmake --build Build/Release
    
  • Git/网络经验:origin = HTTPS(https://gitea.hulk.wang/hulk/FilmForge.git), 该实例 HTTPS/SSH 直传大包均不稳,已在本仓库配置 http.proxy http://127.0.0.1:6789 + http.sslVerify=false(仓库级), 代理端口变化时用 git config http.proxy http://127.0.0.1:<新端口> 更新。
  • printf 浮点:--specs=nano.specs 默认裁剪 %f(输出空白),已在 cmake/project.cmake 启用 -u _printf_float(F405 flash 充裕,ROM +8~10KB 无感)。
  • 在线排查寄存器地址速查(GD32F4xx):ADC0=0x40012000(RDATA@0x4C)、 USART0=0x40011000、USART1=0x40004400、GPIOA=0x40020000(OCTL@0x14)、 RCU:CTL0=0x40023800 / PLL=0x40023804 / CFG0=0x40023808。

开发进度

已完成

  • 模板就位(GD32F405RET6 / IRC16M 168MHz / 硬件 FPU / 上板验证过的模板体系)。

  • debug 串口:USART1 PA2/PA3 @115200,printf 重定向 + RBNE 中断收环形缓冲 (模板已有,COM4 观测)。

  • RS485 数据口驱动(rs485.h/rs485.c):USART0 PA9/PA10 AF7 + DE PA8 GPIO 手动切换;独立接收环形缓冲(预留一格策略 + PRIMASK 临界区);阻塞发送 (DE→TBE→TC→DE,防尾字节截断);rs485_rx_clear() 清接收缓冲。

  • NTC 测温驱动(ntc.h/ntc.c):PC5 模拟输入 + ADC0 常规通道(软件触发单次 转换 + 校准),16 次采样均值 + 分压反解阻值 + B 值公式换算摄氏度 (含开路/短路电源轨检测)。

  • debug_uart_write() 助手(uart.c):debug 口阻塞单字节发送。

  • 命令协议框架移植(command.h/command.c,自 E23x 移植):D5 帧协议 (D5 BOARD_TYPE LEN [cmd] CRC 累加和校验)+ B5 响应;数据源改为 RS485 收缓冲 (rs485_rx_*),响应经 rs485_send 发出(DE 自动切换);帧状态机 + CRC + 参数解析工具(uint_to_str/parse_uint_dec/parse_timer_parameter/find_parameter_value) 全套保留;通用命令骨架:M888 软复位 / M999 版本号 / M9999 进 OTA; 业务命令(加热/控温)待定义,switch 中扩展(TODO 注释有格式示例)。

  • 命令协议 v1.0 定稿(权威定义见根目录 protocols.md,帧 hex 全部脚本生成):

    • M1 查温度 / M2 查上报状态 / M2S 上报间隔+开关(M2S0 停)/ M3 加热开关 (M3S1/M3S0/M3 查询)/ M4 目标温度(M4S/M4 查询)/ M888 / M999 / M9999
  • 加热驱动 + PID 闭环实装(heat.h/heat.c,PB12=HEAT_EN):

    • 执行器:慢 PWM(载波 1s、占空 10% 步进 → 脉宽恒 >=100ms、开关频率恒 1Hz, 满足 2N7002K→VBE2412 PMOS 驱动电路 <=10Hz 约束)
    • PID:500ms 周期(NTC 采样 100ms),位置式 + 积分限幅 + 输出量化 10% 档; 参数 Kp=8/Ki=0.05/Kd=0(初值,宏在 board_config.h,待整定)
    • 安全:上电默认关;NTC 故障(OPEN/SHRT)强制关断;过温 150℃ 滞回保护; 未使能关断;使能切换/故障时复位 PID 积分
    • 实测 30℃ 闭环(NTC 贴 PTC 鳍片):30s 爬升、超调 1.4℃(7%)、60s 收敛中
    • M3/M4 已从软件桩切到 heat 模块(协议帧不变)
    • 协议要点:全幂等(ack 丢可重发)、响应超时建议 300ms×3 次、温度一律 ASCII
  • PID 抗积分饱和(5b24b95):条件积分(back-calculation lite)抑制输出限幅 期间的积分累积。

  • PID 全面重写(2026-10-15,待真机验证)——真机 30℃ 阶跃 +2.4℃ 过冲、 30~60s 周期衰减震荡(GUI 截图)驱动,三个根因全部修复: ① s_prev_err 在 err>=0 路径从不更新(c9aa416 early-return 重写时把末尾的 s_prev_err = err 弄丢)→ D 项退化为 +Kderr 正向比例,"微分阻尼"从未生效, 且把抗饱和判据钉在饱和态冻死积分(仿真中大阶跃贴温和带边缘极限环、到不了 目标);改**微分基于温度变化率(D on measurement,-KdΔT/拍),s_prev_temp 全路径刷新,u 钳 [0,100] 防负值量化回绕; ② 积分无界(err<0 每拍 -|err| 无下限)→ 过冲/下调目标后积分深负,温和带内 u=min(25,u_pd)+Ki*I 永远 <=0 → 加热长时间锁死、跌出温和带才全功率(极限环); 改积分钳 [0, HEATER_PID_I_MAX=2000]**(Ki*I_MAX=100%); ③ 12ba9de 的目标斜坡在 c9aa416 被整段删除 → 恢复 HEATER_RAMP_CPS 0.8℃/s

    • 使能时从当前温度起坡(s_ramp_target,升/降同限速); 另修三项:④ 抗饱和判据改看最终输出 u(原先看温和带重算前的 u);⑤ PWM 相位仅升档复位(原先降档也复位,降占空反而补发一整段 ON 脉冲,系统性 多给热);⑥ NTC 故障 3 拍(1.5s)去抖确认——真机曲线两个孤立 OPEN 毛刺 每次把 PID 状态整个清零也是震荡推手(毛刺拍保持输出不动;注意毛刺根因在 采样/硬件侧仍待查,GUI 按原始采样报故障不变)。 离线仿真(sim_pid_check.py:一阶热容+传感器滞后模型、2s/5% 慢 PWM 执行器、 旧/新同模型对比):30℃ 阶跃到达后波动 ±0.4℃→+0.04℃;50℃ 阶跃旧版极限环 到不了目标 vs 新版 94s 到达零过冲;50→40 下调 44s 恢复、波动 <±0.6℃。 固件版本升 V0.1.0(M999 报 v41.0.1.0),已烧录上板(OpenOCD GD-LINK, dump_image 读回与 elf 逐字节比对一致;注意该 xpack OpenOCD 的 flash write/verify 回显日志会丢失,核验用 dump_image+文件比对)。 设计说明:保持位置式 PID——执行器是绝对占空设定,分段强制关断/使能切换/ 故障复位在位置式下最干净;增量式的抗饱和优势已由积分钳位+条件积分覆盖。
  • PID V2 抗震荡迭代(2026-10-15,V0.1.1 已烧录待真机确认)——V0.1.0 真机 复测:过冲/OPEN 毛刺问题已消(峰 +0.40.5、无故障标记),但目标附近仍有 **±0.71.0℃/1520s 三角波极限环**(50℃ 目标实测截图)。根因:err<0 硬切 0% + err>=0 仅 Kperr,稳态维持功率(~10% 级)全靠 Ki=0.05 慢填(要几分钟) → 目标附近等效"P+上侧死区"继电器,叠加热滞后即极限环。V2 改法(heat.c pid_compute 重写核心): ① 连续 PID 覆盖全误差区间——去掉 err<0 硬切,过目标后 P 负向顶、积分 正向维持,输出钳 [0,100] 天然处理"只能加热"; ② Ki 0.05→0.25(稳态功率 ~1 分钟内建立;扫参显示 0.5 在传感器滞后 8s 工况下积分环失稳 ±0.9、1.0 更差,0.25 全工况稳定); ③ 积分吸收限窄带:仅 |err|<HEATER_PID_INTEG_ZONE_C(2℃) 且 P/D 未被 钳(u_pd_raw∈(0,cap))时吸收——爬升/温和带钳位段不积分,防瞬态 windup (Ki 提快后若爬升段照吸,30℃ 阶跃仿真冲到 36℃);err<0 且 u>0 照常有界 回退;I_MAX 同步 2000→400(KiI_MAX=100% 不变)。 仿真(sim_pid_check.py,V1/V3 对比 + 参数扫描 python sim_pid_check.py sweep,基准/恶劣两工况):恶劣工况 50℃ 阶跃到达后 [49.70,50.11](过冲 +0.11℃)、末端残余 ±0.02℃;30℃ 阶跃末端 ±0.010.02℃;50→40 下调 66s 恢复 ±0.02℃。已烧录 V0.1.1(M999 报 v41.0.1.1,dump 比对核验一致)。

  • PID V3 修"突然飙升 4℃"(2026-10-15,V0.1.2)——DAPLink 旁路抓证定位: V0.1.1 真机稳态后温度以 0.12℃/s 平滑飙升 +24℃ 再回落。定位手段(可复用): ① COM5 脚本探针(probe_once.py/probe_state.py)确认目标 50/加热 ON, 并现场抓到 14 帧连续 OPEN(~1.4s)——NTC 间歇开路真实存在(硬件侧 待修);② OpenOCD mrw 旁路观测(observe2.tcl+obs_decode.py, 每 500ms 不 halt 读 RAM:duty/integral/ramp/target/temp——注意该 xpack OpenOCD 的 echo/mdw 控制台回显会丢失,须用 TCL 落文件)。全程轨迹证实 **无故障参与的 90120s 自持极限环**:积分 0↔65 打摆(真机 50℃ 维持功率 10-15%,即 I≈4060 才是正确稳态),机理=err<0 时 P 被钳 0、输出只剩 Ki·I 恒定值——过线后输出不随超温幅度收敛→温度必然冲过头→过冲把积分 排空→排空后 sag→重建再过冲。NTC 毛刺清零积分只是点火器。V0.1.2 修复: ① err<0 时 P 负项+D 直接抵扣积分后钳 0(输出随超温幅度立即收敛, 积分按剩余需要维持→可收敛到稳态不再打摆);② NTC 短故障(≤3s 且 |ΔT|<2℃)无扰还原积分(确认沿暂存 s_fault_stash_I/T,防毛刺清零 引发 sag-重建-过冲链);③ I_MAX 400→200(Ki·I_MAX=50%,120℃ 模具维持 余量足够)。判定测试基建:test_run.py(固定序列 M999→M2S500→M4S50→ M3S1→N 秒→收尾;预定义判据 J1 上报率/J2 稳态均值±0.5/J3 稳态极值±1.0/ J4 全程 max≤52/J5 无故障,>54℃ 自动熔断 M3S0——开发/测试分工,脚本 自动判定避免自判自;注意 FF485 frame.payload 已是 str)。

  • PID V3 判定迭代与收敛(V0.1.2→V0.1.3,2026-10-15,900s 判定测试):

    • V0.1.2(P-veto + 故障无扰还原 + I_MAX 200)判定:J1/J2/J5 PASS,但 J3 FAIL(稳态窗 [46.6,52.1])、J4 FAIL(max 52.5)——P-veto 消灭了 积分排空式打摆(观测器证实收敛末段积分稳在 ~48=真实维持功率、温度 ±0.2),但双热容被控对象(鳍片+本体,NTC 只见鳍片)每次顶/谷都给 对方"预存"动量,15 分钟才收敛;且斜坡 0.8℃/s 远快于对象,爬升段误差 长期 2℃ 把积分提前充到 3050,首冲 +2.5℃。
    • V0.1.3:HEATER_RAMP_CPS 0.8→0.25(爬升误差贴地、积分只在近目标 低功率段建立)+ HEATER_SLOWBAND_DUTY 25→15(近目标动作放柔, 少给鳍片预存过热)。41℃ 冷启判定:J1~J5 全 PASS——到达 69s、 首冲 +1.20℃、全程 max 51.2、稳态窗 [49.0,50.9]、均值 49.95、 1795 帧 0 故障。观测器同步确认:远场(err>7)才见 75% 满功率, 温和带内 15% 缓爬,50.06℃ 到达时积分 35.9/占空 5%。
    • 测试基建沉淀:test_run.py(判定+熔断)、observe2.tcl+obs_decode.py (DAPLink 旁路,注意符号地址随 bss 布局变,重编译后须 nm 重取; 观测文件被占用时追加不清空)、sim_pid_check.py(含 V1/V3/V4 对照、 毛刺/噪声注入、扫参)。CMD 坑:if exist X del X && ... 文件不存在时 整链跳过;多行 python -c 偶发被吞——写文件执行。
    • 遗留(硬件侧):NTC 间歇开路(现场抓到 14 帧连续 OPEN ~1.4s, 短故障无扰还原已缓解控制影响,但根因在采样/接触)——查 NTC 焊接/ 压接/走线,必要时 ntc.c 采样侧加防护。独立代码审查 agent 未在时限内 返回,本轮定位以仪器化数据(COM5 帧流 + DAPLink 内存轨迹)为准。
  • PID 调试全程速览(2026-10-15,V0.1.0→V0.1.14,判定测试自动出结论):

    版本 关键改动 判定/实测结果 结论
    V0.1.0 全面重写:D-on-measurement+积分钳位+斜坡恢复+故障去抖 过冲 +2.4→+0.4 旧版 D 项失效/积分无界/斜坡缺失三根因修复
    V0.1.1 Ki 0.05→0.25+窄带吸收 ±1℃ 三角波极限环 err<0 硬切=继电器死区
    V0.1.2 P 参与过线区+故障无扰还原+I_MAX200 J3/J4 FAIL P-veto 生效但收敛 15min
    V0.1.3 斜坡 0.25C/s+温和带 15% J1~J5 全 PASS(旧判据) 稳态 [49.0,50.9] 不满足 ±0.5
    V0.1.4 占空抖动 1.25% 分辨率 p2p=1.8 FAIL 量化非主因
    V0.1.5 Kp5/Kd60/Ki0.15 连续 PID p2p=4.2 FAIL ±2C/90s 慢摆,调参到头
    V0.1.6 分区 bang-bang+慢积分 卡 48.6C FAIL 带外平衡点陷阱
    V0.1.7 带外也吸收 p2p=4.1 FAIL 吸收/回退不对称→I 上漂 90
    V0.1.8 对称慢速积分 p2p=4.0 FAIL 积分稳了;秒级读数坏值穿透
    V0.1.9 斜率限幅+带内退闸 无法判定 NTC 持续开路(后重焊恢复)
    V0.1.10 去上限+RAMP2.0+温和带25% p2p=2.7 FAIL 快斜坡生效;坏值仍是主力
    V0.1.11 NTC 5 点滑动中值 p2p=1.30 中值滤波立竿见影
    V0.1.12 近场 ASSIST 减速曲线 实时 p2p=0.31 最后 2C 推力真空修复;判定窗被传感器事件污染
    V0.1.13 大偏差冻结(>1.5C) p2p=1.60 塌陷深谷消除(47.1→49.0)
    V0.1.14 对称积分加倍 0.30 p2p=1.80 与 13 同水平=硬件平台期

    最终状态(V0.1.14 在板):到达 26s(30→50C,提速 9 倍);控制环本体 (无事件段)p2p 0.30.9C 达标;判定窗残余 1.61.8C 全部来自 NTC 焊接点 秒级接触不良(-2.5C/4.5s 塌陷、顶格 52.2C 卡死 10s,1.5C 以内坏值仍会穿透 三重滤波)。下一步:彻底重焊/更换 NTC(根治手段),固件三重防护 (中值+斜率限幅+大偏差冻结)已就位,重焊后 python test_run.py 1200 一键 重判(J3 即 ±0.5 规格)。测试环境 55C 上限已按用户要求移除(真机靠物理 温度开关)。

  • ±0.5℃ 波动压榨迭代(2026-10-15,V0.1.3→V0.1.6,用户规格 ±0.5C, 判据 J3 收紧为稳态窗 [目标±0.5])——V0.1.3 稳态窗 [49.0,50.9](压着 旧判据 ±1.0 过)不可接受,连续三轮仪器化迭代:

    • V0.1.4 占空抖动(Bresenham):载波 2s=4 拍分摊档位余数,平均分辨率 5%→1.25%(真机维持功率 ~10-12% 恰卡在 10/15 档间,固定取整迫使 0↔15% 满幅切换)。观测器证实抖动生效(duty 5/10/15/20/25 细粒度), 但极限环未消(J3 p2p=1.8 FAIL)→ 量化不是主因。
    • V0.1.5 降增益强阻尼(Kp 10→5 / Kd 30→60 / Ki 0.25→0.15 / 窄带 2→1): J3 p2p=4.2 FAIL——变成 ±2C/90s 慢摆。观测器抓到机理:带内半周期 净吸积分 +18(err 0.5~1.0 持续 30s),过线后 P-veto 每拍泄 2.8 快速 排空 → 维持功率记忆反复丢失 → 谷底深潜 48.6。连续 PID 在本对象 (双热容+单向执行器+0.1C 读数量化)上调参到头;Kd=60 还放大 NTC 0.1C 量化跳变成 ±6% duty 抖动。
    • V0.1.6 分区 bang-bang + 慢积分学习(结构性换型):作用分三区—— 带内 |err|<HEATER_PID_BAND_C(0.4C):P/D 退出,u=KiI 恒功率,积分慢速 学习维持功率(I+=err0.3/拍);带外下方:P 拉回(温和带钳位不变), 积分不吸收;带外上方:关断 + I 固定 0.3/拍限速回退(保住维持功率 记忆)。波动幅度被物理限制在带宽+滑行量,I 学准后驻留带内。 Kd 回 30(60 的噪声放大不可用)、I_MAX 回 400(Ki*I_MAX=60%)。
    • V0.1.6 判定 FAIL(带外平衡点陷阱):观测器抓到温度卡 48.6C——P 输出 恰好平衡散热、err 停带外,积分只在带内学习永远没机会介入。V0.1.7 修复 (带外下方 P 未被钳时同样慢速吸收):前半程良好(49.8/integ 37 稳),但 吸收(0.3/拍)与回退(0.3/拍固定)不对称 → I 上漂到 90 持续超供,后半程 大摆(J3 p2p=4.1 FAIL)。V0.1.8 改全区域对称慢速(GAIN=BLEED=0.15): 积分不漂了(35~42 窄幅)、带内占有率 44%,但 J3 仍 FAIL(p2p=4.0)—— csv 分析抓到 -1.2C/0.5s 读数突跳 + 5s 后 +1.8C 回弹(非故障级 NTC 毛刺,真实热速率 <=0.2C/拍),毛刺触发 25% 猛拉 ~10s 直接制造 +2C 过冲。
    • V0.1.9(待判定):① NTC 读数斜率限幅(HEATER_NTC_SLEW_C=0.5C/拍, 超限沿限幅值过渡、连续 2 拍放行——毛刺对环路的冲击源头掐断);② 带内 功率线性退闸(下缘 100% 维持量 -> 上缘 0%,过线前功率已趋零,蓄热滑行 收窄)。已烧录 MATCH。
    • NTC 硬件恶化为持续开路(阻塞判定):V0.1.9 判定测试全程 M1/M2 报 OPEN(帧流正常、命令全 ok),加热安全关断(故障路径实测有效)。用户 重焊后恢复(30.1C 稳定读数),进入 V0.1.10~V0.1.14 快速迭代:
    • V0.1.10 去上限 + 提速:测试脚本去掉 54C 自动熔断/J4 上限判据(用户 明确:真机靠物理温度开关,55C 只是测试环境约束);RAMP 0.25→2.0(实测 0.25 时斜坡本身就是瓶颈,9C 走 36s);温和带 15%→25%。判定:reach 49s 但 J3 p2p=2.7 FAIL、首冲 +3.3。
    • V0.1.11 NTC 5 点滑动中值(ntc.c 采样侧):V0.1.10 稳态摆的根源确认为 秒级读数坏值穿透斜率限幅(中值滤波只挡 <250ms 毛刺)。效果显著: 稳态 p2p 4.0→1.30([49.40,50.70])。温和带 7→3C、25→35%。
    • V0.1.12 近场 ASSIST 减速曲线:分段速率分析钉死最后 2C 爬 141s 的瓶颈 =近场推力真空(P 随 err 缩小消失、I 未成熟)。近场扩展区 [-0.3,3.0) 合并退闸+缓爬:u = Ki*I + ASSIST 退闸曲线 + Kd,上限 NEAR_MAX_DUTY 50%。 实时观测 p2p 0.31C(达标水平);但判定窗两极值(47.1 塌陷 + 52.2 顶格卡死)均为传感器事件:-2.5C/4.5s 塌陷与顶格 10s,焊接点仍不稳。
    • V0.1.13 大偏差冻结:滤波值与原始读数偏差 >1.5C(HEATER_NTC_JUMP_C) 冻结不跟随,回弹自动恢复(真实冷却 ~0.05C/拍永不误触发)。判定 p2p 1.60([49.00,50.60])——塌陷深谷消除。reach 32s(提速 7 倍)。
    • V0.1.14 对称积分加倍(GAIN=BLEED 0.15→0.30,I 充满 400s→200s,修复 判定窗内驻留点偏低):p2p 1.80([49.20,51.00]),与 V0.1.13 同水平 (差异在传感器事件噪声内)——到达当前硬件条件下的平台期。
    • 当前结论:控制环本体(无事件段)p2p≈0.30.9 达标;残余 1.61.8 全部 来自 NTC 焊接点秒级接触不良(塌陷/顶格事件,1.5C 以内的坏值仍会穿透 防护)。下一步是彻底重焊/更换 NTC 及其引线,固件侧三重防护 (中值+斜率限幅+大偏差冻结)已就位。到达速度 26s(30→50C,提速 9 倍)。 测试/观测基建:test_run.py(无熔断版,J1/J2/J3±0.5/J4 无故障自动判定)、 observe2.tcl+obs_last.py(每次重编译必须 nm 重取符号地址)、 seg_rate.py(分段爬升速率)、log_tail.py(实时帧解析)。 中间数据(obs_samples.txt/test_data.csv/test_events.log)已清理, 由测试/观测脚本重新生成。
    • 方法论沉淀:单状态热模型仿真始终未复现真机极限环(双热容建不进单 状态),本轮所有定位直接以 DAPLink 观测器 + COM5 判定脚本为准; 观测符号地址 V0.1.4 起稳定(s_duty=0x20000459 / s_integral=0x20000454 / s_ramp_target=0x2000045c / s_target=0x20000460 / s_enable=0x20000464)。
  • 50℃→40℃ 阶跃验证脚本(temp_test_com.py,工作区修改未提交):M2S500 全曲线采样,输出超调/settle 时间/稳态误差/降温段统计(结果以实跑为准)。

  • 电源保持 PA11 实装(pwr_hold.h/pwr_hold.c):上电最先拉高 PA11 维持 PMOS 自锁供电(先于一切外设初始化);pwr_hold_off() 预留软关机(置低即失电)。

  • 专用 GUI 上位机 v0.1.0 完成(GUI/,参照 DLPilot 分层:协议模块纯标准库 + PySide6-Fluent 界面 + 自检 + PyInstaller):

    • 文件角色:filmforge_rs485.py(帧/解析/串口线程 FF485)、app_fluent.py (双栏仪表盘 + 自绘温度曲线,未引第三方图表库)、config.py(设置记忆 filmforge_gui.json)、filmforge_gui.py(入口+CLI --port/--cmd/--seconds-report)、 selftest_rs485.py/selftest_gui.py(协议向量 / 离屏渲染+派发回归)、 build_exe.py → release/FilmForge.exe+发布 zip、publish_gitea.py (tag FilmForge-GUI-vX.Y.Z)
    • 功能:实测温度曲线+目标温度阶梯线同图(闭环监视)、M2 上报帧直采 / 上报关闭时 上位机自动轮询 M1 补点、NTC OPEN/SHRT 断口+故障标记、时间窗/暂停/窗口统计、 目标(M4S 0200)/加热(M3S1 确认)/上报(M2S 10060000)控制、连接自动同步 版本/上报/加热/目标、M888/M9999 二次确认+复位后重查、收发日志、CSV 记录
    • COM5 真机全链路验证通过:M999=v41.0.0.1、M1/M2/M3/M4 查询、M2S500 上报 3s 收 6 帧、M2S0 停止;GUI 端到端(串口线程→队列→派发→曲线/状态芯片)实测通过
    • 关键教训(勿重蹈,详见 GUI/README.md):①应答派发必须是 FIFO 队列(半双工 应答按发送序返回;单 pending 槽在连接序列 150ms 连发下被乱序击穿,实测 M2 的 OFF 被 M3 槽吃掉、M4 目标值混入曲线);②时间戳在串口线程读取点打(SerialMate 教训);③M4 目标应答路由窗口取条目生命周期 1.2s(适配器换向延迟实测可 >0.5s, 上报开启时才收紧 0.5s);④Fluent 控件样式走 setCustomStyleSheet(DLPilot 同款坑)
    • 发布策略:GUI 产物(release/)暂不入库,本地 python build_exe.py 生成;发布走 publish_gitea.py 上传 zip(tag FilmForge-GUI-vX.Y.Z,与固件 tag 区分)
  • SerialMate 串口工具问题已定位(见 E:\Hulk-Coding\HW-Debug-Tools\SerialMate\ISSUE_rx_timestamp_and_lag.md): 时间戳在 GUI 消费时打导致失真 + 接收日志无限增长致卡顿;待该工具修复后 再做长时间曲线联调。

  • 工程改名:FilmForge_FW、BOARD_TYPE_CODE 41。

计划 / TODO(已提出尚未实现)

  • RS485 拉通上板验证:命令收发/响应/上报实测通过(固件端零丢字节, 响应 10ms 级实时);遗留为 USB-RS485 适配器方向切换丢包(换适配器解决) 与 SerialMate 显示层问题(见 ISSUE 文档)。
  • NTC 测温上板验证:NTC 支路修复后读数正常(23.6~25.9℃ 稳定)。
  • B 值确认:温度计对照实测定 NTC_B_VALUE(3950/3435 二选一)。
  • 适配器更换:换可靠的 USB-RS485 适配器(当前自动方向切换间歇丢响应)。
  • 加热驱动 + PID 闭环(PB12 慢 PWM 1s/10% 步进 + PID 500ms 周期, 安全保护齐全;实测 30℃ 闭环通过:30s 爬升、超调 1.4℃、60s 收敛中)。
  • NTC 彻底重焊/更换(当前头号事项):焊接点秒级接触不良已用 DAPLink+COM5 数据链钉死(-2.5C/4.5s 塌陷、顶格卡死、曾持续开路), 是 ±0.5C 规格最后的拦路虎;固件三重防护已就位(V0.1.14)。
  • ±0.5℃ 规格复判:NTC 重焊后 python test_run.py 1200(J3=±0.5); 当前稳态控制环本体 p2p 0.30.9C 已达标。真实工况温度(80120℃)下 需复测整定(Kp=8/Ki=0.15/Kd=30、ASSIST 35%、I_MAX400,board_config.h)。
  • 专用 GUI 上位机 v0.2.0:v0.1.0 已完成 COM5 真机全链路验证;v0.2.0 与 实装固件对齐——去除"固件预留/仅软件状态"旧表述(协议模块预设说明、温控 卡片标题/备注、开启加热确认弹窗改为真实控温警示、ack 提示),GUI README 与 publish_gitea 发布说明模板同步,protocols.md M3 表项去"预留"; 监控逻辑简化:温度监控统一走 M2 周期上报(移除"M1 自动轮询"双通道及 poll 配置项,M1 仅手动单点查询),目标温度滑条范围 0200→0120(协议 上限 200 仍可手输)。v0.2.1 真机回归修复(COM5 实测暴露):①M4 查询 应答与周期上报帧同为 ASCII 浮点被误占(目标 chip 显示成实测值)→ 查目标 改应答驱动"暂停上报 M2S0→M4→自动恢复 M2S"链;②设定 ok 延迟 >1.2s 超时清队后状态 chip 与设备脱节(实测 M2S100 ok 迟到、chip 停 OFF)→ 8s 内晚到 ok 按发送时计划补偿应用;③断开按钮偶发不切换、断开后日志仍刷 → 连接按钮改意图驱动(点击即翻转、2s 未建立判丢失回退),已连接/已断开/ 丢失全部入日志,连接同步序列每步发送前确认在线(修复序列整组被 "未连接串口"丢弃)。selftest 全过(新增 8 项回归用例);已打包冒烟并发布 Gitea Release FilmForge-GUI-v0.2.1(无应用图标——logos/filmforge.ico 仍缺位)。
  • GUI v0.2.2~v0.2.13(递进修复/新功能,Gitea 发布待确认):
    • v0.2.2:串口线程 read(256) 在持续帧流下攒满 256 字节才返回(win32 字节 间隔超时因帧间隔更短永不触发,100ms 上报 ≈2.5s 一批),曲线/命令/响应全部 3 秒一卡、一股脑一大坨——真机日志确认每簇 ≈25 帧、间隔恒 3s。已改 read(in_waiting or 1),真机复测曲线实时、命令即发即回。
    • v0.2.3:发送入口幂等去重——同命令未决时忽略重复发送(真机连点"停止"12 次 挤爆 8 条 pending 队列、"丢弃积压命令"刷屏),状态栏提示"应答中"。
    • v0.2.4:目标阶梯线渲染 bug(v0.1.0 起潜伏)——_target_points 对最后一次 目标变更只画终点,从旧值斜拉直线到图表右缘(实测 57→41 被画成 2 分钟斜坡, 截图确认);改为纯阶梯(变更时刻垂直落变+水平延伸),数据本身无误。
    • v0.2.5:排版调整——串口连接卡片从右列移到左列顶部,左列图表卡/右列日志卡 弹性拉伸填充,两列底边对齐。
    • v0.2.6:对齐修复——双列改放同一滚动容器(整页统一滚动):原先两列各自包 QScrollArea 独立滚动,窗口化下底边错位(全屏正常);现两列高度锁定一致, 不足一屏各自拉伸到底、超出一屏整页滚动。
    • v0.2.7:图标接入(logos/ 下 ico/png/svg 已就位)——exe 嵌入图标 (build_exe.py --icon,此前缺文件自动跳过)、窗口标题栏/任务栏/弹窗图标 (app+window 两级 setWindowIcon,_icon_path() 兼容源码态与 _MEIPASS 打包态)、 Windows 任务栏独立标识 AppUserModelID "hulk.FilmForgeGUI"。README"无应用 图标"遗留项关闭。
    • v0.2.8:慢性卡顿修复——_log() 手工 cursor 裁剪(cursor.Start/ BlockUnderCursor 实例级枚举访问)在 PySide6 6.11 上必然抛 AttributeError, 日志超 500 行(100ms 上报 ≈50s)后每个 100ms tick 异常中止 _tick 后半段: 应答超时清理失效(pending 永不清 → "停止"点击被幂等去重永久拦截,实拍状态栏 "M2S0 应答中,已忽略重复发送")、RX 队列积压 ratchet(帧处理滞后 54ms→均值 1037ms/峰值 2701ms,日志/chip/曲线显示滞后数秒,"停止 5s 后才见命令发出"实为 日志显示积压,TX 本身 5~166ms 即出)、日志无限增长、CPU +30%。改原生 setMaximumBlockCount(500) 环形裁剪;selftest 新增"500 行封顶/封顶后可追加/ 最新行在末尾"3 条回归。离屏 A/B 实测(GUI/_diag_latency_buggy.py,假固件 100ms 上报+定时点击):buggy 组 5.5 分钟异常 2241 次、修复组全程漂移 ≤15ms/ 零异常/零积压。v0.2.7 exe(2026-10-10 打包)内嵌同版本 PySide6 6.11.2, 同带此 bug,需重新打包生效。
    • v0.2.9:整体审查修复 5 项——①CSV 写失败(磁盘满/记录盘被拔)自动停记录 并提示,不再沿 _tick 帧处理路径抛 OSError 中止主循环(与 v0.2.8 日志裁剪同款 失效管道);②故障标记 faults 封顶 1000 条(曾是唯一无上限缓冲:NTC 抖动时 10 条/s 无限增长,且 _draw_faults 每 paint 全量扫描);③串口写超时单独捕获 SerialTimeoutException——只丢该条命令、链路保留,不再按 OSError 当"设备拔出" 关端口断链(适配器停滞曾迫使手动重连);④日志自动滚动仅在贴底时跟随(上报 运行时可上翻历史,不再被拽回底部);⑤冻结视图切片右缘同样二分(暂停时长不再 增加 render 成本)。selftest 新增 7 项回归(封顶/滚动跟随/CSV 失败自停/冻结 切片)共 59 项全过。整体审查报告(2026-10-12)其余 P3 遗留:selftest 未覆盖 _tick 主循环(超时清理/丢失判定)、"全部"窗口全量切片有 MAX_SAMPLES 兜底、 _last_set 单槽位罕见误补偿(8s 自愈)、build_exe remove_stale 无提示强杀 运行实例、M888 后固定 2s 重查可能早于固件就绪、_danger_reset 把去重忽略记成 "已下发"。协议建议的 300ms×3 自动重发维持不做(设计决策,用户拍板)。
    • v0.2.10:曲线 Y 轴手动量程 + "暂无数据"提示不消失修复——①"实时温度曲线" 卡新增 Y 轴行:下限/上限输入框 + 自动勾选钮(默认勾选=随可见数据自适应; 取消勾选=按输入框固定量程,非法输入回退最近一次自适应范围并经状态栏提示; 首次切手动按当前自适应范围预填平滑接管;固定量程下曲线越界 setClipRect 裁剪回绘图区、当前值标记超量程不画,实时读数卡仍可见)。模式+量程随 filmforge_gui.json 持久化(y_axis 键,存值非法一律回自动)。 ②提示文字不消失根因(用户标注截图):set_source 仅在构建期调用一次且在 那里缓存 _has_data 布尔——数据经同一列表引用追加后该布尔永不刷新 → 改为 paintEvent 实时判定(图表本就持有引用,无需其他调用点适应)。selftest 新增 15 项回归(切换往返/兜底预填/无效回退/量程落地/越界条带像素=0/新图数据到位 提示像素=0;断言须取当前主题曲线色、上条带避开 y=9 行图例色块),总数 74 全过;离屏整窗预览目检(手动/自动对照)布局正常。
    • v0.2.11:Y 轴量程控制改右缘滑条(用户要求放右侧、滑条形式)——移除 v0.2.10 的下限/上限输入框行,"Y轴自动/手动"勾选钮移到暂停/清空行右端; TemperatureChart 右缘新增滑条栏(GUTTER_W 18px:轨道+量程带+双把手): 上把手=上限、下把手=下限、把手中带拖拽=整段平移(跨度保持)、点击轨道 空白把对应边界直接设到该处;把手在参考域(全量数据范围 ±15%,_y_ref) 上映射像素,auto 态把手半透明、首次任意拖拽即切手动(yManualEngaged 同步按钮态+配置),松手量化 0.1℃ 经 yRangeChanged 记设置(最小间距 0.5℃),按钮一键回自动;越界裁剪沿 v0.2.10 不变。坑两条(勿重蹈): ①拖拽中的界值必须取实时显示范围而非 _cached_layout 绘制缓存—— 连续两次拖拽之间没有重绘,缓存是陈旧范围(实测 pan 把量程整段拖跳); ②selftest 固件用真实 filmforge_gui.json 时"初始自动"断言会被用户存的 手动模式击穿——改指向临时目录设置文件彻底隔离。 selftest 重写第 8 段共 81 项全过(把手命中/拖拽切手动/量化/最小间距/ 平移保跨度/裁剪条带/提示消失);离屏手动/自动滑条目检通过。
    • v0.2.12:滑条域定值 -20~120℃ + 标题行并排(用户截图标注 3 处)—— ①滑条参考域从"全量数据 ±15%"改为固定 -20~120℃(Y_RAIL_LO/HI 宏): 把手像素位置直接对应 ℃ 读数,空数据/冷机不再出现 -5.0 这类随数据漂移的 误导把手位;命中/拖拽/钳位全部按此域,140℃ 域上 1px≈0.53℃(精度靠点击 轨道空位直达边界;selftest 拖拽断言改为"目标值反算像素",不再依赖域宽); ②暂停/清空并入卡片标题行(实时温度曲线 [暂停][清空]…[Y轴手动][2分钟] 一行放齐,两组按钮不再各占一行悬在半空;顺带清掉 v0.2.11 重构残留的 孤儿按钮组代码;此卡不走 self._card 通用头,内联构建支持多 trailing); ③实测/目标图例移到绘图区左上(右侧被滑条把手/值标签占用)。selftest 81 项全过;离屏目检(空态/手动拖后)通过。
    • v0.2.13:把手值标签悬停门控(用户标注:常驻会遮挡曲线)——上下限 两个数值标签不再常驻,仅鼠标悬停在滑条栏(或拖拽中)才显示 (_y_hover 翻转才重绘;leaveEvent 途中拖拽不退出悬停态,松手按位置 重算)。selftest 新增 2 项(未悬停标签区像素=0 / 悬停出现标签),共 83 项全过。exe 重新打包见下。
    • selftest 全过(v0.2.13 后共 83 项回归);v0.2.13 exe 已重新打包并冒烟 通过(release/FilmForge.exe + FilmForge-GUI-v0.2.13-win64.zip, 依次取代 v0.2.10/v0.2.11/v0.2.12 包),Gitea Release 待用户确认后执行。 遗留见其 README"已知事项":USB-RS485 适配器更换。
  • GUI 慢性卡顿修复(根因已定位并修复 v0.2.8,待真机确认): 根因/机理/实测数据见上方 v0.2.8 条目(cursor.Start 实例级枚举访问在 PySide6 6.11 必抛 → tick 异常中止 → 应答超时清理失效 + RX 积压 ratchet + 日志无限增长)。复现/验证脚本:GUI/_diag_latency.py(修复基线)/ GUI/_diag_latency_buggy.py(带 bug 对照,假固件 100ms 上报+定时点击)。 遗留:待真机长时间运行确认 + 重新打包 exe。
  • LED 引脚确认:FilmForge 自制板 LED 引脚未定(当前沿用模板默认 PA15, 仅为构建占位),按原理图修改。
  • delay_10us 校准:M4@168MHz 系数为估算值(/300000),需实测修正。

备注

  • 本机凭据/发布约定见用户级 C:\Users\Lenovo\.zcode\AGENTS.md。
  • 模板工程位置:E:\Hulk-Coding\gd32f4xx_template_cmake_vscode(模板对标工程, Gitea: gd32f4xx_template_cmake_vscode)。
  • E23x 工程位置:E:\Hulk-Coding\gd32e23x_template_cmake_vscode(协议/驱动参考)。