# LT8619C I2C 注入器 — BPI-Pico-RP2040 / MicroPython # ============================================================ # 用途: 在 demo 板(LT8619C, 7bit 地址 0x32)的 I2C 总线上择机注入寄存器写入, # 避开 demo 板 MCU 的 ~500ms 轮询: 等一轮活动结束 + 静置 15ms 后快速 # "切bank→写→读回校验", 失败自动重试, 完成后立即释放总线回监听。 # 接线: GP8(物理脚11)=SDA, GP9(物理脚12)=SCL, GND 共地; 不接 VCC, 不加上拉。 # 串口: USB CDC, 命令均十六进制, 一行一回: # w 择机写 + 读回校验, 例: w 60 6E 80 (B通道镜像) # r 择机读, 例: r 60 6E # s 总线边沿计数统计 # ? 帮助 # 返回行以 '=' 开头为正常结果, '!' 为告警/失败。 import machine, time, sys, select, array, micropython SDA, SCL = 8, 9 I2C_ADDR = 0x32 FREQ = 100_000 QUIET_MS = 50 # 静置判定窗口(总线空闲该时长后才注入) ARM_TIMEOUT_MS = 1500 # 武装后等下一轮总线活动的超时(超过则硬闯并告警) RETRY = 3 CAP_EDGES = 8000 # 嗅探最大边沿数(内存 ~40KB) # ---- 嗅探: 紧循环读 GPIO_IN(0xD0000004) 位8/9, 变化即记录时间戳(TIMERAWL, us) ---- # val 编码: bit0=SDA(GP8), bit1=SCL(GP9) # 性能: 字节码 machine.mem32 ~12us/读(混叠), viper 裸指针 ~0.27us/读(~3MHz 采样) —— 必须用 viper def _cap_edges_bc(ts, vv, max_n, timeout_us): n = 0 last = (machine.mem32[0xD0000004] >> 8) & 3 tend = machine.mem32[0x40054028] + timeout_us while n < max_n: v = (machine.mem32[0xD0000004] >> 8) & 3 if v != last: ts[n] = machine.mem32[0x40054028] vv[n] = v n += 1 last = v elif machine.mem32[0x40054028] >= tend: break return n def _cap_edges_viper(ts, vv, max_n: int, timeout_us: int) -> int: p_gpio = ptr32(0xD0000004) p_timer = ptr32(0x40054028) p_ts = ptr32(ts) p_vv = ptr8(vv) last = int(p_gpio[0]) >> 8 & 3 tend = int(p_timer[0]) + timeout_us n = 0 while n < max_n: v = int(p_gpio[0]) >> 8 & 3 if v != last: p_ts[n] = int(p_timer[0]) p_vv[n] = v n += 1 last = v elif int(p_timer[0]) >= tend: break return n _cap_edges = _cap_edges_bc try: _cap_edges_viper(array.array('i', bytes(4)), array.array('b', bytes(1)), 0, 0) _cap_edges = _cap_edges_viper # viper 可用则用(~3MHz 采样) except Exception: pass # 退回字节码(仅够解码 <15kHz 的总线) last_edge_us = time.ticks_us() edges = 0 LED = machine.Pin(25, machine.Pin.OUT) # 板载 LED: 常亮 = 注入器监听中 def _on_edge(_pin): global last_edge_us, edges last_edge_us = time.ticks_us() edges += 1 def bus_listen(): """两脚浮空输入 + 双沿中断(只听不驱)""" for p in (SDA, SCL): pin = machine.Pin(p, machine.Pin.IN, None) pin.irq(trigger=machine.Pin.IRQ_RISING | machine.Pin.IRQ_FALLING, handler=_on_edge) def wait_window(): """等一轮总线活动结束再静置 QUIET_MS; 返回 True=正常等到, False=超时硬闯""" saw = edges t0 = time.ticks_ms() while edges == saw: # 等下一轮活动出现(demo ~500ms 一轮) if time.ticks_diff(time.ticks_ms(), t0) > ARM_TIMEOUT_MS: print('! 无总线活动 %.0fms, 直接注入' % ARM_TIMEOUT_MS) return False time.sleep_ms(2) t0 = time.ticks_ms() while True: # 等静置 if time.ticks_diff(time.ticks_us(), last_edge_us) >= QUIET_MS * 1000: return True if time.ticks_diff(time.ticks_ms(), t0) > 2000: print('! 静置超时, 直接注入') return False time.sleep_us(200) def inject(bank, reg, val=None): """注入并校验; val=None 只读。返回 (ok, readback)""" for attempt in range(1, RETRY + 1): wait_window() i2c = machine.I2C(0, sda=machine.Pin(SDA), scl=machine.Pin(SCL), freq=FREQ) try: if val is not None: i2c.writeto(I2C_ADDR, bytes([0xFF, bank])) # 切 bank i2c.writeto(I2C_ADDR, bytes([reg, val])) # 写寄存器 rb = i2c.readfrom_mem(I2C_ADDR, reg, 1)[0] # repeated-start 读回 except OSError as e: print('! 第%d次总线错误 %s' % (attempt, e)) rb = None finally: bus_listen() # 立即释放总线 if rb is None: continue if val is None or rb == val: return True, rb print('! 第%d次校验不符: 写%02X 读%02X' % (attempt, val, rb)) return False, None def main(): bus_listen() for _ in range(3): # 上电自检: 快闪 3 次 LED.on(); time.sleep_ms(80); LED.off(); time.sleep_ms(80) LED.on() # 常亮 = 监听中 print('LT8619C 注入器就绪 GP8=SDA GP9=SCL %dkHz addr 0x%02X' % (FREQ // 1000, I2C_ADDR)) poll = select.poll() poll.register(sys.stdin, select.POLLIN) buf = b'' while True: try: if poll.poll(50): buf += sys.stdin.read(1) if buf[-1:] not in (b'\n', b'\r'): continue line = buf.strip().decode() buf = b'' parts = line.split() if not parts: continue cmd = parts[0].lower() if cmd == 'w' and len(parts) == 4: bank, reg, val = (int(x, 16) for x in parts[1:]) ok, rb = inject(bank, reg, val) print('= w %02X %02X %02X : %s 读回=%s' % (bank, reg, val, 'OK' if ok else 'FAIL', '%02X' % rb if rb is not None else '--')) elif cmd == 'r' and len(parts) == 3: bank, reg = (int(x, 16) for x in parts[1:]) ok, rb = inject(bank, reg) print('= r %02X %02X : %s %s' % (bank, reg, 'OK' if ok else 'FAIL', '%02X' % rb if rb is not None else '--')) elif cmd == 's': print('= 边沿计数 %d' % edges) elif cmd == 'c': ms = int(parts[1]) if len(parts) > 1 else 700 for p in (SDA, SCL): # 捕获期间暂停监听中断 machine.Pin(p, machine.Pin.IN, None).irq(handler=None) ts = array.array('i', bytes(4 * CAP_EDGES)) vv = array.array('b', bytes(CAP_EDGES)) n = _cap_edges(ts, vv, CAP_EDGES, ms * 1000) bus_listen() print('= capture %d edges / %d ms' % (n, ms)) t0 = ts[0] if n else 0 out = [] for i in range(n): out.append('%d %d' % (ts[i] - t0, vv[i])) if len(out) >= 100: print('C ' + '\nC '.join(out)) out = [] if out: print('C ' + '\nC '.join(out)) print('C.') else: print('? w bank reg val | r bank reg | s | c [ms]') except KeyboardInterrupt: break print('退出') main()