Test Types & Methods

Test fonctionnel de PCBA : le guide complet

Guide complet du test fonctionnel de PCBA (FCT) avec Python, OpenHTF et TofuPilot : rails d'alimentation, communication, courant consommé et traçabilité.

JJulien Buteau
intermediate15 min de lecture14 mars 2026

Le test fonctionnel de PCBA (FCT) vérifie qu'une carte assemblée fonctionne comme prévu. C'est le dernier test avant l'expédition. Contrairement à l'ICT (test in-circuit) qui vérifie les composants individuels, le FCT teste la carte comme un système : rails d'alimentation, interfaces de communication, firmware et courant consommé. Ce guide vous montre comment construire un FCT de production avec Python, OpenHTF et TofuPilot.

Méthodes de test PCBA comparées

MéthodeCe qu'elle testeQuand l'utiliserÉquipement
ICT (Test In-Circuit)Composants individuels : résistances, condensateurs, CIGrands volumes, après refusion SMTLit à clous, testeur ICT dédié
Sonde mobileMême chose que l'ICT, sans fixationFaibles volumes, prototypesMachine à sondes mobiles
FCT (Test Fonctionnel)Comportement au niveau carte : alimentation, communication, firmwareChaque carte avant expéditionFixation + instruments + script de test
Boundary Scan (JTAG)Connexions CI, logique numériqueCartes BGA complexesAdaptateur JTAG
AOI (Inspection Optique Automatisée)Joints de soudure, placement des composantsAprès refusion, avant ICT/FCTMachine AOI

La plupart des lignes de production exécutent l'AOI après refusion, puis le FCT avant le conditionnement. L'ICT est optionnel et rentable uniquement à grands volumes (10K+ cartes/an).

Couverture du FCT

Un FCT typique vérifie ces catégories :

CatégorieQuoi testerMesures typiques
AlimentationTous les rails de tension, courant consomméRails 3.3V, 5V, 1.8V ; courant au repos et en activité
CommunicationUART, SPI, I2C, USB, EthernetRequête de version firmware, réponse d'auto-test
E/S numériquesGPIO, LED, boutonsÉtat LED, réponse bouton, niveaux logiques
AnalogiqueLectures ADC, sortie DACTension calibrée, lectures capteurs
Composants passifsRésistances de pull-up/pull-downMesure de résistance (hors tension)
FirmwareVersion, auto-test, intégrité flashChaîne de version, vérification CRC

Étape 1 : Définir votre fixation de test

Une fixation de test connecte le DUT (dispositif sous test) à vos instruments. Pour le FCT, vous avez typiquement besoin de :

InstrumentObjectifConnexion
Alimentation de bancAlimenter le DUTFiches banane vers la fixation
Multimètre (DMM)Mesurer tension, courant, résistanceSondes de test vers la fixation
Adaptateur UARTCommuniquer avec le firmware du DUTUSB-série vers la fixation
Optionnel : oscilloscopeVérifier l'intégrité des signauxSondes vers les points de test

Étape 2 : Créer les Plugs d'instruments

Chaque instrument obtient un Plug OpenHTF. Le plug gère la configuration et la déconnexion. Les Plugs sont injectés dans les phases de test via le décorateur @htf.plug().

fct/plugs.py
import openhtf as htf
from openhtf.plugs import BasePlug


class PowerSupplyPlug(BasePlug):
    \"\"\"Contrôle de l'alimentation de banc.\"\"\"

    def setUp(self):
        self.output_on = False
        # Remplacer par une vraie connexion à l'instrument (ex. PyVISA)

    def set_voltage(self, channel: int, voltage: float):
        \"\"\"Définir la tension de sortie sur un canal.\"\"\"
        pass  # Remplacer : psu.write(f":INST:SEL CH{channel}"); psu.write(f":VOLT {voltage}")

    def set_current_limit(self, channel: int, current: float):
        \"\"\"Définir la limite de courant sur un canal.\"\"\"
        pass  # Remplacer : psu.write(f":CURR {current}")

    def enable_output(self):
        self.output_on = True
        # Remplacer : psu.write(":OUTP ON")

    def disable_output(self):
        self.output_on = False
        # Remplacer : psu.write(":OUTP OFF")

    def tearDown(self):
        self.disable_output()


class MultimeterPlug(BasePlug):
    \"\"\"DMM pour mesures de tension, courant et résistance.\"\"\"

    def setUp(self):
        pass  # Remplacer par une connexion PyVISA

    def measure_voltage(self) -> float:
        return 3.31  # Remplacer : float(dmm.query(":MEAS:VOLT:DC?"))

    def measure_current(self) -> float:
        return 0.12  # Remplacer : float(dmm.query(":MEAS:CURR:DC?"))

    def measure_resistance(self) -> float:
        return 4720.0  # Remplacer : float(dmm.query(":MEAS:RES?"))

    def tearDown(self):
        pass


class UartPlug(BasePlug):
    \"\"\"Interface UART pour la communication avec le DUT.\"\"\"

    def setUp(self):
        pass  # Remplacer : serial.Serial("/dev/ttyUSB0", 115200)

    def send_command(self, cmd: str) -> str:
        if cmd == "AT+VERSION?":
            return "2.1.0"  # Remplacer par une vraie lecture série
        if cmd == "AT+STATUS?":
            return "OK"
        return ""

    def tearDown(self):
        pass

Étape 3 : Écrire le test des rails d'alimentation

Cette phase alimente le DUT et mesure tous les rails de tension.

fct/test_power.py
import openhtf as htf
from openhtf.util import units


