Les interruptions Arduino transforment un simple croquis en véritable système réactif. Un capteur déclenche un événement, le microcontrôleur interrompt son travail en cours, exécute une routine dédiée, puis reprend comme si de rien n’était. Bien configuré, ce mécanisme offre une gestion précise des signaux, des capteurs rapides et des tâches temps réel.
En pratique, tout ne se résume pas à un simple attachInterrupt(). Gestion de variables partagées, limites de delay() et millis(), priorités matérielles, contraintes avec Serial ou I2C : chaque détail influe sur la fiabilité du projet. La suite du guide entre dans le concret, avec des exemples, des schémas d’architecture logicielle et des bonnes pratiques applicables sur vos propres montages.
| Élément clé | Résumé |
| Rôle des interruptions ⚡ | Permettent de réagir instantanément à un événement sans bloquer le programme principal. |
| Types d’interruptions | Interruptions matérielles sur des broches dédiées et interruptions internes liées au système. |
| Fonction d’attache | attachInterrupt() permet de déclencher une fonction lors d’un changement d’état du signal. |
| Bonnes pratiques 🛠️ | Maintenir les routines très courtes, éviter les délais et protéger les variables partagées. |
Comprendre le rôle des interruptions sur Arduino et microcontrôleur
Une interruption est un mécanisme matériel qui suspend l’exécution du programme principal pour exécuter immédiatement une fonction spéciale appelée ISR (Interrupt Service Routine). Une fois cette fonction terminée, le microcontrôleur reprend l’exécution là où il s’était arrêté.
Sur Arduino, ce principe s’appuie sur l’architecture du microcontrôleur (AVR pour l’UNO R3, ARM pour l’UNO R4 ou certains Teensy). Chaque événement matériel, comme un changement d’état sur une broche ou un débordement de timer, déclenche un vecteur d’interruption spécifique mappé dans la table interne du microcontrôleur.
Ce fonctionnement apporte une réponse rapide à des signaux extérieurs, sans boucler en permanence dans le code utilisateur. Au lieu de surveiller un bouton dans loop() avec digitalRead(), le programme attend tranquillement pendant que le matériel se charge de signaler le changement d’état dès qu’il survient.
« Une bonne utilisation des interruptions remplace avantageusement les boucles de polling bloquantes et améliore la réactivité globale d’un système embarqué. »

Interruptions matérielles vs interruptions logicielles sur Arduino
Sur Arduino, on distingue plusieurs familles d’interruptions. Certaines sont liées directement au matériel, d’autres à des événements gérés par les bibliothèques et les timers internes. Bien les différencier aide à structurer proprement le code.
Interruptions externes (changement sur une broche)
Les interruptions externes réagissent à un changement de niveau logique sur une broche dédiée. Sur Arduino Uno R3, elles se trouvent par défaut sur les broches 2 et 3 (INT0 et INT1). Elles se configurent avec la fonction attachInterrupt().
Plusieurs modes de déclenchement existent :
- RISING : front montant (0 → 1 logique)
- FALLING : front descendant (1 → 0 logique)
- CHANGE : tout changement d’état
- LOW : niveau bas maintenu
Chaque mode influence la fréquence potentielle de déclenchement et la sensibilité au bruit. Le mode CHANGE réagit le plus souvent, alors qu’un front unique (RISING ou FALLING) réduit le risque de doublons.
Interruptions de broches (Pin Change Interrupt)
En plus des interruptions externes, les microcontrôleurs AVR proposent des Pin Change Interrupt qui surveillent un groupe de broches. Plusieurs broches partagent la même ligne d’interruption, ce qui oblige à tester dans l’ISR quelle broche a réellement changé d’état.
ArduinoArduino et domotique : automatiser sa maison pour pas cherCe mécanisme élargit le nombre de broches « interruptibles », au prix d’un traitement logicielle un peu plus complexe. Pour un clavier matriciel ou plusieurs capteurs logiques, cette approche offre une solution flexible.

Interruptions de timers
Les timers matériels génèrent aussi des interruptions. Ils s’activent à intervalles réguliers (overflow, compare match) et servent à créer un horloge interne précis, à produire des signaux PWM avancés ou à lancer des tâches périodiques.
Sur Arduino Uno R3 cadencé à 16 MHz, chaque timer dispose de ses propres registres. Sur un Teensy 4.x à 600 MHz, la précision et la fréquence maximale augmentent nettement, ce qui rend les interruptions de timers adaptées à des signaux très rapides.
Interruptions logicielles et bibliothèques
Certaines bibliothèques utilisent des interruptions en coulisse. Par exemple, la gestion de millis() s’appuie sur un timer. Des librairies de communication (UART, SPI, parfois I2C) déclenchent des interruptions pour recevoir ou transmettre des données en tâche de fond.
Le développeur n’a pas forcément besoin d’intervenir directement sur ces vecteurs. En revanche, il doit tenir compte de leur existence pour évaluer la charge globale d’interruptions et éviter les conflits de priorité dans des architectures complexes.

