from pathlib import Path
import json,hashlib,zipfile
R=Path(__file__).resolve().parent;B=R.parent;H=B/'engineering_handoff_local';T=R/'firmware/固件/lamp_target';sha=lambda p:hashlib.sha256(p.read_bytes()).hexdigest()
build=json.loads((T/'build/build_verification.json').read_text());board=B/'lamp_IR_PCB_A39/board/lamp_safety_evt_a3_IR_PCB.kicad_pcb';bc=json.loads((B/'lamp_IR_PCB_A39/checks.json').read_text());assert sha(board)==bc['PCB_sha256'];assert build['IR_GPIO_bound'] and not build['led_output_enabled']
checks={'status':'A40_PC15_IR_TARGET_BINDING_LOCAL_PASS_FABRICATION_HOLD','board_pair':'A39 PCA lamp_safety_evt_a3_IR_PCB','board_sha256':sha(board),'target_build':build,'IR_initialization_clock_prior_cases':16,'test_scope':'Actual vendor-register-layout MMIO model, no voltage/oscillator/sensor/hardware proof','sanitizers':'ASAN_UBSAN_PASS','clock_ownership':'LSEON/LSERDY/LSEBYP must all be clear; no RTC/BDCR writes; unexpected ownership disables IR permission','provisional_assert_ms':10,'provisional_max_sample_gap_ms':5,'removal':'Clear IR permission at next serviced LOW/unavailable sample; not independently measured physical latency','negative_control':'Synthetic PC14 reader rejected by PC15 raw-input assertion; not a historical image','NTC_ADC_temperature_supply_bound':False,'LED_driver_health_bound':False,'outputs_enabled':False,'physical_verified_requirements':0,'reference_requirements':20}
(R/'checks.json').write_text(json.dumps(checks,ensure_ascii=False,indent=2)+'\n')
refs={'STM32G0x0_reference':'https://www.st.com.cn/resource/en/reference_manual/rm0454-stm32g0x0-advanced-armbased-32bit-mcus-stmicroelectronics.pdf','reference_scope':'Official indexed RM0454 Rev5 section6.3.13/6.3.14; local full PDF download attempt timed out, no PDF hash claimed','ST_datasheet':'https://www.st.com/resource/en/datasheet/stm32g030f6.pdf','device_header':'firmware/固件/lamp_target/Device/Include/stm32g030xx.h','device_header_sha256':sha(T/'Device/Include/stm32g030xx.h')}
(R/'source_references.json').write_text(json.dumps(refs,indent=2)+'\n')
report=f'''# 灯具 PC15 红外输入目标固件绑定 A40

状态：A39红外PCB的物理脚3/PC15已在目标源代码与链接映像中绑定；本地主机寄存器、策略及调用路径验证通过。FABRICATION_HOLD，三款整机20项效果验收仍0项实测验证。

## 已完成的实际接入

`lamp_board_ir_init`在原有安全启动之后调用：确认 LSEON/LSERDY/LSEBYP 均为0，开启GPIOC时钟，检查PC15未被锁定，设置PC15输入、无内部上下拉（使用A39外部47k下拉）、清除该脚AFR。保留其他GPIO位和RTC/BDCR，不重置或清除RTC域。固件设计使用SysTick计时、不使用LSE；需要本固件和A39板的正常冷启动环境，不能承诺任意旧固件保留的时钟配置可直接兼容。

每次安全采样检查PC15时钟、模式、上下拉、AF和锁定状态；异常时不可用，原始IR位不授权。正常时读取GPIOC.IDR bit15，经独立滤波器产生门控的 `infrared_hand_present`。10ms连续高样本确认有手；LOW/不可用立即在本次采样清除许可；样本间隔大于5ms重新确认。相同时间戳不会累计时长，32位计时回绕已测试。10/5ms是候选参数，尚无传感器响应测量。软件在下次被调度采样时处理撤销，不能把5ms参数或主机测试当作已测关断延时；CPU停顿需外部看门狗的实际测试。

主循环和每个完整串口命令/AI结果处理前均采样并调用 `lg_poll`。输出仍要求独立新鲜视觉结果与其他安全条件；没有用AI_RESULT冒充IR输入。GPIO/RCC配置与guard只能由当前串行控制路径持有，不能在未协调的ISR中启用LSE/改PC15。

## 验证证据

- 16组真实厂商寄存器布局模型：两种先前GPIO状态 × 8种LSE占用标志组合；测试物理PC15配置、其他位和RTC配置保留。
- 引脚锁定、时钟被关、GPIO/上下拉/AF被改时许可撤销；入手确认、移手、重新入手、5ms边界/6ms超限、短脉冲、同时间戳、倒退时间和回绕均测试。
- 实际采样函数到实际guard的主机集成：其他安全条件明确用主机模拟就绪，产生AI输出后移手撤销输出/票据；重入必须有新票据和新视觉帧。目标本身没有这些模拟就绪条件。
- 故意改读PC14的合成负对照被物理PC15断言拒绝；不是宣称某个旧硬件或旧二进制已实测失败。
- 原有guard、IR策略、计时、票据、UART/UI/输出适配器回归与128组原始GPIO采样回归通过，ASan/UBSan通过；8组原有A36启动测试及旧代码负对照保留通过。
- 完整Cortex-M0+编译链接、向量/符号及main可达IR初始化、采样到滤波器的调用路径静态核验通过。FLASH {build['flash_binary_bytes']} B，BIN SHA256 `{build['sha256_bin']}`。

配对PCB A39 SHA256 `{sha(board)}`；网表U1.3=IR_HAND。A38旧元数据PCB的PC15仍NC，不能烧录为具有红外硬件功能的板。仅配对PCA9956B分支，不适用于IS31FL3236A相反使能极性分支。

## 未完成与下一步

本轮绑定GPIO是实现证据，不是光学/电压/RTC晶振或实际整机效果证据。红外传感器精确MPN、光学位置、供电/线缆/ESD、肤色/手套/环境光及响应时间仍未选定或验证，接口只接受候选3.3V主动高推挽信号。NTC温度/供电ADC和LED驱动健康反馈仍未接入，相关授权字段保持false。guard未配置校准或授权，输出适配器未绑定，外部看门狗未踢，LED输出关断。

下一步接NTC与供电测量，并闭合驱动反馈/输出控制、校准配置和物理验证路径。全目标仍包含电磨、吸尘器、灯具三款与原产品一致的效果、千台整机成本及实机验收，均不能由此固件测试代替。未采购、联系供应商、烧录、通电或下单制造。

## 工程复核

包内包括可编辑完整源码、ELF/BIN/MAP、主机测试与日志、配对索引及SHA清单。运行 `sh run_checks.sh`，需要cc/Python3/arm-none-eabi工具链，测试所需源码均在包内。ST参考依据见 `source_references.json`；官方完整PDF下载未成功，不伪造其本地hash。BOM/CAD未改变。
'''
(R/'README.md').write_text(report);(H/'A40-lamp-PC15-target-binding.md').write_text(report)
files=[p for p in R.rglob('*') if p.is_file() and p.name not in ['prepare.py','package.py'] and not p.name.startswith('.') and p.suffix in ['.c','.h','.s','.ld','.sh','.py','.md','.json','.log','.txt','.elf','.bin','.map']]
manifest=''.join(f'{sha(p)}  {p.relative_to(R).as_posix()}\n' for p in sorted(files));(H/'A40-SHA256SUMS.txt').write_text(manifest)
z=H/'Lamp-PC15-IR-target-binding-review-A40.zip'
with zipfile.ZipFile(z,'w',zipfile.ZIP_DEFLATED) as a:
 for p in files:a.write(p,p.relative_to(R).as_posix())
 a.writestr('SHA256SUMS.txt',manifest)
