• 15 heures
  • Moyenne

Ce cours est visible gratuitement en ligne.

course.header.alt.is_video

course.header.alt.is_certifying

J'ai tout compris !

Mis à jour le 08/11/2019

Utilisez une liaison série (UART)

Connectez-vous ou inscrivez-vous gratuitement pour bénéficier de toutes les fonctionnalités de ce cours !

Les liaisons série sont à la base des télécommunications entre systèmes numériques de façon générale, et en particulier entre la carte Uno et son environnement.

Nous allons les découvrir dans ce chapitre. Pour cela, nous présentons les principes d’une communication, dite UART, mise en œuvre sur tout microcontrôleur.

Ensuite, nous appliquerons ce principe à la communication entre l’ordinateur et la carte Uno. Cela passera par la fenêtre Terminal du logiciel Arduino.

Les liaisons série

En informatique et en télécommunications numériques, les liaisons série désignent l’ensemble des dispositifs matériels et logiciels qui permettent de relier deux systèmes numériques qui communiquent entre eux en échangeant des données bit à bit qui se succèdent, les uns après les autres, sur un canal de communication qui peut être filaire, radio voire optique. Par ailleurs, le canal peut correspondre à une propagation guidée (fil de cuivre, guide d’ondes ou fibre optique) ou à une propagation libre (Wi-Fi, Bluetooth et la plupart des liaisons radio).

À l’opposé des liaisons série, on distingue les liaisons parallèles dans lesquelles les données (souvent organisées en octets) sont transmises par un câble parallèle (on transmet simultanément 8 bits, soit un octet).

Aujourd’hui, comme les liaisons série sont beaucoup moins gourmandes en cuivre (pour les liaisons filaires) ou en fibres optiques, on privilégie largement les liaisons série. Dans le cas de liaisons radio, il serait difficile (quoique pas impossible) d’envisager des liaisons parallèles. Certes, les liaisons série sont intrinsèquement moins rapides puisqu’on transmet bit à bit tandis qu’en liaison parallèle, à un instant donné, 8 bits sont simultanément sur le canal de communication. Toutefois, cet inconvénient de relative lenteur est largement compensé par tous les avantages que comportent les liaisons série.

Nous allons toutefois décrire très rapidement une taxonomie des types de liaisons série et rapidement tourner notre attention vers les UART, qui en sont une déclinaison simple qui existe quasiment sur tous les microcontrôleurs.

Une taxonomie des types de liaisons série

La taxonomie est assez arbitraire selon qu’on adopte telle ou telle clé de détermination pour classer les différents types de liaisons. Comme ce paragraphe est assez qualitatif et ne vise pas l’exhaustivité — ce qui est largement au-delà des ambitions de ce cours — nous pouvons nous permettre d’user de critères de classification arbitraires et rester vagues sur cet univers riche et complexe. :zorro:

Notre première clé est le caractère synchrone ou asynchrone de la liaison :

  • en transmission synchrone, l’émetteur et le récepteur partagent un même signal d’horloge qui permet de déterminer de façon précise l’instant auquel un bit est transmis. L’avantage de la technique est la vitesse de transmission qui peut devenir très grande, la synchronisation du transfert permettant d’assurer la transmission correcte. En revanche, il faut en général que ce signal d’horloge partagé soit aussi présent sur le canal de communication. Par ailleurs, quand la liaison est très longue, le signal d’horloge partagé peut être désynchronisé par la propagation de l’information sur la ligne. On met en œuvre beaucoup de science et de technologie pour asservir l’horloge parvenant au récepteur sur l’horloge à l’émetteur, de façon à garantir une excellente synchronisation. Il n’est pas certain que les coûts que ça engendre soit compensés, d’autant que les liaisons asynchrones ne cessent de progresser en termes de vitesse, et restent intrinsèquement plus simples ;

  • dans une liaison asynchrone, émetteur et récepteurs n’ont pas besoin de partager une horloge commune mais utilisent des horloges locales séparées, moyennant évidemment des fréquences qui sont très proches. Dans ce principe de communication asynchrone, un top départ est donné et les bits évoluent à l’émetteur au rythme de son horloge locale et sont exploités au récepteur aussi au rythme de son horloge locale. Tant que les fréquences d’horloges sont suffisamment proches, mais sans qu’il soit besoin qu’elles soient synchronisées de façon stricte, la transmission peut se faire. En général, on ajoute un bit de parité qui permet de prévenir une erreur de transmission et la technique asynchrone est plutôt réservée à des débits relativement plus faibles que ce qu’on peut faire en liaison synchrone, et pour des trames relativement courtes.

