Mesurer des longueurs d'impulsions avec une carte Arduino / Genuino

Ce n’est pas la taille qui compte, c'est la longueur ... oh wait.

Image d'entête

par skywodd | | Licence (voir pied de page)

Catégories : Tutoriels Arduino | Mots clefs : Arduino Genuino Pulse Impulsion


Dans ce tutoriel, je vous propose de voir ensemble comment mesurer la longueur / durée d'une impulsion électrique au moyen d'une carte Arduino / Genuino.

Sommaire

Bonjour à toutes et à tous !

À force d'écrire des tutoriels de plus d'une trentaine de pages à chaque fois, mon planning de publication ne ressemble plus à rien. J'ai donc décidé pour cet article de vous parler d'un sujet simple, mais intéressant (et utile) : la mesure d'impulsions électriques.

Une simple impulsion électrique peut transporter de l'information. Dans un précédent article, on a pu voir par exemple qu'une bête impulsion électrique d'une durée entre 1 et 2 millisecondes (à fréquence fixe) permettait de contrôler un servomoteur.

Dans le vaste monde de l'électronique, la transmission de données par longueur d'impulsion est quelque chose de très commun. Par exemple, les radiocommandes de modélisme envoient la position des joysticks de contrôle grâce à un signal à largeur d'impulsion variable (signal PWM). De même, les modules sonar à ultrason transmettent en retour de l'onde ultrason la distance par rapport à l'obstacle le plus proche grâce à une impulsion de longueur variable, proportionnelle à la distance.

Mesurer une impulsion électrique peut sembler trivial. On peut être tenté de résumer cette mesure à "on lance un timer au début de l'impulsion et on l'arrête à la fin de l'impulsion, voilà". Dans les faits, mesurer une impulsion est quelque chose de relativement complexe.

Certains microcontrôleurs haut de gamme disposent de périphériques matériels dédiés à la mesure d'impulsions. Cependant, la plupart des petits microcontrôleurs n'ont rien pour mesurer matériellement la durée d'une impulsion. Il faut donc combler ce manque par du code et quand on essaye de réinventer soit même la roue carrée, ça finit souvent mal.

Le sujet de cet article sera donc simple : mesurer la longueur / durée d'une impulsion électrique avec une carte Arduino / Genuino, sans réinventer la roue carrée.

Le signal

Pour bien comprendre cet article, il faut d'abord comprendre ce qu'est une impulsion électrique.

Capture écran d'un signal PWM

Capture écran d'un signal PWM

Une impulsion électrique est une portion de signal qui est dans un état précis durant une durée quelconque. C'est tout.

Dans la capture d'écran ci-dessus vous pouvez voir (au choix) : 2 impulsions "hautes" ou deux impulsions "basses" (ainsi que quelques restes de signal sur les côtés). Dans cet exemple, il s'agit d'un signal périodique issue d'un générateur de signaux, par conséquent, les deux impulsions se suivent et font la même taille. Ce n'est pas forcément tout le temps le cas. Dans une application plus concrète, comme le signal de retour d'un module sonar à ultrason, l'impulsion serait unique par exemple.

Quand on mesure une impulsion, on doit d'abord définir sa polarité. Si le signal passe de 0 à 1 puis de 1 à 0, c'est une impulsion haute. Si le signal passe de 1 à 0 puis de 0 à 1, c'est une impulsion basse.

C'est un avion ? C'est un oiseau ? Non c'est pulseIn() !

Comme je l'ai précisé en introduction, mesurer une impulsion n'est pas aussi facile qu'on peut le croire. Obtenir une mesure précise demande des timings précis. C'est pour cela qu'en général, quand on tente de réinventer la roue dans ce domaine, on finit avec des roues carrées.

Le framework Arduino fournit une fonction testée et éprouvée pour mesurer des impulsions (hautes ou basses) : pulseIn().

1
2
unsigned long pulseIn(broche, valeur);
unsigned long pulseIn(broche, valeur, timeout);

La fonction pulseIn() accepte au maximum trois paramètres et retourne un nombre entier long (unsigned long) correspondant à la durée de l'impulsion mesurée en microsecondes, ou 0 en cas d'erreur.

Le premier paramètre est le numéro de broche sur laquelle faire la lecture de l'impulsion.

Le second paramètre est la polarité de l'impulsion à mesurer. Si vous souhaitez mesurer une impulsion haute, il faut passer HIGH en paramètre à la fonction. Inversement, si vous souhaitez mesurer une impulsion basse, il faudra passer LOW en paramètre à la fonction.

