This commit is contained in:
david rice
2026-04-20 10:34:42 +01:00
parent 118d8ad380
commit e718a93667
8 changed files with 735 additions and 77 deletions

View File

@@ -21,6 +21,14 @@ V18_TIMEBASE = 1e-6 # s/div — 1 µs/div = 10 µs total window
V18_TRIG_LEVEL = 1.76 # V — falling-edge trigger on supply droop > 40 mV
TRIG_TIMEOUT_S = 15.0 # s — wait this long for Rigol to capture after arming
# CH2 — SN65DSI83 IRQ pin (CMOS output, active HIGH, high-impedance when IRQ_EN=0)
# CSR 0xE0.0 IRQ_EN=0 (default): pin is high-impedance → reads ~0 V (no pull on PCB, normal)
# IRQ_EN=1, no error: driven LOW (~0 V)
# IRQ_EN=1, error asserted: driven HIGH (~1.25 V min per VOH spec)
# No pull-up required — CMOS output drives both high and low.
INT_V_SCALE = 0.2 # V/div — shows 0~1.8 V range clearly
INT_V_OFFSET = -0.9 # V — centres display on 0.9 V midpoint
rigol: vxi11.Instrument | None = None
@@ -62,19 +70,27 @@ def is_connected() -> bool:
def configure():
"""
Configure Rigol for 1.8 V supply monitoring.
Configure Rigol CH1 for 1.8 V supply monitoring and CH2 for SN65DSI83 INTB pin.
AUTO trigger sweep: if no droop occurs, scope still captures on timeout
so we always get a supply snapshot even when the rail is healthy.
"""
rigol.write(":STOP")
time.sleep(0.2)
# CH1 — 1.8 V supply rail
rigol.write(":CHANnel1:DISPlay 1")
rigol.write(":CHANnel2:DISPlay 0")
rigol.write(":CHANnel1:COUPling DC")
rigol.write(":CHANnel1:PROBe 10")
rigol.write(f":CHANnel1:SCALe {V18_SCALE:.3f}")
rigol.write(f":CHANnel1:OFFSet {V18_OFFSET:.3f}")
# CH2 — SN65DSI83 INTB pin (active-low open-drain, external 10 kΩ pull-up to 1.8 V required)
rigol.write(":CHANnel2:DISPlay 1")
rigol.write(":CHANnel2:COUPling DC")
rigol.write(":CHANnel2:PROBe 1") # direct probe, no attenuation
rigol.write(f":CHANnel2:SCALe {INT_V_SCALE:.3f}")
rigol.write(f":CHANnel2:OFFSet {INT_V_OFFSET:.3f}")
rigol.write(f":TIMebase:MAIN:SCALe {V18_TIMEBASE:.2E}")
rigol.write(":TRIGger:MODE EDGE")
rigol.write(":TRIGger:EDGe:SOURce CHANnel1")
@@ -85,7 +101,7 @@ def configure():
rigol.write(":RUN") # start acquiring immediately after configure
time.sleep(0.2)
print(f"[RIGOL] Configured: 1.8 V rail, {int(V18_TIMEBASE*1e6)} µs/div, "
print(f"[RIGOL] Configured: CH1=1.8 V rail, CH2=INTB pin, {int(V18_TIMEBASE*1e6)} µs/div, "
f"trigger <{V18_TRIG_LEVEL} V falling (AUTO sweep, running)")
@@ -117,24 +133,21 @@ def wait_captured(timeout_s: float = TRIG_TIMEOUT_S) -> bool:
return False
def read_waveform_csv(path: Path) -> int:
def _read_channel_csv(channel: str, path: Path, stop_first: bool = True) -> int:
"""
Read Ch1 waveform from Rigol over SCPI and write to CSV.
Sends :STOP first to ensure acquisition is complete before reading —
this is reliable regardless of trigger/status state.
Read one Rigol channel waveform over SCPI and write to CSV.
stop_first=False skips :STOP when the scope was already stopped by a prior read.
Returns the number of samples written, or 0 on error.
"""
try:
rigol.write(":STOP")
time.sleep(0.3)
rigol.write(":WAVeform:SOURce CHANnel1")
if stop_first:
rigol.write(":STOP")
time.sleep(0.3)
rigol.write(f":WAVeform:SOURce {channel}")
rigol.write(":WAVeform:FORMat ASC") # Rigol DS1000Z uses ASC not ASCII
time.sleep(0.1)
except Exception as e:
print(f"[RIGOL] Waveform setup error: {e}")
print(f"[RIGOL] {channel} waveform setup error: {e}")
return 0
try:
@@ -145,7 +158,7 @@ def read_waveform_csv(path: Path) -> int:
x_orig = float(preamble[5])
x_ref = float(preamble[6])
except Exception as e:
print(f"[RIGOL] Preamble error: {e}")
print(f"[RIGOL] {channel} preamble error: {e}")
return 0
try:
@@ -158,11 +171,11 @@ def read_waveform_csv(path: Path) -> int:
vals = [float(v) for v in raw.split(",") if v.strip()]
except Exception as e:
print(f"[RIGOL] Data read error: {e}")
print(f"[RIGOL] {channel} data read error: {e}")
return 0
if not vals:
print("[RIGOL] No samples parsed — check scope channel and format settings")
print(f"[RIGOL] {channel}: no samples parsed — check channel and format settings")
return 0
try:
@@ -175,5 +188,18 @@ def read_waveform_csv(path: Path) -> int:
writer.writerow([f"{t:.9f}", f"{v:.6f}"])
return len(vals)
except Exception as e:
print(f"[RIGOL] CSV write error: {e}")
print(f"[RIGOL] {channel} CSV write error: {e}")
return 0
def read_waveform_csv(path: Path) -> int:
"""Read CH1 (1.8 V supply) waveform from Rigol and write to CSV."""
return _read_channel_csv("CHANnel1", path, stop_first=True)
def read_int_csv(path: Path) -> int:
"""
Read CH2 (SN65DSI83 INTB pin) waveform from Rigol and write to CSV.
Must be called after read_waveform_csv() — scope is already stopped.
"""
return _read_channel_csv("CHANnel2", path, stop_first=False)