Pour finir notre très bref panorama des liaisons série du marché, nous allons encore présenter une clé de détermination de notre taxonomie, sur la base de quelques autres liaisons série que vous connaissez au moins de nom :

  • notre liaison UART est une liaison “point à point”, ce qui signifie que deux dispositifs numériques sont connectés entre eux directement, sans passer par un routeur ou tout dispositif qui permettrait d’aiguiller des trames venant d’un émetteur vers plusieurs récepteurs. Une liaison “point à point” est clairement une version très simple et rudimentaire de liaison série. Il existe d’autres normes que la liaison UART en mode “point à point” mais c’est la plus usitée parce que, précisément, elle revendique d’être universelle ;

  • on distingue ensuite les liaisons multipoints à bas débit. Vous avez peut-être entendu parler du CAN (pour Controller Area Network) utilisé notamment dans l’automobile. L’idée, dans l’automobile ou dans les véhicules en général, est de diminuer les kilomètres de câbles qui permettent de relier tous les dispositifs numériques entre eux, en connexions point à point. C’est donc un BUS (pour Bit Unit System) qui relie en mode multipoint tous les systèmes numériques entre eux (l’ABS, le contrôle de trajectoires ESP, les éléments de confort, climatisation, chauffage, etc.). Une unité centrale envoie des trames que tous les dispositifs esclaves reçoivent, mais dans la trame figure également le destinataire qui seul exécutera la commande correspondante ;

  • enfin, on distingue les liaisons multipoints à haut débit comme Ethernet, mis en œuvre pour Internet, USB ou FireWire.

Encore une fois, l’étude des liaisons série en général est un univers à part entière et sort très largement des objectifs de ce cours. Nous nous contentons d’utiliser dans la suite la liaison UART de manière opérationnelle, de manière à rendre notre robot pilotable par un dispositif Android, smartphone ou tablette.
Prenons à présent le temps d’étudier quelque peu les trames qui sont transmises depuis l’émetteur vers le récepteur.

La trame UART

Nous nous contentons d’un niveau de description opérationnel, en ce sens que nous décrivons dans l’optique d’être capable de mettre en œuvre, sans subtilité inutile. La figure ci-dessous illustre la trame UART :

Constitution d'une trame UART. Par Pierre Merriaux — Travail personnel, CC BY 2.5, https://commons.wikimedia.org/w/index.php?curid=1728957
Constitution d'une trame UART. Par Pierre Merriaux — Travail personnel, CC BY 2.5, https://commons.wikimedia.org/w/index.php?curid=1728957