Fonctionnement d’une ISR : règles de base et contraintes techniques
La routine d’interruption (ISR) est une fonction spéciale exécutée par le microcontrôleur lorsque l’événement associé survient. Sa signature, sur Arduino, suit en général le modèle suivant :
void ISR_nom() { /* code court et déterministe */ }
Le temps d’installation et de retour d’une ISR crée une latence incompressible. Sur les données disponibles, on relève par exemple un temps d’installation d’environ 3 µs et un temps de retour au programme d’environ 2 µs sur certaines plateformes. Ce budget s’ajoute au temps d’exécution propre du code de l’ISR.
Ce que le compilateur sauvegarde pendant une interruption
Lorsqu’une interruption survient, le microcontrôleur sauvegarde certains registres, l’adresse de retour et parfois des états spécifiques. Cette sauvegarde garantit le retour au programme principal dans l’état attendu.
Plus une ISR manipule de variables locales, d’opérations arithmétiques ou de fonctions imbriquées, plus le compilateur réserve de registres. Le temps d’exécution s’allonge et la réactivité globale diminue. Une approche minimaliste à l’intérieur de l’ISR reste préférable pour préserver un comportement stable.
Instructions à éviter dans une ISR sur Arduino
Plusieurs fonctions Arduino restent indisponibles ou inadaptées dans une ISR, soit parce qu’elles utilisent elles-mêmes des interruptions, soit parce qu’elles bloquent le flot d’exécution.
Parmi les restrictions connues :
- delay() inactif dans une ISR
- millis() inactif dans une ISR
- Serial indisponible dans une ISR (risque de blocage ou corruption)
- Wire (I2C) indisponible dans une ISR
- tone() indisponible dans une ISR
Les protocoles comme SPI, I2C et UART restent fortement déconseillés dans une ISR. Ils s’appuient sur des mécanismes internes qui supportent mal une réentrée forcée, et l’attente de transferts peut bloquer le système.
Configurer une interruption externe avec attachInterrupt()
La fonction attachInterrupt() fournit une interface simple pour lier une ISR à une broche et à un type de déclenchement.
Syntaxe générale :
attachInterrupt(digitalPinToInterrupt(pin), ISR_nom, mode);
Où :
- pin : numéro de broche Arduino
- ISR_nom : nom de la fonction d’interruption
- mode : RISING, FALLING, CHANGE ou LOW
Exemple : interruption sur un bouton poussoir
Objectif : incrémenter un compteur à chaque appui sur un bouton relié à la broche 2 de l’Arduino Uno. Le comptage se fait dans l’ISR, l’affichage dans loop().
Principe général :
- Une variable globale volatile pour le compteur
- Une ISR très courte qui incrémente ce compteur
- Dans loop(), une copie locale du compteur pour l’affichage
Cette structure limite les accès concurrents à la variable partagée et respecte la contrainte sur les sections critiques.
Gestion des rebonds (debounce) avec interruptions
Un bouton mécanique génère souvent des rebonds électriques. Une interruption sur front CHANGE ou RISING déclencherait plusieurs appels d’ISR pour un seul appui.
Une stratégie consiste à combiner l’interruption avec une limitation temporelle via un timer ou une variable de temps en microsecondes enregistrée lors du dernier déclenchement. Le filtrage peut aussi se faire via un circuit RC ou un trigger de Schmitt.
Interruptions et temps réel : limites de réactivité selon la carte
Le temps de réaction d’un système dépend de la fréquence d’horloge et de l’architecture du microcontrôleur. Les cartes suivantes illustrent bien les ordres de grandeur :
| Carte | Fréquence horloge | Conséquence pratique sur les interruptions |
|---|---|---|
| Arduino Uno R3 | 16 MHz | Réactivité suffisante pour capteurs lents à moyens, limitations sur des fréquences d’interruptions très élevées. |
| Arduino Uno R4 | 48 MHz | Temps d’exécution réduit, plus de marge pour gérer des interruptions rapprochées. |
| Teensy 4.0 | 600 MHz | Gestion d’interruptions haute fréquence, utile pour des signaux audio, moteurs rapides ou protocoles personnalisés. |
| Teensy 4.1 | 600 MHz | Performances similaires au 4.0, avec plus d’entrées/sorties et de ressources mémoire pour des architectures complexes. |
Même avec une horloge rapide, la fréquence maximale d’interruptions reste limitée par le temps d’installation, de retour et de traitement interne de chaque ISR. Une succession d’interruptions trop denses finit par monopoliser le processeur.
« Un système noyé sous les interruptions perd en prévisibilité. La qualité d’un design embarqué tient souvent davantage à la hiérarchisation des événements qu’à la simple puissance brute du microcontrôleur. »
Gestion des priorités d’interruptions et masquage temporaire
Sur certaines architectures, les interruptions disposent de priorités matérielles. Sur Arduino Uno, on recense par exemple 25 niveaux de priorité répartis suivant l’ordre défini par la table de vecteurs AVR. Les événements système critiques (reset, erreurs) se situent en haut de la liste, les autres plus bas.
L’utilisateur ne manipule pas directement ces niveaux sur une carte Arduino classique, mais doit en tenir compte lorsqu’il empile plusieurs fonctionnalités reposant sur des interruptions : timer, UART, broches externes, etc.
Désactiver et réactiver les interruptions
Lorsqu’un code doit manipuler une variable partagée entre le contexte principal et une ISR, une section critique protège cette variable. Sur Arduino, la bibliothèque AVR propose généralement les fonctions suivantes :
- noInterrupts() : désactive globalement les interruptions
- interrupts() : réactive globalement les interruptions
Le principe consiste à entourer l’accès à la variable partagée avec ce bloc, puis à rétablir la situation initiale au plus vite pour éviter de masquer trop longtemps les autres interruptions légitimes.
Variables volatiles et interdiction d’accès concurrent
Les variables partagées entre ISR et programme principal doivent être déclarées volatile. Ce mot-clé ordonne au compilateur de ne pas optimiser ces accès, afin de tenir compte des modifications asynchrones provoquées par les interruptions.
ArduinoPiloter un relais 230 V avec Arduino en sécurité : 5 erreurs à éviterEn complément, certains types de variables (entiers sur plusieurs octets) nécessitent une protection contre les accès non atomiques. Une lecture partielle pendant que l’ISR met à jour la valeur génère une incohérence. Dans ce cas, une brève désactivation des interruptions avant la lecture ou l’écriture sécurise l’opération.
Interruptions et consommation de courant : impact sur l’alimentation
Une carte Arduino ne fournit qu’un courant limité par broche et sur la sortie 3,3 V. Sur les données disponibles :
- 40 mA maximum par broche
- 50 mA maximum sur la sortie 3,3 V
Les interruptions influencent indirectement la consommation en réveillant régulièrement le microcontrôleur. Sur un montage basse consommation, un système en mode veille se réveille à chaque interruption externe, traite l’événement, puis retourne en sommeil.
Un débit d’interruptions trop élevé maintient la carte en activité et augmente la consommation globale. Pour un projet alimenté sur batterie, une conception fine des seuils de déclenchement et de la fréquence des événements limite cet impact.
Timers, millis() et interruptions : construire une architecture non bloquante
La fonction millis() repose déjà sur une interruption de timer interne. Elle offre un compteur de millisecondes depuis le démarrage du microcontrôleur. Ce mécanisme sert à concevoir des systèmes non bloquants sans delay().
En combinant millis() avec des interruptions externes, on obtient des architectures répartissant clairement les responsabilités :
- Les interruptions gèrent les événements imprévisibles (capteur, bouton, fin de course).
- millis() gère le déclenchement périodique de tâches (mise à jour d’afficheur, envoi de trame série).
- La boucle loop() orchestre le tout sans blocage.
Pour aller plus loin sur la gestion du temps sans delay(), un guide spécifique comme /arduino-timer-millis/ offre des schémas de code réutilisables et une approche modulaire.
Utiliser des timers matériels pour un contrôle fin
Pour des besoins de précision supérieurs ou des fréquences élevées, la configuration directe des timers matériels remplace les solutions basées sur millis(). Un registre de comparaison (compare match) génère une interruption à intervalle fixe, calculé selon la fréquence d’horloge et le prescaler.
Sur un Uno R3 à 16 MHz, le choix du prescaler et de la valeur de comparaison détermine la fréquence finale. Sur un Teensy à 600 MHz, la résolution augmente et ouvre la voie à des signaux très rapides, par exemple pour du contrôle moteur précis ou des générateurs de signaux.
Exemples concrets de projets utilisant les interruptions
Plusieurs applications classiques s’appuient déjà sur les interruptions pour améliorer leur robustesse et leur réactivité. Les exemples suivants montrent comment intégrer cette mécanique dans des projets réalistes.
Générateur de signal carré avec timers et interruptions
Un générateur de signal carré basé sur un timer matériel et une ISR permet d’obtenir une fréquence stable sans monopoliser loop(). Le principe :
- Configuration d’un timer en mode CTC (Clear Timer on Compare).
- Définition de la valeur de comparaison pour la fréquence souhaitée.
- Dans l’ISR du timer, inversion de l’état d’une broche de sortie.
Cette approche produit un signal indépendant du reste du programme. Le microcontrôleur gère d’autres tâches pendant que le timer assure la stabilité temporelle.
Système de fins de course pour robot
Sur un robot mobile ou un axe motorisé, les fins de course signalent l’atteinte d’une extrémité de course. Une interruption externe sur chaque capteur de fin de course permet de couper immédiatement la commande moteur, sans attendre le prochain tour de loop().
Architecture typique :
- Une ISR par fin de course, qui met à jour un drapeau d’erreur.
- Dans loop(), lecture régulière de ce drapeau pour adapter la stratégie (arrêt, retour arrière, alerte).
- Protection matérielle supplémentaire via relais ou driver pour couper la puissance en dernier recours.
Contrôle moteur avec détection d’anomalies
Dans un système de contrôle moteur, des capteurs d’encodeur, de courant ou de température signalent des anomalies. Une interruption sur front rapide détecte une surintensité, une perte de repère ou un dépassement de température avant que le moteur ne subisse des dommages.
L’ISR se contente de :
- Couper immédiatement les signaux d’activation vers le driver.
- Enregistrer la cause de l’arrêt dans une variable globale.
- Laisser loop() gérer l’affichage d’un message, l’enregistrement en mémoire ou l’envoi d’une trame de diagnostic.
Bonnes pratiques pour architecturer un projet Arduino avec interruptions
Une utilisation maîtrisée des interruptions repose autant sur la technique que sur l’organisation du code. Quelques principes structurants aident à maintenir un projet lisible et extensible.
Limiter la logique dans les ISR
Une ISR se concentre idéalement sur trois types d’actions :
- Mise à jour de variables globales simples.
- Enregistrement d’un horodatage avec micros() ou un compteur local.
- Déclenchement d’un drapeau ou d’un état dans une machine à états.
Le traitement complexe, les conversions, la fusion de données capteurs ou la communication externe se déplacent dans loop() ou dans des fonctions dédiées appelées depuis le contexte principal.
Documenter les ISR et les dépendances
Chaque ISR devrait être accompagnée de commentaires indiquant :
- La source de l’interruption (broche, timer, périphérique).
- Les variables globales modifiées.
- Les hypothèses temporelles (fréquence maximale attendue, durée approximative d’exécution).
Cette documentation facilite la maintenance et révèle rapidement les zones sensibles en cas d’évolution du projet.
Tester sous charge et en conditions réelles
La théorie ne remplace pas un essai en dynamique. Un système d’interruptions se teste :
- Avec des stimuli plus fréquents que ceux prévus en usage normal.
- Avec plusieurs sources d’interruptions actives simultanément.
- En surveillant les dérives de timing et les éventuels blocages.
Même si certaines données, comme la latence précise ou la fréquence maximale d’interruptions, ne sont pas toujours disponibles, un banc de test orienté vers le pire cas réaliste aide à vérifier le comportement global du système.
Panorama des limites techniques connues et zones d’ombre
Les interruptions offrent un levier puissant, mais certaines zones restent moins documentées ou dépendent fortement de la carte et du compilateur utilisés.
Parmi les données manquantes ou très dépendantes du contexte :
- Latence précise des interruptions sur chaque combinaison carte / fréquence.
- Fréquence maximale supportée en continu sans dégradation du système.
- Comportement sous forte charge avec plusieurs vecteurs simultanés.
- Études complètes de fiabilité temps réel en environnement industriel.
Dans la plupart des projets Arduino de loisir ou de prototypage, ces limites ne sont pas atteintes. Pour des applications plus exigeantes, un profilage ciblé, l’utilisation d’outils de mesure (oscilloscope, analyseur logique) et une connaissance approfondie du microcontrôleur deviennent nécessaires.






