mercredi 18 septembre 2019

Analyse d'une communication UART


On peut probablement passer des années à utiliser l'UART de l'Arduino de façon routinière sans savoir exactement ce qui se passe sous le capot. Dans ce billet, je m'amuse à observer le contenu d'un message UART.

Tout comme SPI et I2C, l'UART est un mode de communication série, ce qui signifie que les données sont acheminées l'une à la suite de l'autre sur une même broche du microcontrôleur.

La transmission UART implique 2 broches en plus de la masse (GND): TX pour la transmission, et RX pour la réception.  Sur un Arduino Uno, la broche numéro 0 reçoit les données (RX) alors que la broche numéro 1 est émettrice (TX). Le TX d'un des appareils impliqués dans la communication est toujours relié au RX de l'autre appareil.

Avec Arduino, vous utilisez l'UART chaque fois que vous faites appel à la classe Serial: Serial.print, Serial.println, Serial.read, etc. Typiquement, on l'utilise pour la communication entre un microcontrôleur et un ordinateur, ou entre plusieurs microcontrôleurs. Le protocole MIDI, beaucoup utilisé en musique, est une liaison UART à 31250 bauds.

Le sigle UART signifie "Universal Asynchronous Receiver Transmitter": le protocole permet donc d'émettre et de recevoir des données de façon asynchrone.  "Asynchrone" signifie qu'il n'existe pas de signal d'horloge commun partagé par l'émetteur et le récepteur, d'où la nécessité de régler l'émetteur et le récepteur à une même vitesse de transmission. Par exemple, lorsque votre sketch Arduino commence par Serial.begin(9600), vous réglez la vitesse de l'UART à 9600 bauds du côté Arduino, ce qui vous oblige à régler le moniteur série, du côté ordinateur, à 9600 bauds également.

Pour observer des messages UART, j'ai branché un analyseur logique à la broche TX d'un Arduino Uno, et j'ai visualisé les résultats sur le logiciel Sigrok PulseView.


Le sketch ne surprendra personne: je règle la vitesse de la communication à 9600 bauds, et j'envoie le message A, suivi d'un court délai pour m'aider à mieux distinguer l'émission de deux messages distincts.


Voici ce que me présente l'analyseur logique lors de l'émission de ce message:



Pendant qu'aucun message n'est transmis, la broche TX est au niveau logique haut. Un message UART commence toujours par un bit de départ (start) de niveau logique bas afin d'annoncer le début d'un message.

Le bit de départ est suivi d'une succession de 8 bits qui constituent le message proprement dit. Dans ce cas, ces 8 bits sont 1, 0, 0, 0, 0, 0, 1, et 0. Puisque le message débute par le bit de poids faible et se termine par le bit de poids fort, le résultat correspond au nombre binaire 01000001, ou 65 en décimal, soit le code ASCII de la lettre "A".

Le message se termine par un bit de fin (stop) de niveau logique haut. La broche demeure ensuite au niveau logique haut jusqu'à l'émission du prochain message.

Vous pouvez constater qu'il n'y a aucune frontière visible entre l'émission de deux bits consécutifs identiques; c'est la raison pour laquelle il est important que l'émetteur et le récepteur soient réglés à la même vitesse. Avec une transmission à 9600 bauds, chaque bit dure environ 100 µs (1/9600).


Si je double la vitesse de transmission pour qu'elle soit de 19200 bauds, la durée de chaque bit sera plutôt de 1/19200, donc environ 50 µs:


Si j'envoie un message constitué de plusieurs caractères, chacun des caractères est accompagné par son bit de départ et son bit de fin. Ici, j'ai envoyé le message "OK":


Cette fois, le message est constitué d'un bit de départ, de 8 bits correspondant au nombre binaire 01001111 (ou 79 en décimal, le code ASCII pour le caractère O), d'un bit de fin, d'un deuxième bit de départ, de 8 bits correspondant au nombre binaire 01001011 (ou 75 en décimal, correspondant au code ASCII pour le caractère "K"), et d'un dernier bit de fin.

Modifier les paramètres par défaut

Par défaut, la communication UART de l'Arduino est réglée à 8 bits de données, pas de parité et un bit "stop", mais il est possible de modifier ces paramètres en ajoutant un deuxième paramètre à la fonction Serial.begin().

Quelques exemples:

  • Serial.begin(9600, SERIAL_5N1) règle la vitesse de transmission à 9600 bauds, avec 5 bits de données, pas de parité, et un bit stop.
  • Serial.begin(4800, SERIAL_7N2) règle la vitesse de transmission à 4800 bauds, avec 7 bits de données, pas de parité, et 2 bits stop.
  • Serial.begin(19200, SERIAL_8E1) règle la vitesse de transmission à 19200 bauds, avec 8 bits de données, parité paire ("even")  et un bit stop.
  • Serial.begin(115200, SERIAL_8O2) règle la vitesse de transmission à 115200 bauds, avec 8 bits de données, parité impaire ("odd"), et 2 bits stop.
(Voir cette page pour tous les cas possibles.)

Parité

Le bit de parité est un bit optionnel qui peut être ajouté entre les bits de données et le bit stop afin de vérifier sommairement que l'intégrité du message a été correctement transmise. La parité peut être paire ou impaire.

Le sketch ci-dessous envoie le symbole "a" avec un bit de parité paire:


Voici le résultat:

Le bit start est suivi du nombre binaire 01100001 (ou 97 qui est le code ASCII pour "a"), du bit de parité (1), puis du bit stop.

Lorsque la parité est paire, le bit de parité prend la valeur nécessaire pour que le nombre total de bits qui sont au niveau logique haut soit paire. Dans cet exemple, si vous comptez le nombre de "1" dans le message "01100001", vous obtenez 3, qui est un chiffre impair. Puisque nous avons réglé la parité pour qu'elle soit paire, le bit de parité a pris la valeur "1" afin que le total 3 + 1 donne un résultat pair.

Voici un deuxième exemple, toujours avec une parité paire, sauf que le message est maintenant la lettre "c":


Cette fois, le bit de parité est au niveau logique bas, car le message correspondant à la lettre "c" est 01100011, qui comporte 4 bits au niveau logique haut. Ce nombre étant déjà paire, on lui additionne un bit de parité ayant la valeur "0" afin que le total demeure paire.

Comme vous pouvez le deviner, ce sera le contraire si j'envoie les mêmes messages avec un bit de parité impaire:


Si le message est "a", le bit de parité est au niveau logique bas, afin que le total des bits de niveau haut soit impair: 1 + 1 + 1 + 0 = 3:

Si le message est "c", le bit de parité est au niveau logique haut, afin que le total des bits de niveau haut soit impair:  1 + 1 + 1 + 1 + 1 = 5:

À lire également

D'autres types de communication série ont également été abordés: Analyse d'une communication SPI et analyse d'une communication I2C.

Yves Pelletier   (TwitterFacebook)

1 commentaire: