Updates
This commit is contained in:
Binary file not shown.
Binary file not shown.
@@ -59,7 +59,13 @@ CLK_LP_LOW_MIN_NS = 300.0
|
|||||||
# On this hardware normal HS = 105–122 mV; confirmed flicker = 14–32 mV (DC / LP-11 recovery).
|
# On this hardware normal HS = 105–122 mV; confirmed flicker = 14–32 mV (DC / LP-11 recovery).
|
||||||
# Captures where LP-01/LP-00 completed normally but the bridge never entered HS mode show
|
# Captures where LP-01/LP-00 completed normally but the bridge never entered HS mode show
|
||||||
# essentially zero amplitude (the burst window is DC LP-11), so lp_low alone cannot detect this.
|
# essentially zero amplitude (the burst window is DC LP-11), so lp_low alone cannot detect this.
|
||||||
HS_BURST_AMPLITUDE_MIN_MV = 50.0 # mV — below this, no real HS burst is present
|
HS_BURST_AMPLITUDE_MIN_MV = 40.0 # mV — below this, no real HS burst is present
|
||||||
|
# Lowered from 50 mV: 48 mV capture (0001) was a false alarm; true flicker (0008) at 34 mV.
|
||||||
|
|
||||||
|
# Mode A minimum amplitude: LP-11-return edge artifacts produce near-zero amplitude in the
|
||||||
|
# burst window (burst is pure LP-low DC between two LP-11 regions). Require ≥ this to
|
||||||
|
# distinguish a genuine weak-HS attempt from a false rolling-std trigger on LP-11 return.
|
||||||
|
HS_MODE_A_MIN_MV = 10.0 # mV
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
@@ -969,13 +975,20 @@ def analyze_lp_file(path: Path) -> "LPMetrics":
|
|||||||
f"(TCLK_PREPARE+TCLK_ZERO minimum) — SN65DSI83 may fail to lock CLK lane"
|
f"(TCLK_PREPARE+TCLK_ZERO minimum) — SN65DSI83 may fail to lock CLK lane"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Flicker suspect: three confirmed failure modes on this hardware:
|
# Flicker suspect: four confirmed failure modes on this hardware:
|
||||||
#
|
#
|
||||||
# A) Normal LP-low (~342–380 ns) → bridge misses SoT → returns to LP-11
|
# A) Normal LP-low (~342–380 ns) → bridge misses SoT → returns to LP-11
|
||||||
# Signature: lp11_to_hs fires at real LP-low end (~347 ns), hs_amplitude ≈ 15–30 mV.
|
# Signature: lp11_to_hs fires at real LP-low end (~347 ns), hs_amplitude ≈ 15–30 mV.
|
||||||
# Guard: lp11_to_hs >= LP_LOW_DUR_MIN_NS prevents DC-content false positives
|
# Guard: lp11_to_hs >= LP_LOW_DUR_MIN_NS prevents DC-content false positives
|
||||||
# where the ~3 ns noise spike fires the gate but HS IS present.
|
# where the ~3 ns noise spike fires the gate but HS IS present.
|
||||||
#
|
#
|
||||||
|
# A2) LP-11 present, HS attempt made but amplitude too weak for rolling-std to fire
|
||||||
|
# Signature: lp11_to_hs is None (rolling-std < HS_OSC_STD_V throughout 500 ns
|
||||||
|
# lookahead), hs_amplitude < 50 mV, LP-11 returns ~500 ns later.
|
||||||
|
# Root cause: marginal VDD_DSI (LP-11 sags to ~1.0 V vs 1.2 V nominal), reducing
|
||||||
|
# HS drive strength below the detection threshold.
|
||||||
|
# Confirmed: capture 0010 (lp11_to_hs=None, amp≈32 mV, LP-11 returned at +513 ns).
|
||||||
|
#
|
||||||
# B) Short LP-low (50–200 ns, vs nominal ~342–380 ns) → marginal SoT timing
|
# B) Short LP-low (50–200 ns, vs nominal ~342–380 ns) → marginal SoT timing
|
||||||
# → HS burst starts but is weak, hs_amplitude ≈ 40–60 mV (vs normal 100–122 mV).
|
# → HS burst starts but is weak, hs_amplitude ≈ 40–60 mV (vs normal 100–122 mV).
|
||||||
# Signature: lp_low anomalously short, lp11_to_hs fires at noise spike (~3 ns).
|
# Signature: lp_low anomalously short, lp11_to_hs fires at noise spike (~3 ns).
|
||||||
@@ -997,8 +1010,16 @@ def analyze_lp_file(path: Path) -> "LPMetrics":
|
|||||||
hs_amplitude_mv is not None
|
hs_amplitude_mv is not None
|
||||||
and hs_amplitude_mv < HS_BURST_AMPLITUDE_MIN_MV
|
and hs_amplitude_mv < HS_BURST_AMPLITUDE_MIN_MV
|
||||||
and (
|
and (
|
||||||
# Mode A: LP-low normal, HS never started (rolling-std confirms it)
|
# Mode A: LP-low normal, rolling-std fired but HS amplitude is sub-threshold.
|
||||||
(lp11_to_hs_ns is not None and lp11_to_hs_ns >= LP_LOW_DUR_MIN_NS)
|
# Require amp ≥ HS_MODE_A_MIN_MV to exclude LP-11-return artifacts: when LP-11
|
||||||
|
# returns after LP-low without any HS attempt the burst window is pure DC ~0 V
|
||||||
|
# (two LP-11 regions straddling a clean LP-low), giving amp ≈ 0–3 mV. A genuine
|
||||||
|
# weak HS attempt leaves measurable oscillations well above this floor.
|
||||||
|
(lp11_to_hs_ns is not None and lp11_to_hs_ns >= LP_LOW_DUR_MIN_NS
|
||||||
|
and hs_amplitude_mv >= HS_MODE_A_MIN_MV)
|
||||||
|
# Mode A2: rolling-std never fired — HS absent or amplitude below HS_OSC_STD_V;
|
||||||
|
# weak oscillations are misclassified as LP-low, masking the true HS failure
|
||||||
|
or lp11_to_hs_ns is None
|
||||||
# Mode B: LP-low anomalously short + low amplitude = marginal HS launch
|
# Mode B: LP-low anomalously short + low amplitude = marginal HS launch
|
||||||
or _lp_low_short
|
or _lp_low_short
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ REGISTER_COMMANDS = [
|
|||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# SN65DSI83 I2C configuration
|
# SN65DSI83 I2C configuration
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
SN65_I2C_BUS = 2 # i2c-2 on i.MX 8M Mini — change if bridge is on a different bus
|
SN65_I2C_BUS = 4 # i2c-4 on this board
|
||||||
SN65_I2C_ADDR = 0x2C # SN65DSI83 fixed 7-bit I2C address
|
SN65_I2C_ADDR = 0x2C # SN65DSI83 fixed 7-bit I2C address
|
||||||
|
|
||||||
# Known Samsung DSIM register names (base 0x32E10000, i.MX 8M Mini)
|
# Known Samsung DSIM register names (base 0x32E10000, i.MX 8M Mini)
|
||||||
@@ -124,25 +124,28 @@ def get_registers():
|
|||||||
}), 200
|
}), 200
|
||||||
|
|
||||||
|
|
||||||
def _i2c_read_byte(bus: int, addr: int, reg: int) -> int | None:
|
def _i2c_read_byte(bus: int, addr: int, reg: int) -> tuple[int | None, str]:
|
||||||
"""Read one byte from an I2C device using i2cget. Returns None on failure."""
|
"""Read one byte via i2cget. Returns (value, "") on success or (None, error_str) on failure."""
|
||||||
try:
|
try:
|
||||||
result = subprocess.run(
|
result = subprocess.run(
|
||||||
["i2cget", "-y", str(bus), f"0x{addr:02x}", f"0x{reg:02x}"],
|
["i2cget", "-y", "-f", str(bus), f"0x{addr:02x}", f"0x{reg:02x}"],
|
||||||
capture_output=True, text=True, timeout=3
|
capture_output=True, text=True, timeout=3
|
||||||
)
|
)
|
||||||
if result.returncode == 0:
|
if result.returncode == 0:
|
||||||
return int(result.stdout.strip(), 16)
|
return int(result.stdout.strip(), 16), ""
|
||||||
except Exception:
|
stderr = result.stderr.strip() or f"exit code {result.returncode}"
|
||||||
pass
|
return None, stderr
|
||||||
return None
|
except FileNotFoundError:
|
||||||
|
return None, "i2cget not found in PATH"
|
||||||
|
except Exception as e:
|
||||||
|
return None, str(e)
|
||||||
|
|
||||||
|
|
||||||
@app.route("/sn65_registers", methods=["GET"])
|
@app.route("/sn65_registers", methods=["GET"])
|
||||||
def get_sn65_registers():
|
def get_sn65_registers():
|
||||||
"""Read SN65DSI83 CSR 0x0A (PLL/CLK status) and 0xE5 (error flags) via I2C."""
|
"""Read SN65DSI83 CSR 0x0A (PLL/CLK status) and 0xE5 (error flags) via I2C."""
|
||||||
csr_0a = _i2c_read_byte(SN65_I2C_BUS, SN65_I2C_ADDR, 0x0A)
|
csr_0a, err_0a = _i2c_read_byte(SN65_I2C_BUS, SN65_I2C_ADDR, 0x0A)
|
||||||
csr_e5 = _i2c_read_byte(SN65_I2C_BUS, SN65_I2C_ADDR, 0xE5)
|
csr_e5, err_e5 = _i2c_read_byte(SN65_I2C_BUS, SN65_I2C_ADDR, 0xE5)
|
||||||
|
|
||||||
regs = {}
|
regs = {}
|
||||||
errors = []
|
errors = []
|
||||||
@@ -151,10 +154,10 @@ def get_sn65_registers():
|
|||||||
regs["csr_0a"] = {
|
regs["csr_0a"] = {
|
||||||
"value": f"0x{csr_0a:02x}",
|
"value": f"0x{csr_0a:02x}",
|
||||||
"pll_lock": bool(csr_0a & 0x80), # bit 7: PLL_EN_STAT (powered + locked)
|
"pll_lock": bool(csr_0a & 0x80), # bit 7: PLL_EN_STAT (powered + locked)
|
||||||
"clk_det": bool(csr_0a & 0x40), # bit 6: high-speed CLK lane detected
|
"clk_det": bool(csr_0a & 0x08), # bit 3: CHA_CLK_DET (HS clock detected)
|
||||||
}
|
}
|
||||||
else:
|
else:
|
||||||
errors.append("CSR 0x0A: i2cget failed")
|
errors.append(f"CSR 0x0A: {err_0a}")
|
||||||
|
|
||||||
if csr_e5 is not None:
|
if csr_e5 is not None:
|
||||||
regs["csr_e5"] = {
|
regs["csr_e5"] = {
|
||||||
@@ -167,7 +170,7 @@ def get_sn65_registers():
|
|||||||
"cha_crc_err": bool(csr_e5 & 0x40),
|
"cha_crc_err": bool(csr_e5 & 0x40),
|
||||||
}
|
}
|
||||||
else:
|
else:
|
||||||
errors.append("CSR 0xE5: i2cget failed")
|
errors.append(f"CSR 0xE5: {err_e5}")
|
||||||
|
|
||||||
return jsonify({
|
return jsonify({
|
||||||
"i2c_bus": SN65_I2C_BUS,
|
"i2c_bus": SN65_I2C_BUS,
|
||||||
|
|||||||
@@ -29,6 +29,10 @@ from datetime import datetime
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
import math
|
import math
|
||||||
|
import os
|
||||||
|
import struct
|
||||||
|
import tempfile
|
||||||
|
import wave
|
||||||
|
|
||||||
import anthropic
|
import anthropic
|
||||||
import vxi11
|
import vxi11
|
||||||
@@ -37,8 +41,7 @@ from dotenv import load_dotenv
|
|||||||
import ai_mgmt
|
import ai_mgmt
|
||||||
import rigol_scope
|
import rigol_scope
|
||||||
from csv_preprocessor import (analyze_lp_file, LPMetrics,
|
from csv_preprocessor import (analyze_lp_file, LPMetrics,
|
||||||
HS_BURST_AMPLITUDE_MIN_MV, FLICKER_LP_LOW_MAX_NS,
|
HS_BURST_AMPLITUDE_MIN_MV, FLICKER_LP_LOW_MAX_NS)
|
||||||
analyze_int_file)
|
|
||||||
|
|
||||||
load_dotenv(Path(__file__).parent / ".env")
|
load_dotenv(Path(__file__).parent / ".env")
|
||||||
|
|
||||||
@@ -565,6 +568,7 @@ def _configure_for_lp():
|
|||||||
scope.write(":TRIGger:EDGE:SOURce CHANnel3")
|
scope.write(":TRIGger:EDGE:SOURce CHANnel3")
|
||||||
scope.write(":TRIGger:EDGE:SLOPe NEGative")
|
scope.write(":TRIGger:EDGE:SLOPe NEGative")
|
||||||
scope.write(f":TRIGger:EDGE:LEVel {LP_TRIG_LEVEL:.3f}")
|
scope.write(f":TRIGger:EDGE:LEVel {LP_TRIG_LEVEL:.3f}")
|
||||||
|
scope.write(":TRIGger:SWEep NORMal") # must wait for real LP-11→LP-01 edge, not auto-fire on HS
|
||||||
time.sleep(0.1)
|
time.sleep(0.1)
|
||||||
|
|
||||||
|
|
||||||
@@ -653,19 +657,6 @@ def _fetch_registers(ts: str, iteration: int) -> None:
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
def _analyze_int_file(ts: str, iteration: int) -> None:
|
|
||||||
"""Print IRQ pin summary and alert if the SN65DSI83 asserted the IRQ line."""
|
|
||||||
path = DATA_DIR / f"{ts}_int_{iteration:04d}.csv"
|
|
||||||
if not path.exists():
|
|
||||||
return
|
|
||||||
try:
|
|
||||||
m = analyze_int_file(path)
|
|
||||||
print(m.summary())
|
|
||||||
if m.int_asserted:
|
|
||||||
print(f"\n *** IRQ ASSERTED: SN65DSI83 flagged a bridge error at "
|
|
||||||
f"capture {iteration:04d} — check CSR 0xE5 for error bits ***\n")
|
|
||||||
except Exception as e:
|
|
||||||
print(f" INT ANALYSIS ERROR: {e}")
|
|
||||||
|
|
||||||
|
|
||||||
def dual_capture(iteration: int) -> str:
|
def dual_capture(iteration: int) -> str:
|
||||||
@@ -699,12 +690,6 @@ def dual_capture(iteration: int) -> str:
|
|||||||
print(f" SAVED: {v18_path.name} ({n} samples)")
|
print(f" SAVED: {v18_path.name} ({n} samples)")
|
||||||
else:
|
else:
|
||||||
print(" RIGOL CH1: waveform read failed — check connection and probe.")
|
print(" RIGOL CH1: waveform read failed — check connection and probe.")
|
||||||
int_path = DATA_DIR / f"{ts}_int_{iteration:04d}.csv"
|
|
||||||
n_int = rigol_scope.read_int_csv(int_path)
|
|
||||||
if n_int:
|
|
||||||
print(f" SAVED: {int_path.name} ({n_int} samples)")
|
|
||||||
else:
|
|
||||||
print(" RIGOL CH2: IRQ read failed.")
|
|
||||||
_restore_hs_config()
|
_restore_hs_config()
|
||||||
try:
|
try:
|
||||||
requests.put(URL, json={"state": "on"}, timeout=1)
|
requests.put(URL, json={"state": "on"}, timeout=1)
|
||||||
@@ -856,6 +841,56 @@ def _append_flicker_log(ts: str, iteration: int, m: LPMetrics) -> None:
|
|||||||
])
|
])
|
||||||
|
|
||||||
|
|
||||||
|
def _play_alarm() -> None:
|
||||||
|
"""Play three short beeps using a generated WAV tone."""
|
||||||
|
sample_rate = 44100
|
||||||
|
freq = 880
|
||||||
|
duration = 0.35
|
||||||
|
n_samples = int(sample_rate * duration)
|
||||||
|
samples = [int(32767 * math.sin(2 * math.pi * freq * i / sample_rate))
|
||||||
|
for i in range(n_samples)]
|
||||||
|
packed = struct.pack(f"<{n_samples}h", *samples)
|
||||||
|
|
||||||
|
tmp = None
|
||||||
|
try:
|
||||||
|
fd, tmp = tempfile.mkstemp(suffix=".wav")
|
||||||
|
os.close(fd)
|
||||||
|
with wave.open(tmp, "w") as w:
|
||||||
|
w.setnchannels(1)
|
||||||
|
w.setsampwidth(2)
|
||||||
|
w.setframerate(sample_rate)
|
||||||
|
w.writeframes(packed)
|
||||||
|
|
||||||
|
# os.system inherits the full shell environment (XDG_RUNTIME_DIR, PULSE_SERVER, etc.)
|
||||||
|
played = False
|
||||||
|
for cmd in (f"aplay -q {tmp}", f"pw-play {tmp}", f"paplay {tmp}"):
|
||||||
|
if os.system(f"{cmd} 2>/dev/null") == 0:
|
||||||
|
played = True
|
||||||
|
for _ in range(2):
|
||||||
|
time.sleep(0.2)
|
||||||
|
os.system(f"{cmd} 2>/dev/null")
|
||||||
|
break
|
||||||
|
|
||||||
|
if not played:
|
||||||
|
try:
|
||||||
|
with open("/dev/tty", "w") as tty:
|
||||||
|
for _ in range(5):
|
||||||
|
tty.write("\a")
|
||||||
|
tty.flush()
|
||||||
|
time.sleep(0.3)
|
||||||
|
except Exception:
|
||||||
|
print("\a" * 5, end="", flush=True)
|
||||||
|
|
||||||
|
except Exception:
|
||||||
|
print("\a" * 5, end="", flush=True)
|
||||||
|
finally:
|
||||||
|
if tmp:
|
||||||
|
try:
|
||||||
|
os.unlink(tmp)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
def _analyze_lp_files(
|
def _analyze_lp_files(
|
||||||
ts: str, iteration: int
|
ts: str, iteration: int
|
||||||
) -> tuple[list[str], list[LPMetrics]]:
|
) -> tuple[list[str], list[LPMetrics]]:
|
||||||
@@ -1340,9 +1375,6 @@ def run_interactive_test() -> None:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f" TRANSFER ERROR: {e}")
|
print(f" TRANSFER ERROR: {e}")
|
||||||
|
|
||||||
# ── IRQ pin analysis ───────────────────────────────────────────
|
|
||||||
_analyze_int_file(ts, iteration)
|
|
||||||
|
|
||||||
# ── Rule-based LP analysis ─────────────────────────────────────
|
# ── Rule-based LP analysis ─────────────────────────────────────
|
||||||
lp_summaries, suspects = _analyze_lp_files(ts, iteration)
|
lp_summaries, suspects = _analyze_lp_files(ts, iteration)
|
||||||
|
|
||||||
@@ -1365,11 +1397,7 @@ def run_interactive_test() -> None:
|
|||||||
|
|
||||||
if claude_flicker:
|
if claude_flicker:
|
||||||
# ── Keep display ON — ask operator ─────────────────────────
|
# ── Keep display ON — ask operator ─────────────────────────
|
||||||
# Play alarm sound once to alert the operator
|
_play_alarm()
|
||||||
subprocess.run(
|
|
||||||
["pw-play", "/usr/share/sounds/freedesktop/stereo/alarm-clock-elapsed.oga"],
|
|
||||||
check=False,
|
|
||||||
)
|
|
||||||
print("\n" + "=" * 64)
|
print("\n" + "=" * 64)
|
||||||
print(" CLAUDE SUSPECTS FLICKER — OBSERVE THE DISPLAY NOW")
|
print(" CLAUDE SUSPECTS FLICKER — OBSERVE THE DISPLAY NOW")
|
||||||
print("=" * 64)
|
print("=" * 64)
|
||||||
|
|||||||
@@ -4,9 +4,9 @@ rigol_scope.py
|
|||||||
Controls the Rigol DS1202Z-E at 192.168.45.5 for 1.8 V supply rail monitoring.
|
Controls the Rigol DS1202Z-E at 192.168.45.5 for 1.8 V supply rail monitoring.
|
||||||
Called from dual_capture() in mipi_test.py during the LP pass.
|
Called from dual_capture() in mipi_test.py during the LP pass.
|
||||||
|
|
||||||
The scope is armed (single trigger) just before the Agilent LP capture.
|
The scope is armed just before the Agilent LP capture.
|
||||||
The LP→HS current step droops the 1.8 V rail, triggering the Rigol.
|
The LP→HS current step droops the 1.8 V rail, triggering the Rigol.
|
||||||
The waveform is then read over SCPI and written directly to the local data/ folder.
|
The CH1 waveform is read over SCPI and written to the local data/ folder.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import csv
|
import csv
|
||||||
@@ -21,14 +21,6 @@ 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
|
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
|
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
|
rigol: vxi11.Instrument | None = None
|
||||||
|
|
||||||
|
|
||||||
@@ -70,7 +62,7 @@ def is_connected() -> bool:
|
|||||||
|
|
||||||
def configure():
|
def configure():
|
||||||
"""
|
"""
|
||||||
Configure Rigol CH1 for 1.8 V supply monitoring and CH2 for SN65DSI83 INTB pin.
|
Configure Rigol CH1 for 1.8 V supply monitoring.
|
||||||
AUTO trigger sweep: if no droop occurs, scope still captures on timeout
|
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.
|
so we always get a supply snapshot even when the rail is healthy.
|
||||||
"""
|
"""
|
||||||
@@ -84,12 +76,7 @@ def configure():
|
|||||||
rigol.write(f":CHANnel1:SCALe {V18_SCALE:.3f}")
|
rigol.write(f":CHANnel1:SCALe {V18_SCALE:.3f}")
|
||||||
rigol.write(f":CHANnel1:OFFSet {V18_OFFSET:.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 0")
|
||||||
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(f":TIMebase:MAIN:SCALe {V18_TIMEBASE:.2E}")
|
||||||
rigol.write(":TRIGger:MODE EDGE")
|
rigol.write(":TRIGger:MODE EDGE")
|
||||||
@@ -101,7 +88,7 @@ def configure():
|
|||||||
rigol.write(":RUN") # start acquiring immediately after configure
|
rigol.write(":RUN") # start acquiring immediately after configure
|
||||||
time.sleep(0.2)
|
time.sleep(0.2)
|
||||||
|
|
||||||
print(f"[RIGOL] Configured: CH1=1.8 V rail, CH2=INTB pin, {int(V18_TIMEBASE*1e6)} µs/div, "
|
print(f"[RIGOL] Configured: CH1=1.8 V rail, {int(V18_TIMEBASE*1e6)} µs/div, "
|
||||||
f"trigger <{V18_TRIG_LEVEL} V falling (AUTO sweep, running)")
|
f"trigger <{V18_TRIG_LEVEL} V falling (AUTO sweep, running)")
|
||||||
|
|
||||||
|
|
||||||
@@ -197,9 +184,3 @@ def read_waveform_csv(path: Path) -> int:
|
|||||||
return _read_channel_csv("CHANnel1", path, stop_first=True)
|
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)
|
|
||||||
|
|||||||
Reference in New Issue
Block a user