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 pyvisarm = pyvisa.ResourceManager()psu = rm.open_resource("USB0::0x1AB1::0x0E11::DP8C224200001::INSTR")psu.timeout = 5000psu.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 pyvisarm = pyvisa.ResourceManager()psu = rm.open_resource("USB0::0x1AB1::0x0E11::DP8C224200001::INSTR")psu.timeout = 5000def 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 maximumset_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 pyvisaimport timerm = pyvisa.ResourceManager()psu = rm.open_resource("USB0::0x1AB1::0x0E11::DP8C224200001::INSTR")psu.timeout = 5000CHANNELS = { 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 Aconfigure_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 timeimport pyvisaimport openhtf as htfclass 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 timeimport openhtf as htffrom openhtf.util import unitsfrom tofupilot.openhtf import TofuPilotfrom 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 |