Contrôlez une alimentation Rigol série DP800 via USB ou Ethernet avec Python, encapsulez-la dans un plug OpenHTF et enregistrez les résultats dans TofuPilot. Ce guide couvre la connexion, le contrôle multi-canaux, les paramètres de protection et un exemple complet de test de production.
Prérequis
- Alimentation Rigol série DP800 (DP821, DP831, DP832 ou DP832A)
- Python 3.8+
- Câble USB-B ou connexion réseau vers l'alimentation
pip install pyvisa pyvisa-py tofupilot openhtfÉtape 1 : Se connecter à l'instrument
Les instruments Rigol DP800 exposent une interface VISA via USB-TMC et TCP/IP.
| Connexion | Exemple d'adresse VISA |
|---|---|
| USB-TMC | USB0::0x1AB1::0x0E11::DP8C224200001::INSTR |
| Ethernet (VXI-11) | TCPIP0::192.168.1.50::inst0::INSTR |
| Ethernet (socket brut) | TCPIP0::192.168.1.50::5555::SOCKET |
import pyvisa
rm = pyvisa.ResourceManager()
psu = rm.open_resource("USB0::0x1AB1::0x0E11::DP8C224200001::INSTR")
psu.timeout = 5000
psu.write_termination = "
"
psu.read_termination = "
"
print(psu.query("*IDN?"))
# Rigol Technologies,DP832,DP8C224200001,00.01.14Étape 2 : Comprendre la syntaxe SCPI Rigol
La syntaxe SCPI des Rigol DP800 diffère de Keysight sur plusieurs points importants.
| Opération | Rigol DP800 | Keysight E36xx |
|---|---|---|
| Sélectionner le canal | :INST CH1 | :INST:SEL OUT1 |
| Définir la tension | :VOLT 5.0 | :VOLT 5.0 |
| Définir le courant limite | :CURR 1.0 | :CURR 1.0 |
| Activer la sortie | :OUTP CH1,ON | :OUTP ON |
| Lire la tension | :MEAS:VOLT? CH1 | :MEAS:VOLT? |
| Lire le courant | :MEAS:CURR? CH1 | :MEAS:CURR? |
| Activer OVP | :OUTP:OVP CH1,ON | :VOLT:PROT:STAT ON |
| Niveau OVP | :OUTP:OVP:VAL CH1,5.5 | :VOLT:PROT:LEV 5.5 |
Étape 3 : Contrôler la tension, le courant et la sortie
import pyvisa
rm = pyvisa.ResourceManager()
psu = rm.open_resource("USB0::0x1AB1::0x0E11::DP8C224200001::INSTR")
psu.timeout = 5000
def set_channel(psu, channel: int, voltage: float, current: float) -> None:
psu.write(f":INST CH{channel}")
psu.write(f":VOLT {voltage:.3f}")
psu.write(f":CURR {current:.3f}")
def enable_output(psu, channel: int) -> None:
psu.write(f":OUTP CH{channel},ON")
def disable_output(psu, channel: int) -> None:
psu.write(f":OUTP CH{channel},OFF")
def measure_voltage(psu, channel: int) -> float:
return float(psu.query(f":MEAS:VOLT? CH{channel}"))
def measure_current(psu, channel: int) -> float:
return float(psu.query(f":MEAS:CURR? CH{channel}"))
# Exemple : alimenter un rail 3,3 V à 500 mA maximum
set_channel(psu, 1, voltage=3.3, current=0.5)
enable_output(psu, 1)
vout = measure_voltage(psu, 1)
iout = measure_current(psu, 1)
print(f"CH1: {vout:.3f} V, {iout:.4f} A")Étape 4 : Contrôle multi-canaux (DP832)
Le DP832 dispose de trois canaux indépendants. Configurez tous les canaux avant d'activer une sortie.
import pyvisa
import time
rm = pyvisa.ResourceManager()
psu = rm.open_resource("USB0::0x1AB1::0x0E11::DP8C224200001::INSTR")
psu.timeout = 5000
CHANNELS = {
1: {"voltage": 5.0, "current": 2.0}, # Rail principal 5 V
2: {"voltage": 3.3, "current": 1.0}, # Rail logique
3: {"voltage": 12.0, "current": 0.5}, # Rail analogique
}
for ch, cfg in CHANNELS.items():
psu.write(f":INST CH{ch}")
psu.write(f":VOLT {cfg['voltage']:.3f}")
psu.write(f":CURR {cfg['current']:.3f}")
for ch in CHANNELS:
psu.write(f":OUTP CH{ch},ON")
time.sleep(0.1)
for ch in CHANNELS:
v = float(psu.query(f":MEAS:VOLT? CH{ch}"))
i = float(psu.query(f":MEAS:CURR? CH{ch}"))
print(f"CH{ch}: {v:.3f} V {i:.4f} A")Étape 5 : Configurer la protection OVP et OCP
Configurez toujours la protection avant d'activer la sortie dans un environnement de production.
def configure_protection(psu, channel: int, ovp_volts: float, ocp_amps: float) -> None:
psu.write(f":OUTP:OVP:VAL CH{channel},{ovp_volts:.3f}")
psu.write(f":OUTP:OVP CH{channel},ON")
psu.write(f":OUTP:OCP:VAL CH{channel},{ocp_amps:.3f}")
psu.write(f":OUTP:OCP CH{channel},ON")
# Rail 5 V : déclenchement à 5,5 V ou 2,5 A
configure_protection(psu, 1, ovp_volts=5.5, ocp_amps=2.5)Les seuils de déclenchement OVP et OCP doivent être réglés 10 à 15 % au-dessus des valeurs nominales pour éviter les déclenchements intempestifs tout en détectant rapidement les défauts.
Étape 6 : Créer un plug OpenHTF
import time
import pyvisa
import openhtf as htf
class RigolDP800(htf.plugs.BasePlug):
"""Plug OpenHTF pour les alimentations Rigol série DP800."""
VISA_ADDRESS = "USB0::0x1AB1::0x0E11::DP8C224200001::INSTR"
def setUp(self):
self._rm = pyvisa.ResourceManager()
self._psu = self._rm.open_resource(self.VISA_ADDRESS)
self._psu.timeout = 5000
self._psu.write_termination = "
"
self._psu.read_termination = "
"
def tearDown(self):
for ch in (1, 2, 3):
try:
self._psu.write(f":OUTP CH{ch},OFF")
except Exception:
pass
if self._psu:
self._psu.close()
self._rm.close()
def configure(self, channel: int, voltage: float, current: float):
self._psu.write(f":INST CH{channel}")
self._psu.write(f":VOLT {voltage:.3f}")
self._psu.write(f":CURR {current:.3f}")
def set_protection(self, channel: int, ovp: float, ocp: float):
self._psu.write(f":OUTP:OVP:VAL CH{channel},{ovp:.3f}")
self._psu.write(f":OUTP:OVP CH{channel},ON")
self._psu.write(f":OUTP:OCP:VAL CH{channel},{ocp:.3f}")
self._psu.write(f":OUTP:OCP CH{channel},ON")
def enable(self, channel: int):
self._psu.write(f":OUTP CH{channel},ON")
def disable(self, channel: int):
self._psu.write(f":OUTP CH{channel},OFF")
def measure_voltage(self, channel: int) -> float:
return float(self._psu.query(f":MEAS:VOLT? CH{channel}"))
def measure_current(self, channel: int) -> float:
return float(self._psu.query(f":MEAS:CURR? CH{channel}"))Étape 7 : Écrire le test de production avec TofuPilot
import time
import openhtf as htf
from openhtf.util import units
from tofupilot.openhtf import TofuPilot
from plugs.rigol_dp800 import RigolDP800
@htf.plug(psu=RigolDP800)
def power_on_dut(test, psu):
"""Configure les rails, active la protection et alimente le DUT."""
psu.configure(channel=1, voltage=5.0, current=2.0)
psu.set_protection(channel=1, ovp=5.5, ocp=2.5)
psu.configure(channel=2, voltage=3.3, current=1.0)
psu.set_protection(channel=2, ovp=3.7, ocp=1.2)
psu.enable(channel=1)
psu.enable(channel=2)
time.sleep(0.5)
@htf.plug(psu=RigolDP800)
@htf.measures(
htf.Measurement("ch1_voltage")
.in_range(minimum=4.85, maximum=5.15)
.with_units(units.VOLT),
htf.Measurement("ch1_current")
.in_range(maximum=2.0)
.with_units(units.AMPERE),
htf.Measurement("ch2_voltage")
.in_range(minimum=3.20, maximum=3.40)
.with_units(units.VOLT),
htf.Measurement("ch2_current")
.in_range(maximum=1.0)
.with_units(units.AMPERE),
)
def measure_power_rails(test, psu):
"""Lit et valide tous les rails d'alimentation sous charge."""
test.measurements.ch1_voltage = psu.measure_voltage(channel=1)
test.measurements.ch1_current = psu.measure_current(channel=1)
test.measurements.ch2_voltage = psu.measure_voltage(channel=2)
test.measurements.ch2_current = psu.measure_current(channel=2)
@htf.plug(psu=RigolDP800)
def power_off_dut(test, psu):
"""Désactive toutes les sorties après le test."""
psu.disable(channel=1)
psu.disable(channel=2)
def main():
test = htf.Test(
power_on_dut,
measure_power_rails,
power_off_dut,
test_name="Power Supply Validation",
)
with TofuPilot(test):
test.execute(test_start=lambda: input("Entrez le numéro de série : ").strip())
if __name__ == "__main__":
main()Dépannage
| Symptôme | Cause probable | Solution |
|---|---|---|
| Instrument non trouvé via USB | Règles udev manquantes (Linux) | Ajouter SUBSYSTEM=="usb", ATTR{idVendor}=="1ab1", MODE="0666" aux règles udev |
VI_ERROR_TMO lors d'une requête | read_termination non défini | Définir `psu.read_termination = " |
| "` | ||
| La sortie déclenche l'OCP immédiatement | L'appel de courant capacitif dépasse la limite | Augmenter la limite OCP de 20 % au-dessus du régime permanent ou monter la tension progressivement |
| Les mesures indiquent 0 V | Canal en mode CC, charge trop lourde | Augmenter la limite de courant ou réduire la charge |
| La connexion Ethernet se coupe | Timeout d'inactivité VXI-11 (~60 s) | Rouvrir la ressource ou envoyer un *IDN? périodique comme keep-alive |
| La commande ne retourne aucune donnée | Utilisation de query() pour une commande set | Utiliser write() pour les commandes qui ne retournent pas de données |