feat(I2C-Inject): 归档 RP2040 固件到 mcu-rp2040 并补齐说明
This commit is contained in:
@@ -0,0 +1,192 @@
|
||||
# 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 <bank> <reg> <val> 择机写 + 读回校验, 例: w 60 6E 80 (B通道镜像)
|
||||
# r <bank> <reg> 择机读, 例: 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()
|
||||
Reference in New Issue
Block a user