@htf.measures(
    htf.Measurement("rail_3v3")
    .in_range(3.2, 3.4)
    .with_units(units.VOLT)
    .doc("Rail d'alimentation 3.3V"),
    htf.Measurement("rail_5v0")
    .in_range(4.8, 5.2)
    .with_units(units.VOLT)
    .doc("Rail d'alimentation 5.0V"),
    htf.Measurement("rail_1v8")
    .in_range(1.7, 1.9)
    .with_units(units.VOLT)
    .doc("Rail cœur 1.8V"),
)
@htf.plug(psu=PowerSupplyPlug, dmm=MultimeterPlug)
def test_power_rails(test, psu, dmm):
    \"\"\"Alimenter le DUT et vérifier tous les rails de tension.\"\"\"
    psu.set_voltage(1, 12.0)
    psu.set_current_limit(1, 1.0)
    psu.enable_output()

    test.measurements.rail_3v3 = dmm.measure_voltage()
    test.measurements.rail_5v0 = dmm.measure_voltage()
    test.measurements.rail_1v8 = dmm.measure_voltage()

Étape 4 : Écrire le test de communication

Vérifier que le firmware du DUT répond correctement via UART.

fct/test_comms.py
import openhtf as htf


@htf.measures(
    htf.Measurement("firmware_version")
    .equals("2.1.0")
    .doc("Chaîne de version du firmware"),
    htf.Measurement("self_test_status")
    .equals("OK")
    .doc("Résultat de l'auto-test de la carte"),
)
@htf.plug(uart=UartPlug)
def test_communication(test, uart):
    \"\"\"Interroger la version du firmware et le statut de l'auto-test.\"\"\"
    test.measurements.firmware_version = uart.send_command("AT+VERSION?")
    test.measurements.self_test_status = uart.send_command("AT+STATUS?")

Étape 5 : Écrire le test de courant consommé

Un courant excessif indique généralement un court-circuit ou un composant endommagé.

fct/test_current.py
import openhtf as htf
from openhtf.util import units


@htf.measures(
    htf.Measurement("idle_current")
    .in_range(0.05, 0.20)
    .with_units(units.AMPERE)
    .doc("Courant consommé au repos par la carte"),
)
@htf.plug(dmm=MultimeterPlug)
def test_current_draw(test, dmm):
    \"\"\"Mesurer le courant consommé au repos.\"\"\"
    test.measurements.idle_current = dmm.measure_current()

Étape 6 : Écrire le test des composants passifs

Vérifier les résistances de pull-up avec le DUT hors tension. Des valeurs de résistance incorrectes provoquent des erreurs de communication intermittentes difficiles à détecter autrement.

fct/test_passives.py
import openhtf as htf
from openhtf.util import units


@htf.measures(
    htf.Measurement("i2c_pullup")
    .in_range(4500, 5100)
    .with_units(units.OHM)
    .doc("Résistance de pull-up I2C SDA"),
)
@htf.plug(dmm=MultimeterPlug)
def test_pullup_resistors(test, dmm):
    \"\"\"Vérifier les valeurs des résistances de pull-up I2C (hors tension).\"\"\"
    test.measurements.i2c_pullup = dmm.measure_resistance()

Étape 7 : Assembler et exécuter

Connectez toutes les phases dans un test avec TofuPilot pour la traçabilité de production.

fct/main.py
import openhtf as htf
from tofupilot.openhtf import TofuPilot


def main():
    test = htf.Test(
        test_power_rails,
        test_communication,
        test_current_draw,
        test_pullup_resistors,
        procedure_id="PCBA-FCT-001",
        part_number="PCBA-200",
    )
    with TofuPilot(test):
        test.execute(test_start=lambda: input("Scanner le numéro de série : "))


if __name__ == "__main__":
    main()

Chaque mesure est envoyée à TofuPilot avec son nom, sa valeur, ses limites, ses unités et son statut de réussite/échec. Vous obtenez FPY, Cpk et graphiques de contrôle par mesure sans code supplémentaire.

Checklist de conception pour la testabilité (DFT)

Une bonne couverture FCT commence dès la conception du PCB. Suivez ces recommandations :

Règle DFTPourquoiImpact sur le FCT
Ajouter des points de test sur tous les rails de tensionAccès DMM sans sonder les CIMesure directe de tension
Sortir UART/SPI/I2C sur un connecteurTest de communication sans lit à clousVérification du firmware
Ajouter une LED de mise sous tensionVérification visuelle rapideMesure booléenne
Inclure un auto-test dans le firmwareLa carte peut vérifier ses propres périphériquesUne seule commande valide plusieurs sous-systèmes
Étiqueter les points de test sur le sérigraphieLes opérateurs peuvent sonder manuellement si nécessaireRéduit la complexité de la fixation
Router les points de test en bord de carteAccès par pointes pogo dans la fixationConstruction de fixation plus rapide

Modes de défaillance FCT courants

DéfaillanceCause typiqueComment la détecter
Rail de tension hors spécificationMauvaise valeur de résistance dans le diviseur du régulateur, joint de soudure froidMesurer la tension du rail avec des limites
Courant consommé excessifPont de soudure, CI endommagéMesurer le courant au repos et en activité
Timeout de communicationPull-up manquant, mauvais baud rate, CI non programméInterroger le firmware, vérifier le temps de réponse
Mauvaise version firmwareErreur de programmation, mauvais binaireInterroger la chaîne de version, comparer avec l'attendu
Défaillance intermittenteJoint de soudure marginal, connecteur desserréExécuter le test plusieurs fois, vérifier la variance des mesures

Plus de guides

Mettez ce guide en pratique