La liaison série asynchrone
Un UART, pour Universal Asynchronous Receiver Transmitter, est un émetteur-récepteur asynchrone universel. En voici le principe pour un Arduino :
- Transmission de données d'un équipement 1 (un microcontrôleur Arduino) à un équipement 2 (PC, GPS, émetteur Bluetooth, microcontrôleur ....) .
- Données à transmettre existent sous forme parallèle (octet) et sont transmises sous forme série (LSB en premier)
- Données reçues sous forme série (LSB en premier ...:-) puis reconditionnées sous forme d'octet.
- Pour permettre une liaison plus rapide les données sont stockées dans un buffer (mémoire tampon) d'une capacité de 64 octets.
- Entre 2 équipements les fils sont croisés : Tx1 relié à Rx2 et Tx2 relié à Rx1 (voir figure)
- Les niveaux de tension sont de type TTL soit 0 V pour le niveau bas et +5V pour le niveau haut.
- Asynchrone car aucune horloge (bit clock) n'est transmise entre l'emetteur et le récepteur. Le recepteur ignore quand il va recevoir une donnée.
- Afin de faciliter l'interopérabilité entre périphériques des vitesses de transmission sont normalisées par multiples et sous-multiples de 9600 baud,
- l'unité baud correspondant à une vitesse de transmission de un bit par seconde.
Avantages
: standardisée,
universelle, pas cher
3 fils suffisent (émission Tx, réception Rx, masse GND) et
souvent l'alimentation + 5 V
Inconvénients
: Assez lent.
(maximum pour un Arduino : 115200 bauds)
Une liaison UART ne permet que de relier 2 équipements.
Constitution d'une trame UART Arduino :
- un bit de start toujours à 0 : servant à la synchronisation du récepteur
- les données : pour un code Ascii étendu 8 bits (un octet)
- et un bit de stop toujours à 1
Le niveau logique de repos est le 1.
Test de la liaison UART
Nous avons voulu savoir comment fonctionnait physiquement la liaison UART.
Pour cela nous avons transmis le caractère "a" et observé à l'oscilloscope la patte Tx d'un Arduino Uno.
Le code
ASCII du caractère a est 97, soit
0x 61 en hexadécimal, soit 0b 0110
0001 en binaire.
0b
0110
0001 = 0x 61 = 6*161+ 1*160 = 96 + 1 = 97
Commentaire de l'oscillogramme obtenu :
- A : Bit de Stop de la trame précédente
- B : Bit de start de la nouvelle trame a = (MSB)0110
0001(LSB),
soit à l'envers (LSB)1000 0110(MSB) - les 4 bits de poids faible : C (LSB=1) D (0) E (0) F (0)
- les 4 bits de poids fort : G (0) H (1) I (1) J (MSB=0)
- K : Bit de Stop de la trame étudiée
1-a) Pour réaliser ce test, copier le programme ci-dessus. Puis
téléversez-le sur un Arduino uno.
Visualiser à l'oscilloscope la transmission du caractère x
1-b) En déduire son code ASCII et vérifier ce résultat dans la table ASCII ci-dessus.
Le baudrate de 8000 bit/s n'est pas standard.
Mais la durée de transmission d'un bit est alors de 1/8000 = 125
µs ce qui correspond à une demi-division de l'oscilloscope
(calibre 250 µs/div).
// Visualisation d'une trame série à l'oscilloscope void setup() { Serial.begin(8000); // Oscilloscope entre Tx->1 et GND calibre : 250 µs:div } void loop() { //Nous envoyons le caractère "a" code ASCII : 97 soit 0x61 soit 0b01100001 // suivi d'un caractère nul pour bien isoler le "a" à l'oscilloscope Serial.write(97); // idendique à Serial.print("a"); ou à Serial.write(0x61); // Trame de 10 bits : Start : 0/LSB:1/0/0/0/0/1/1/MSB:0/Stop:1 // durée de transmission d'un bit : 1/8000 = 0,125 ms = 125 µs soit 1/2 div // durée de la trame : 10*0.125ms = 1,25 ms pour un octet soit 800 octets à la seconde Serial.write(0); }
Un capteur à sortie UART : le GPS
Pour visualiser des trames plus complexes l'oscilloscope n'est pas adapté.
Un analyseur logique et son logiciel associé Saleae permettent d'analyser tous les signaux numériques TTL (0 - 5 V).
Un capteur GPS UART utilise principalement 4 pattes :
- VIN : alimentation + 5 V ;
- GND : la masse ;
- Rx : pour la réception de donnée, facultatif si on utilise les paramètres par défaut du GPS ;
- Tx : transmission des données GPS, par défaut 9600 bit/s (baudrate), rafraichissement des coordonnées 1 Hz.
Nous avons alimenté un capteur GPS entre GND et VIN=5V. Puis nous avons relié sa patte TX à notre analyseur logique pour obtenir ce signal :
Dans une liaison UART pour transmettre
un caractère Ascii (codé sur 1 octet) il faut : 1 bit de start
puis le code ASCII sur 8 bits (1 octet) et un bit de stop soit
10 bits.
Une liaison 9600 bit/s transmet donc 960 caractères par seconde.
Pour plus de précisions sur le capteur GPS, consulter la page projet ballon 2016.
Le Bluetooth
On utilisera aussi la liaison série pour communiquer avec le microcontrôleur du HC06 qui est un émetteur Bluetooth. Et ainsi commander un Arduino avec un téléphone Android par liaison Bluetooth.
U=RI | Arduino Ep.17 - Application Android avec le module Bluetooth HC-06
Hello World !
Nous allons observer le temps de transmission nécessaire pour afficher un message : Hello world ! Réaliser le montage permettant d'observer à l'analyseur logique la sortie Tx (patte 1 ) d'un Arduino uno. Téléverser sur cet Arduino Uno le programme ci-contre. Ce message comporte 13 caractères + \r \n = 15 caractères Ascii2-a) En théorie il faut combien de temps pour transmettre 15 caractères à 9600 bauds ? Mesurer ce temps sur l'Analyseur logique (penser à compter le dernier bit de stop (1)...) Temps de transmission = ......... ms 2-b) D'après la fenêtre série quel est le temps d'éxécution de Serial.println("Hello world !") ? Temps de transmission = ............ ms Temps d'éxécution = ............ ms Comment expliquer cette différence ? 2-c) On ajoute la fonction Serial.flush() juste après. Que devient le temps d'éxécution ? Comparer ce temps au temps de transmission ? Que fait la fonction Serial.flush() ? 2-d) Augmenter la vitesse de transmission à 115200 baud. Serial.begin(115200); Penser à changer aussi la vitesse sur le moniteur série ! Est-ce que le temps d'éxécution change ? (avec et sans la fonction Serial.flush();) Mesurer sur l'analyseur logique le temps de transmission = .... ms |
unsigned long t0,t;
void setup() {
// Initialisation
Serial.begin(9600);
}
void loop() {
t0=micros(); // Top départ
Serial.println("Hello world !");
// Serial.flush(); // A quoi sert cette fonction ?
t=micros()-t0; // temps d'éxécution de Serial.println("Hello world !");
delay(100);
Serial.print("Temps d'execution =");
Serial.print(t);Serial.println(" us");
delay(1000);
}
|
Les fonctions de la bibliothèque Serial
Pour utiliser la liaison série d'un Arduino nous allons utiliser la bibliothèque (en anglais: librarie ) intégrée Serial :
Fonctions |
Description |
Paramètres |
Serial.begin(debit); |
Fixe le débit de communication en nombre de caractères par seconde (l'unité est le baud) pour la communication série. En cas d'utilisation du terminal série, IL FAUDRA FIXER LE MEME DEBIT dans la fenêtre du Terminal ! Cette fonction est placée dans le Setup. |
int debit: debit de communication en caractères par seconde (ou baud).
Pour communiquer avec l'ordinateur,
utiliser l'un de ces débits : 300, 1200, 2400, 4800, 9600, |
|
Affiche les données sous le port série sous forme lisible pour les humains (texte ASCII). Serial.println() ajoute un retour de chariot CR (code ASCII : 13) et un saut de ligne LF (10). |
val: la valeur à afficher. N'importe quel type
de données.
format
: spécifie la base utilisée (pour les nombres entiers) ou le
nombre de décimales (pour les nombres de type float) |
Serial.write(val) |
Ecrit des données binaires sur le port série. Ces données sont envoyées comme une série d'octet; pour envoyer les caractères correspondants aux chiffres d'un nombre, utiliser plutôt la fonction print(). |
val
: une valeur à envoyer sous forme d'octet
simple (code ASCII) |
|
Renvoi le nombre d'octet disponible
pour lecture dans la file d'attente (buffer) du port série,
ou 0 si aucun caractère n'est disponible. La file d'attente du buffer peut recevoir jusqu'à 64 octets voir 128 ou 256 octets si l'on change les paramètres par défaut (ceci au détriment de la mémoire disponible). |
|
|
Retourne le premier entier (format unsigned long : de 0 à 4 294 967 295 = 232 - 1 ) contenu dans le buffer série. Les caractères qui ne sont pas des chiffres (notamment le signe -) sont ignorés. Serial.parseInt() arrête de lire le buffer série au premier caractère qui n'est plus un chiffre. |
|
Mise en application
Objectif : apprendre à utiliser la bibliothèque Serial en duplex.
Le moniteur série envoie des consignes à l'Arduino à 9600 bauds.
L'Arduino lui répond en confirmant avoir bien reçu la consigne.
3-a)
- Si n < 1 alors n =1
- Si n > 10 alors n = 10
- affiche sur le moniteur série : "Nombre reçu : n"
- puis fait clignoter n fois la led, période de 500 ms
Vous avez du mal à commencer ?
Les fonctions utiles pour ce cahier des chages :
pinMode(broche, mode) //mode: soit INPUT (entrée en anglais) (=0) ou OUTPUT (sortie en anglais) (=1) Serial.begin(debit); // le plus souvent on prend débit = 9600 bauds Serial.available(); // renvoie le nombre d'octet disponible pour lecture dans la file d'attente (buffer) du port série, ou 0 si aucun caractère n'est disponible. Serial.print("Message "); Serial.println("Message + retour ligne "); n=Serial.parseInt(); // lit le premier entier disponible dans le buffer série delay (ms); // ms : le nombre de millisecondes que dure la pause digitalWrite(broche, valeur) // valeur : HIGH ou LOW (ou bien 1 ou 0) // Les structures de contrôle : if (uneVariable > 50){ // début du bloc d'instructions à éxécuter // faire quelque chose } // fin du bloc d'instructions à éxécuter for (initialisation; condition; incrementation) { // initialisation : i=0; condition : i<n; incrémentation : i++ //instruction(s)à exécuter n fois; } while(expression){ // tant que l'expression est vraie // instructions à effectuer } while (Serial.available()) Serial.read(); // Exemple pour Vider le buffer Serial c=Serial.read(); // lit (et retire)le premier octet de donnée entrant disponible dans le buffer
Arduino pour les nuls : affiche la structure du programme avec des commentaires.
Toujours en difficulté ? Le corrigé.
3-b) Avec l'analyseur logique faites une capture
des échanges entre le PC et l'Arduino sur les pattes Rx(0) et Tx(1)
de l'Arduino
Combien de temps s'écoule-t-il entre la
réception de l'ordre sur la patte Rx et la transmission de la
réponse de l'Arduino sur la patte Tx.
Sauver votre capture de Saleae Logic des pattes Rx et Tx avec un analyseur Async Serial.
Vous avez fini ? Comment utiliser un GPS avec un Arduino uno ?
4-a) Relier un GPS à un Analyseur logique.
Pour cela alimenter le GPS en reliant
Vin (GPS) à 5V (Arduino)
GND (GPS) à GND (Arduino)
Une inversion d'alimentation entraine
la destruction du GPS !
Puis relier Tx à Ch1 de l'analyseur
logique et observer.
Quel est le temps mis pour transmettre les coordonnées GPS ?
Les coordonnées sont rafraichies à quelle fréquence ?
4-b) On souhaite visualiser le message transmis par le GPS sur le moniteur série.
Réaliser un programme qui :
-
vérifie si un caractère est disponible sur le port série ; Serial.available();
-
lit le caractère disponible sous forme de code ASCII (a) ; a=Serial.read();
-
affiche le caractère sur le moniteur série. Serial.write(a);
Téléverser votre programme puis relier le Tx du GPS au Rx de l'Arduino.
Attention :
à chaque téléversement il ne faut pas que le Tx du
GPS soit relié
sinon il y a un conflit entre le GPS et l'ordinateur qui se
retrouvent reliés ensemble à la patte Tx de l'Arduino.
Dans la salle de classe, il faut ajouter une antenne
au GPS et la placer à la fenêtre pour recevoir le signal.
Il faut aussi attendre que le fix soit fait (diode clignote
rapidement : fix non réalisé)
Sur la fenêtre du moniteur série ci-contre, le fix n'est pas
encore réalisé (pas de coordonnées GPS transmises).
Analyser les trames NMEA reçues et déterminer les coordonnées GPS transmises.
Utiliser par exemple une trame RMC
Une autre trame très courante pour les bateaux est la RMC, qui donne l'heure, la latitude, la longitude, la date, ainsi que la vitesse et la route sur le fond mais pas l'altitude.
$GPRMC,053740.000,A,2503.6319,N,12136.0099,E,2.69,79.65,100106,,,A*53 $GPRMC : type de trame 053740.000 : heure UTC exprimée en hhmmss.sss : 5h 37m 40s A : état A=données valides, V=données invalides 2503.6319 : Latitude exprimée en ddmm.mmmm : 25°03.6319' = 25°03'37,914" N : indicateur de latitude N=nord, S=sud 12136.0099 : Longitude exprimée en dddmm.mmmm : 121°36.0099' = 121°36'00,594" E : indicateur de longitude E=est, W=ouest 2.69 : vitesse sur le fond en nœuds (2,69 kn = 3,10 mph = 4,98 km/h) 79.65 : route sur le fond en degrés 100106 : date exprimée en qqmmaa : 10 janvier 2006 , : déclinaison magnétique en degrés (souvent vide pour un GPS) , : sens de la déclinaison E=est, W=ouest (souvent vide pour un GPS) A : mode de positionnement A=autonome, D=DGPS, E=DR *53 : somme de contrôle de parité au format hexadécimal[3]
L'analyse des trames peut-être effectuée facilement par un Arduino grâce à la bibliothèque TinyGPS.
Si celle-ci n'est pas disponible sur votre poste, il faut l'importer après l'avoir téléchargée en cliquant sur ce lien.
Pour importer : Croquis/Importer bibliothèque/Ajpouter bibliothèque puis Téléchargements/TinyGPS.zip
Exemple : un programme mini pour afficher les coordonnées GPS.
4-c) Testez puis modifier ce programme afin
d'aficher l'altitude en plus de la latitude et longitude.
Aidez-vous du
descriptif des fonctions de la bibliothèque TinyGPS.
/* Programme mini pour afficher les coordonnées GPS sur un Arduino Uno Il faut alimenter le GPS : GND(GPS) à GND (Arduino) Vin (GPS) à 5 V (Arduino) Il faut relier Tx (GPS) à Rx (Arduino) Attention lors du téléversement il faut débrancher le fil Rx de l'Arduino sinon il y a un conflit entre le GPS et l'ordinateur qui se retrouvent reliés ensemble à la patte Tx de l'Arduino. */ #include <TinyGPS.h> // La bibliothèque TinyGPS va analyser les trames GPS TinyGPS gps; // Création de l'objet GPS int a; float lat, lon; void setup() { Serial.begin(9600); } void loop() { if (Serial.available()){ a = Serial.read(); // Serial.write(a); // Pour Debuger et voir les trames GPS if (gps.encode(a)) lireGPS(); // Si TinyGPS a découvert une nouvelle trame, afficher les coordonnées } } void lireGPS() { gps.f_get_position(&lat, &lon); // appel fonction de la bibliothèque TinyGPS Serial.println(); // Sauter une ligne Serial.print(lat,5); // voir les coordonnées GPS Serial.print(",");Serial.println(lon,5); } // D'autres fonctions sont disponibles : lire la vitesse, la date, l'altitude...