121 lines
4.8 KiB
Python
121 lines
4.8 KiB
Python
#!/usr/bin/env python3
|
|
"""Hardware diagnostics for the RM3100 / CH347 link.
|
|
|
|
Walks the signal chain from USB inwards and stops at the first thing that is
|
|
broken, so a failure points at a specific wire rather than "it doesn't work":
|
|
|
|
adapter -> I2C bus -> sensor identity -> registers -> analog section
|
|
|
|
Usage: ./.venv/bin/python diagnose.py
|
|
"""
|
|
|
|
import sys
|
|
import time
|
|
|
|
import ch347
|
|
import rm3100
|
|
|
|
# BIST with STE=1, BW=11 (4 sleep oscillation cycles), BP=11 (4 LR periods):
|
|
# the most forgiving self-test settings (manual Tables 5-6, 5-7).
|
|
BIST_RUN = 0x8F
|
|
|
|
|
|
def check(label, ok, detail=""):
|
|
print(f" [{'OK ' if ok else 'FAIL'}] {label}" + (f" -- {detail}" if detail else ""))
|
|
return ok
|
|
|
|
|
|
def main():
|
|
print("1. USB adapter")
|
|
try:
|
|
bus = ch347.CH347I2C()
|
|
except IOError as exc:
|
|
check("open CH347", False, str(exc))
|
|
print("\n-> Run ./setup.sh, or check the adapter is plugged in and in Mode 1.")
|
|
return 1
|
|
check("open CH347 and set I2C speed", True, "interface 2 claimed, 100 kHz")
|
|
|
|
with bus:
|
|
print("\n2. I2C bus")
|
|
found = bus.scan()
|
|
if not check("devices respond", bool(found),
|
|
", ".join(f"0x{a:02x}" for a in found) if found else "bus is silent"):
|
|
print("\n-> Nothing ACKs. In order of likelihood:")
|
|
print(" - I2CEN not tied HIGH, so the chip is in SPI mode (most common)")
|
|
print(" - SDA/SCL swapped")
|
|
print(" - missing pull-up resistors on SDA and SCL")
|
|
print(" - module not powered")
|
|
return 1
|
|
|
|
candidates = [a for a in found if a in rm3100.RM3100.ADDRESSES]
|
|
if not check("an address in the RM3100 range 0x20-0x23", bool(candidates),
|
|
", ".join(f"0x{a:02x}" for a in candidates) if candidates
|
|
else "responders are not RM3100 addresses"):
|
|
return 1
|
|
|
|
sensor = rm3100.RM3100(bus, candidates[0])
|
|
print(f"\n3. Sensor identity (at 0x{sensor.address:02x})")
|
|
revid = sensor.revid()
|
|
check("REVID", revid == rm3100.EXPECTED_REVID,
|
|
f"0x{revid:02x}" + ("" if revid == rm3100.EXPECTED_REVID
|
|
else f", expected 0x{rm3100.EXPECTED_REVID:02x}"))
|
|
|
|
print("\n4. Register read/write")
|
|
sensor.stop_cmm()
|
|
time.sleep(0.05)
|
|
original = sensor.get_cycle_counts()
|
|
sensor.set_cycle_counts(100)
|
|
readback = sensor.get_cycle_counts()
|
|
ok = readback == (100, 100, 100)
|
|
check("cycle counts write then read back", ok, f"wrote 100, read {readback}")
|
|
sensor.set_cycle_counts(original[0] or rm3100.DEFAULT_CYCLE_COUNT)
|
|
if not ok:
|
|
print("\n-> Registers are unreliable. Try a lower bus speed or shorter wires.")
|
|
return 1
|
|
|
|
print("\n5. Analog section (BIST -- drives the sensor coils)")
|
|
sensor.write_reg(rm3100.REG_BIST, [BIST_RUN])
|
|
sensor.write_reg(rm3100.REG_POLL, [rm3100.POLL_ALL_AXES])
|
|
time.sleep(0.5)
|
|
bist = sensor.read_reg(rm3100.REG_BIST)[0]
|
|
sensor.write_reg(rm3100.REG_BIST, [0x00])
|
|
|
|
axes = {"X": (bist >> 4) & 1, "Y": (bist >> 5) & 1, "Z": (bist >> 6) & 1}
|
|
all_ok = all(axes.values())
|
|
check("LR oscillators", all_ok,
|
|
f"BIST=0x{bist:02x} " + " ".join(f"{k}OK={v}" for k, v in axes.items()))
|
|
if not all_ok:
|
|
print("\n-> The coils are not oscillating, though I2C is fine. Check:")
|
|
if not any(axes.values()):
|
|
print(" - AVDD/AVSS not connected (most common when ALL axes fail).")
|
|
print(" Registers run on DVDD alone, so I2C works while the analog")
|
|
print(" section is dead. Tie AVDD to the same 3V3 rail as DVDD")
|
|
print(" (they must stay within 0.1 V), and AVSS to ground.")
|
|
print(" - REXT timing resistor (33k) missing")
|
|
else:
|
|
print(f" - coil wiring for {', '.join(k for k, v in axes.items() if not v)}")
|
|
return 1
|
|
|
|
print("\n6. Live measurement")
|
|
sensor.configure()
|
|
sensor.set_rate(0x96)
|
|
sensor.start_cmm()
|
|
try:
|
|
ready = sensor.wait_for_data(timeout=2.0)
|
|
if not check("DRDY asserts", ready, "" if ready else "no data within 2 s"):
|
|
return 1
|
|
counts, ut = sensor.read_measurements()
|
|
magnitude = sum(v * v for v in ut) ** 0.5
|
|
check("field magnitude is plausible", 25 <= magnitude <= 65,
|
|
f"{magnitude:.1f} uT (Earth's field is 25-65)")
|
|
print(f" raw {counts}")
|
|
print(f" X {ut[0]:+.3f} Y {ut[1]:+.3f} Z {ut[2]:+.3f} uT")
|
|
finally:
|
|
sensor.stop_cmm()
|
|
|
|
print("\nAll checks passed -- the link is fully working.")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|