Une trame UART est composée de la manière suivante :

  • le niveau logique de repos (en l’absence de transmission) est le niveau logique HAUT, soit 1, défini par le niveau 5V ;

  • la transmission est initiée par un bit de START à 0 qui sert à synchroniser l’envoi de la trame avec le récepteur ;

  • suivent les bits de données dont la taille est comprise entre 5 et 9 bits qui sont envoyés du LSB (bit de poids faible) au MSB (bit de poids fort) ;

  • suit un bit de parité pour le contrôle d’erreur ;

  • suivent un ou deux bits de STOP (niveau logique HAUT, soit à 1, (programmable par l’utilisateur) pour marquer la fin de la trame.

Quand on initialise la liaison (il faut le faire du côté émetteur et aussi du côté récepteur), il faut initialiser de façon cohérente des deux côtés de la liaison. En particulier, il faut régler la vitesse de transmission sur l’une des vitesses standard, soit :

  • 110 bauds ;

  • 300 bauds ;

  • 1 200 bauds ;

  • 2 400 bauds ;

  • 4 800 bauds ;

  • 9 600 bauds ;

  • 19 200 bauds ;

  • 38 400 bauds ;

  • 57 600 bauds ;

  • 115 200 bauds ;

  • 230 400 bauds ;

  • 460 800 bauds ;

  • 921 600 bauds ;

  • 1 843 200 bauds ;

  • 3 686 400 bauds.

Ce sont là les vitesses théoriques standard de la norme, mais tous les microcontrôleurs ne permettent pas nécessairement d’aller jusqu’aux vitesses les plus élevées. 9 600 bauds est une vitesse très standard que nous privilégierons dans la suite. En gros, ça correspond à 9 600 bits par seconde. C’est suffisamment rapide pour que la transmission de quelques chaînes de caractères assez courtes semble instantanée, et suffisamment lent pour permettre au code Arduino de tourner sur une plateforme, la Arduino Uno, qui n’est pas spécialement rapide.

Le bit de parité est optionnel et la liaison doit être initialisée toujours de manière cohérente des deux côtés, pour l’exploiter ou pas. Ce bit de parité est une manière astucieuse de contrôler au moins statistiquement que la liaison s’est faite sans erreur. Pour expliquer comment il fonctionne, nous prenons un exemple. :zorro:

Supposons que nous voulions transmettre le caractère ‘a’ sur l’UART :

  • en binaire sur 8 bits, ‘a’ en ASCII est codé :
    0110 0001
    C’est donc cet octet que nous voulons transmettre sur la liaison série dans une trame UART ;

  • nous allons rajouter à gauche un bit de parité paire. Ce 9e bit est fixé, par définition, de manière à ce que le nombre total de bits (sur les 9 bits) soit pair (on aurait pu ajouter un bit de parité impaire, ce qui en est une variante). Sur notre exemple, ça donne sur 9 bits :
    1 0110 0001 avec en tout sur 9 bits 4 bits à 1, ce qui est bien un nombre pair ;

  • supposons qu’au cours de la transmission, un bit soit corrompu parce qu’il y a beaucoup de bruit électromagnétique, disons dans une usine où il y aurait de gros moteurs électriques. Supposons qu’au récepteur, on reçoive :
    1 0110 1001 où un bit à 0 a été transformé erratiquement en 1 ;

  • le récepteur peut se rendre compte que le nombre total de bits est impair et constater qu’il y a une erreur si émetteur et récepteur se sont mis d’accord au préalable d’un bit de parité pair.

Vous me direz, si deux bits sont corrompus simultanément, le contrôle de parité ne s’en apercevra pas.

C’est rigoureusement vrai. Mais la probabilité pour qu’un bit soit corrompu est déjà très faible, typiquement 108. La probabilité pour que deux bits soient simultanément corrompus dans une même trame est alors de l’ordre de  1016, totalement négligeable. :zorro:

L’idée est de rajouter plusieurs bits de parité sur non pas la totalité des 8 bits à transmettre, mais sur des sous-ensembles de ces 8 bits ; par exemple, des groupes de 4 bits. Chaque groupe possède ainsi son bit de parité. À la réception, on voit dans quel groupe se situe l’éventuelle erreur de transmission puis par intersection des groupes, on arrive à déterminer sur quel bit l’erreur s’est produite. Il est alors facile de corriger l’erreur. :magicien:

Avant d’attaquer la mise en œuvre concrète d’une liaison UART entre la carte Arduino et le PC qui permet de la programmer, il nous reste à regarder le code ASCII, puisque dans l’ensemble de notre cours “robot autonome”, les liaisons seront exclusivement utilisées pour transmettre des trames de caractère ASCII.

Le code ASCII

Le code ASCII (pour American Standard Code for Information Interchange) est une norme informatique de codage de caractères alphanumériques. ASCII comprend 128 combinaisons de 7 bits qui définissent 95 caractères imprimables, dont les chiffres arabes de 0 à 9, les lettres de l’alphabet latin non accentué, minuscules et majuscules, des symboles de ponctuation et quelques caractères mathématiques simples.

Le tableau ci-dessous montre la table ASCII 7 bits historique avec l’état du mot de 7 bits converti en décimal, en hexadécimal, en octal (mais il est totalement inutile de considérer cette colonne) et en HTML pour les caractères imprimables.

Code ASCII 7 bits historique
Code ASCII 7 bits historique. Source : asciichars.com

Pour en comprendre le fonctionnement, nous allons traiter un exemple, le caractère "S" majuscule.

Vous voyez dans la table que ce caractère correspond en décimal à la valeur 83. Si vous convertissez cette valeur décimale 83 en tant que somme pondérée des puissances entières de 2, sur 7 bits, vous obtenez 83 = 64+16+2+1. Vous obtenez donc en binaire, la valeur 83 comme somme pondérée :

64

32

16

8

4

2

1

1

0

1

00

1

1

Si l’ASCII historique est codé sur 7 bits, les microcontrôleurs dont celui de la Uno, traitent des mots de 8 bits, des octets. C’est la raison pour laquelle nous représenterons les caractères ASCII sur 8 bits avec le bit de poids fort toujours à 0. Nous obtenons :

128

64

32

16

8

4

2

1

0

1

0

1

00

1

1

Revenons à notre exemple :

  • les 4 bits de poids faibles représentent la valeur 3 en décimal et donc aussi en hexadécimal, puisqu’on ne sort pas de la capacité de représentation des valeurs de 0 à 9 ;

  • maintenant, quand on passe aux 4 bits de poids forts, on les convertit de la même manière que les poids faibles. On regarde l’équivalent hexadécimal des 4 bits à 0101 et on trouve 53h ;

  • la valeur en hexadécimal des 8 bits est simplement la fusion des 2 conversions, soit 53h avec le h qui figure le fait que c’est une valeur hexadécimale.

Un autre exemple, pour consolider ce point.

Le caractère "O"est codé en décimal par la valeur décimale 79, ce qui donne en binaire : 79 =64+8+4+2+1, soit dans un tableau qui indique les coefficients de la somme pondérée :

128

64

32

16

8

4

2

1

0

1

00

1

1

1

1

Donc 79 en décimal donne l’équivalent binaire, 0100 1111.

Nous marquons un espace entre les deux groupes de 4 bits pour préparer à la conversion en hexadécimal :

  • le premier groupe des 4 bits de poids faibles devient 8+4+2+1=15, soit F en hexadécimal ;

  • le deuxième groupe des 4 bits de poids forts devient simplement ;

  • en joignant les deux groupes, on obtient 4Fh en hexadécimal.

Prenons un exemple.

Beaucoup de microcontrôleurs traitent de mots de 16 bits. Il est hors de question d’écrire les valeurs des données en représentant chacun des bits, ce serait trop long. Nous représentons les données en hexadécimal. Ainsi, la valeur 81FAh (le h pour hexadécimal) est convertie en son équivalent binaire par 4 conversions indépendantes de groupes de 4 bits. On obtient :

8

1

F

A

1000

0001

1111

1010

Soit, en binaire : 1000 0001 1111 1100

Code ASCII 8 bits étendu. Source : https://theasciicode.com.ar
Code ASCII 8 bits étendu. Source : https://theasciicode.com.ar

Toutes les communications entre la Uno et son environnement (le PC et le smartphone ou la tablette) se faisant en ASCII, à priori, en ASCII 7 bits, il sera nécessaire de se reporter régulièrement à la première table ci-dessus. :zorro:

La fenêtre Terminal et la communication entre la carte Arduino et le PC

Nous avons vu dans la partie 2 comment téléverser un code écrit sur son ordinateur dans l’environnement Arduino vers la carte Uno qui ensuite l’exécute, grâce à la liaison USB qui en fait met en œuvre l’UART du microcontrôleur.

Une fois le programme lancé, nous allons utiliser le même canal pour communiquer entre l’ordinateur et la carte Uno. L’idée est ainsi de programmer une communication série dans la boucle "loop" du code, de manière à ce que le code téléversé dans la carte Uno exploite les données qui arrivent sur l’UART. :magicien:

On ouvre le moniteur série en l'activant dans le menu général "Outils". Il y a aussi à l'extrémité supérieure droite une icône pour ce faire. Il faut évidemment que la carte Uno soit connectée et alimentée par le câble USB.

Le petit code que nous allons étudier est très simple mais illustre efficacement cette thématique.

Nous allons modifier quelque peu le programme "Blink" , variante de ce que nous avions étudié dans la partie 2 (on le trouve dans les exemples de la plateforme dans le menu "Basics") comme suit :

  • au lieu que la fréquence de clignotement soit codée en dur dans le code par la déclaration d’une constante, nous allons définir cette fréquence par l’envoi depuis l’ordinateur vers la carte UNO via la liaison série ;

  • comme cette communication est faite dans la boucle "loop", cela signifie que nous pourrons la modifier en cours d’exécution du code.

La figure ci-dessous est un screenshot du code écrit dans l’environnement Arduino :

Code du programme de clignotement de la Led BuiltIn dont la fréquence est pilotée par la liaison série
Code du programme de clignotement de la Led BuiltIn dont la fréquence est pilotée par la liaison série

Le code est déjà relativement bien commenté :

  • on y constate une série de fonctions qui sont appelées comme appartenant à la classe “Serial” ;

  • on y voit une ligne qui peut paraître étrange parce qu’elle met en œuvre les techniques orientées objet dont le langage Arduino est une déclinaison. Il s’agit de la ligne :
    i = input.toInt() ;

  • la variable “input” est de type “chaîne de caractères” (String). Elle a été affectée par la fonction “Serial.readString”. Dans cette ligne, l’appel à la fonction “toInt” est de type “orienté objet” ;

  • la méthode “toInt” est relative à une variable de type “String” et elle est désignée par la partie qui précède le point, soit “input” ;

  • cette méthode “toInt” permet ainsi de convertir la chaîne de caractères ASCII en une valeur de type “int” ;

Par exemple, si l’ordinateur envoie la chaîne “1000”, elle envoie en réalité les caractères ASCII “1”, “0”, “0” et “0” et c’est cette chaîne qui sera affectée en tant que telle dans la variable “input”. En appliquant à cette chaîne la méthode “toInt”, on la convertit en “int” et maintenant “i” contiendra la valeur 1000, non plus en tant que chaîne de caractères mais en tant que variable “int” codée sur 2 octets.

  • il reste à présent à téléverser ce code et à envoyer sur la liaison série le délai de clignotement en millisecondes. :magicien:

En résumé

Comme vous l'avez constaté, la communication série entre l’ordinateur et la carte Uno est basée sur l’utilisation de la librairie“Serial”, qui fournit un certain nombre de fonctions qui permettent d’échanger des chaînes de caractères. Toutefois, dans une application plus réaliste, le robot ne doit pas conserver son “fil à la patte” qui le relie à l’ordinateur. Nous allons donc voir comment, en utilisant une autre liaison série, nous communiquerons par Bluetooth, donc sans fil, entre la carte Uno et un dispositif Android, tablette ou smartphone, pour piloter le robot à distance. :magicien:

Exemple de certificat de réussite
Exemple de certificat de réussite