with zipfile.ZipFile(z) as a:
 assert a.testzip() is None
 for line in manifest.splitlines():
  digest,name=line.split('  ',1);assert hashlib.sha256(a.read(name)).hexdigest()==digest
 count=len(a.namelist())
ip=H/'LOCAL-PACKAGES.json';idx=json.loads(ip.read_text());idx['status']=checks['status'];idx['latest_report']='A40-lamp-PC15-target-binding.md';idx['latest_machine_report']='../lamp_IR_target_A40/checks.json';idx['latest_manifest']='A40-SHA256SUMS.txt'
idx['packages'].append({'archive':z.name,'sha256':sha(z),'bytes':z.stat().st_size,'zip_entries':count,'manifest_entries':len(files),'zip_crc':'PASS','manifest_readback':'PASS','manufacturing_status':'FABRICATION_HOLD'})
idx['latest_lamp_safe_firmware_A40']={'source':'../lamp_IR_target_A40/firmware/固件/lamp_target','binary_sha256':build['sha256_bin'],'board_pair':'A39 PCA lamp_safety_evt_a3_IR_PCB','IR_GPIO_binding':'PC15 bit15 clock/config-checked; candidate10ms assertion/5ms max gap','A35_policy_linked':True,'led_output_enabled':False,'physical_test_release':False}
idx['latest_lamp_IR_hardware_A39']['physical_IR_firmware_bound']=True;idx['latest_lamp_IR_hardware_A39']['bound_firmware_candidate']='../lamp_IR_target_A40/firmware/固件/lamp_target'
idx['latest_lamp_IR_policy_A35']['IR_GPIO_binding']='A40 PC15 conditional clock/config-checked binding; not physically validated';idx['latest_lamp_IR_policy_A35']['linked_candidate']='../lamp_IR_target_A40/firmware/固件/lamp_target'
idx['lamp_clean_build']={'report':'../lamp_IR_target_A40/firmware/固件/lamp_target/build/build_verification.json','binary_sha256':build['sha256_bin'],'physical_validation':'NOT_RUN_NO_SAMPLE','led_output_enabled':False,'scope':'A40 current PCA branch hard-off image with actual PC15 IR sampler; other safety inputs incomplete'}
idx['manufacturing_exclusions']['lamp_A40']='PC15 IR target binding tested locally only; LSE must be off and GPIO/config owned by this firmware. Sensor, ADC, driver/output closure, factory DFM and physical validation incomplete; fabrication remains HOLD.'
ip.write_text(json.dumps(idx,ensure_ascii=False,indent=2)+'\n');print(json.dumps(idx['packages'][-1],indent=2))