Le troisième paramètre (optionnel) est la durée maximum en microsecondes de l'attente d'une impulsion avant la mesure. Si aucune impulsion n'arrive avant la fin du timeout, la fonction s'arrête et retourne 0.

N.B. Par défaut le timeout est d'une seconde !

En interne, la fonction pulseIn() fait trois choses :

  • Elle vérifie qu'une impulsion arrive dans le délai imparti.

  • Elle attend que le signal passe à l'état désiré et commence le comptage.

  • Elle attend que le signal repasse à l'état inverse de celui désiré pour arrêter le comptage.

N.B. La fonction pulseIn() calcule la durée de l'impulsion en comptant le nombre de tick d'horloge du processeur dans une boucle. C'est une solution bien plus fiable et précise que de tenter d'utiliser un timer quand celui-ci n'est pas conçu pour cela.

Quelques précisions importantes

  • Il y a entre une et deux microsecondes de délai avant le début effectif du comptage (juste le temps nécessaire pour appeler la fonction et préparer le comptage). Cela ne pose généralement pas de problème. Cependant, si vous avez un signal avec seulement quelques microsecondes de délai entre deux impulsions, cela peut poser des problèmes.

  • La fonction pulseIn() attends que le signal fasse une transition vers l'état désiré avant de commencer le comptage. Cela signifie que si vous mesurez une impulsion haute et que le signal est déjà à HIGH, il faudra attendre le passage à LOW puis de nouveau à HIGH pour le comptage commence. Ce comportement permet d'éviter de mesurer une impulsion déjà commencée. Cela a cependant pour conséquence de rendre impossible la mesure successive d'une impulsion haute puis basse ou inversement.

  • Il n'est pas possible de mesurer une impulsion en réponse à une interruption sur la même broche. Un cas d'erreur classique est de vouloir mesurer une impulsion dans la fonction appelée par attachInterrupt(). Quand l'interruption se déclenche, le signal a déjà changé d'état pour passer dans l'état désiré et la fonction pulseIn() va bloquer en attendant l'impulsion suivante.

  • Il est vivement déconseillé d'utiliser pulseIn() dans une interruption. Cela fonctionne (à peu près), mais c'est très bancal et cela va à l'encontre des bonnes pratiques de programmation qui veulent qu'une interruption doive être la plus courte / rapide possible.

  • La fonction pulseIn() ne donne des mesures fiables que si les interruptions sont désactivées. Quand les interruptions sont actives, le code en court d'exécution peut être mise en pause par une interruption pour gérer un événement, comme l'envoi d'un caractère sur le port série par exemple. Il est donc important d'entourer chaque appel à pulseIn() par un appel à noInterrupts() et un appel à interrupts() pour désactiver temporairement les interruptions.

  • Dans les versions récentes du framework Arduino (1.6.x et supérieur), une implémentation alternative de pulseIn() est disponible pour les cas où le reste du code fait usage d'interruptions qui ne peuvent être désactivés, même temporairement. Cette implémentation alternative utilise la fonction micros() en interne et elle est accessible via la fonction pulseInLong(). Les paramètres sont les mêmes que pulseIn(). Cette version a une plage de mesure de 10 µs ~ 3 minutes avec une précision de 4 µs (précision de micros()).

Exemple de code

L'exemple de code ci-dessous permet de lire la longueur d'une impulsion haute sur la broche D2 :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/**
 * Code d'exemple pour la fonction pulseIn()
 */

/** Broche d'entrée du signal */
const byte PIN_SIGNAL = 2;

/** Fonction setup() */
void setup() {

  // Configure le port série pour l'exemple
  Serial.begin(115200);

  // Met la broche de signal en entrée
  pinMode(PIN_SIGNAL, INPUT);
}

/** Fonction loop() */
void loop() {

  // Mesure la durée de l'impulsion haute (timeout par défaut de 1s)
  noInterrupts();
  unsigned long duration = pulseIn(PIN_SIGNAL, HIGH);
  interrupts();

  // Affiche la durée de l'impulsion (en us) sur le port série
  Serial.println(duration);
  delay(1000);
}

L'extrait de code ci-dessus est disponible en téléchargement sur cette page (le lien de téléchargement en .zip contient le projet Arduino prêt à l'emploi).

Conclusion

Ce tutoriel est désormais terminé.

Si ce tutoriel vous a plu, n'hésitez pas à le commenter sur le forum, à le diffuser sur les réseaux sociaux et à soutenir le site si cela vous fait plaisir.