Club robotique de Sophia-Antipolis

Accueil > POBOTpedia > Programmation > Apprendre à coder > Les micro-contrôleurs > Les micro-contrôleurs sans ta mère > S’ouvrir au monde

S’ouvrir au monde

ou comment communiquer avec l’extérieur

dimanche 27 novembre 2005, par Eric P.

Après avoir fait clignoter les LEDs à s’en faire péter les rétines, il est maintenant temps de franchir un échelon. C’est bien de faire réaliser des actions avec notre micro-contrôleur, mais c’est encore mieux s’il était possible de le piloter de l’extérieur, de lui faire envoyer des infos,... En 3 mots : communiquer avec l’extérieur.

La solution du jour

Il existe plusieurs moyens pour faire communiquer l’ATmega8 avec l’extérieur (un PC par exemple) :

  • le SPI : on s’en sert déjà pour le programmer depuis le PC
  • l’I2C, dénommé TWI pour Two Wires Interface
  • la liaison série

Le SPI étant occupé (ce qui n’empêcherait de l’utiliser quand même, mais c’est plus complexe), nous allons l’écarter pour l’instant. Faire de l’I2C avec un PC est possible, mais il faudrait développer un petit programme pour cela. On verra cela plus tard. Reste donc pour aujourd’hui notre bonne vieille interface série, que nous allons pouvoir tester côté PC avec l’aide d’Hyperterminal ou de tout autre émulateur de terminal de votre choix.

ATMega8 et communications série

L’ATmega8 dispose de manière interne d’un circuit dénommé USART pour Universal Synchronous and Asynchronous serial Receiver and Transmitter. A noter qu’on entend couramment parler d’UART, mais qu’ATMEL a ajouté ici un S pour Synchronous. Ce qui veut dire que cette interface peut servir à faire aussi bien de la communication série asynchrone courante, c’est à dire avec les bits de start et de stop, mais aussi synchrone dans laquelle les bits de données sont envoyés de manière cadencée par un signal de clock, piloté par un maître de cérémonie. Mais ce sera aussi pour une autre fois, car le PC ne sait pas faire cela de manière simple.

Pour tout savoir sur l’USART, ses fonctionnalités, sa mise en oeuvre,... rendez-vous à la page 130 du datasheet.

Communications série et RS232

Il s’agit là de deux choses différentes, mais que l’on a tendance à amalgamer dans le monde des PC.

Communication série désigne un mode de transmission dans lequel on envoie les bits des données en séquence (en série) sur un même fil, par opposition à communication parallèle où les différents bits sont envoyés en même temps sur des lignes parallèles (d’où le nom).

RS232 désigne une norme électrique pour les communications série, qui spécifie les tensions représentant les états logiques sur les fils véhiculant les signaux et données. En simplifié, sur une ligne RS232, les tensions sont de -12V et +12V. La raison est tout simplement d’avoir des tensions suffisamment différentiables pour être capables de distinguer les états logiques même en cas d’affaiblissement ou de dégradation des signaux électriques (sur une liaison de grande longueur par exemple).

Oui, et alors ?

Et bien, le problème c’est qu’un micro-contrôleur, comme la plupart des puces logiques TTL, ne connait que 0 et 5V pour ce qui est des signaux qu’il gère. Ce qui veut dire que les signaux présents sur les pins RxD et TxD ne prendront que ces deux valeurs. Conclusion : ce n’est pas du RS232, et ça ne pourra pas causer avec un PC via sa prise COM1 par exemple.

Max à la rescousse

Non, ce n’est pas un pote costaud. Juste un petit boitier fait par la société MAXIM (entre autres) et qui permet d’assurer la conversion des signaux entre le monde RS232 et le monde TTL. plusieurs modèles existent dans la gamme, avec des fonctionnalités diverses, et celui qui va nous servir ici les le MAX232. Je ne vais pas le détailler ici, car il suffit de parcourir son datasheet pour tout comprendre, et voir comment il se met en oeuvre.

Le schéma complet

Et bien le voici.

Schéma électronique

On y retrouve plus ou moins le même schéma que celui de l’article précédent. Nous avons cependant été obligés de déplacer deux des LEDs sur un autre port, car les lignes Rx et Tx sont sur les mêmes pins que les bits 0 et 1 du port D. Les deux LEDs correspondantes sont maintenant connectées aux bits 0 et 1 du port C.

La nouveauté réside dans la présence du MAX232 et sa connexion à l’ATmega. Comme vous pouvez le constater, il n’y a pas de quoi s’affoler.

Le montage

Après quelques transformations de notre montage précédent, on arrive à ce résultat :

Le montage expérimental

On y retrouve en partie droite l’ATmega8 avec la liaison avec les LEDs ainsi qu’avec le câble ISP (la nappe multicolore du dessus).

En partie gauche, on y voit le MAX232 flanqué des ses 5 capas, l’arrivée des lignes RS232 en provenance du PC en bas à gauche, et la liaison des lignes série TTL avec l’ATmega8 réalisée par les straps agencés en Z.

Une remarque au passage : j’ai réalisé que le fabriquant du set de straps avait eu la bonne idée d’utiliser les codes de couleurs des résistances pour indiquer la longueur des straps, exprimé en nombre de pas 2,54mm. Hyper pratique quand on utilise une plaquette d’expérimentation. Je sais, il y en a qui vont me dire : "et tu ne l’avais pas compris avant ?". Ben non, mais à ma décharge, jusqu’à présent j’utilisais des fils de récup pour ce genre de travaux, mais j’ai décidé d’utiliser des straps pour ces articles, afin de faire plus propre pour le bien-être du lecteur.

L’application

Cette application fait toujours défiler les LEDs, mais nous permet de le contrôler au moyen de caractères de commande envoyés par un PC sur un de ses port COM. Les commandes disponibles sont :

espace suspend/redémarre le défilement
* enchaîne automatiquement les differentes séquences, et reboucle sur la première en fin de liste. C’est le mode de fonctionnement lors de la mise sous tension
+ passe à la séquence suivante de celle en cours, et boucle sur elle-même
- fait la même chose, mais en passant à la séquence précédente

En plus de cela, le programme affiche un message de bienvenue et la liste des commandes au démarrage, et rend compte de l’exécution de chacune d’entre elles.

Un exemple de session Hyperterminal est illustrée ci-dessous :

Un exemple de session

Pour ceux qui se demandent pourquoi les messages sont en Anglais : non, je n’ai pas pompé ces programmes sur le Net, mais c’est juste parce que mes activités professionnelles me conduisent à travailler sur des projets internationaux. Du coup je ne fais plus gaffe, et rédige mes programmes en Anglais par habitude...

Le programme

Voici le source complet et le Makefile pour avr_gcc.

Source de l’application
Makefile

Comme pour le cadencement du défilement des LEDs vu précédemment, deux stratégies existent :

  • le polling, ou scrutation active, consistant dans le cas de la réception par exemple, à boucler (à vide) tant que le bit indicateur de la réception d’un caractère (RXC) du registre UCSRA reste à 0
  • les interruptions, qui signalent de manière totalement asynchrone les différents événements de la communication (réception d’un octet, buffer de transmission vide, transmission effectuée,...)

Puisque nous sommes déjà au niveau Anakin Adolescent, nous allons directement nous intéresser à la deuxième approche. Des exemples de sources concernant la première sont disponibles dans le datasheet, page 140 par exemple.

L’initialisation de l’USART

Avant de pouvoir l’utiliser, il est nécessaire d’initialiser l’USART, notamment pour définir vitesse et format de communication.

La vitesse se définit dans le registre UBRR, qui se compose des deux registres UBRRH et UBRRL (pour High et Low). la valeur à y placer dépend de la vitesse souhaitée bien entendu, mais aussi de la fréquence du quartz utilisé. Divers tableaux en page 156 du datasheet détaillent tout cela. Faites bien attention à ce propos de n’utiliser que les valeurs pour lesquelles l’erreur de vitesse est inférieure à 0.8 (les valeurs correspondantes sont en gras dans les tableaux).

A noter que l’accès au registre UBRRH est un peu spécial, car son adresse est partagée avec le registre UCSRC. Pour indiquer dans lequel des deux on écrit, il faut position er le bit 7 (commun aux deux) selon (0 pour accéder à UBRRH, 1 pour UCSRC).

L’activation des interruptions USART est contrôlée par le registre UCSRB (USART Control and Status Register B), au moyen des bits RXCIE, TXCIE et UDRIE (voir page 152 du datasheet).

Pour activer la réception, il faut positionner le bit RXEN (Receive Enabled) du registre de contrôle UCSRB. A partir de ce moment, le bit PD0 du port D n’est plus accessible en tant qu’I/O. Puisqu’on veut recevoir une interruption lorsqu’un caractère a été reçu, il faut aussi positionner le bit RXCIE (Receive Complete Interrupt Enabled) de ce même registre.

La partie transmission de l’USART est activée en positionnant le bit TREN (Transmit Enabled) du registre de contrôle UCSRB. A partir de ce moment, le bit PD1 du port D n’est plus accessible en tant qu’I/O. Nous allons également utiliser l’interruption UDRE (USART Data Register Empty), en positionnant le bit UDRIE du registre UCSRB.

C’est à peu près tout. Il faut savoir maintenant que les données reçues ou à envoyer transitent par le registre UDR (USART Data Register).

Mais, allez-vous dire, si on veut envoyer un caractère et qu’un autre arrive pendant ce temps, on va perdre la donnée à transmettre. Et bien non. Car même si ce registre de donnée apparait à une seule adresse, il correspond en réalité à deux registres internes distincts pour la réception et la transmission. Les gars d’ATMEL sont tout sauf des idiots 🙂

Réception de données

Lorsqu’un caractère est complètement reçu par l’USART, celui-ci le transfère dans le registre UDR (USART Data register), positionne le bit RXC du registre UCSRA (USART Control and Status Register A) et lève l’interruption Receive Complete Interrupt.

Il nous suffit d’écrire un handler pour cette interruption afin de traiter le caractère dès qu’il est disponible. A noter que traiter le caractère ne veut pas dire traiter la commande. En effet, le code s’exécutant dans un handler d’interruption doit être le plus court possible, étant donné qu’on a plus ou moins tout arrêté ailleurs pour l’exécuter. Nous nous contenterons donc d’ajouter ce caractère dans une file d’attente dans laquelle l’application ira puiser pour traiter les commandes correspondantes.

Plutôt que de tartiner des pages ici, je vous invite à analyser le code et les commentaires du source (histoire que je ne me sois pas em..dé à le faire pour rien 😉. Vous noterez qu’après les initialisations, le main de l’application se contente de boucler sur deux actions :

  • attendre que des caractères soit présents dans la file d’attente de réception
  • exécuter la commande correspondante

Transmission de caractères

Le procédé est assez similaire à la réception. Une petite différence cependant au niveau de la philosophie de l’interruption UDRE, qui est levée dès que le registre de données est vide, et tant qu’il est vide (et non pas lorsque son état change).

A la différence de la réception, nous n’allons donc activer cette interruption que lorsqu’on place le premier caractère à transmettre dans la file d’attente, et la désactiver dès qu’il n’y aura plus rien en attente. Cette interruption fait donc office de pompe à données.

La file d’attente de transmission est gérée de la même manière que celle de réception. Pour envoyer des caractères depuis le corps de l’application, il suffira de les y ajouter, en activant l’interruption pour le premier de la file.

Quelques remarques

Sections critiques

Vous remarquerez en lisant le source, que les instructions d’ajout ou de retrait de données dans ou depuis les files d’attente sont encadrées par des instruction CLI et SEI, qui respectivement masquent et autorisent les interruptions.

Pourquoi cela ? Tout simplement pour implémenter une section critique, qui ne doit être interrompue sous aucun prétexte. Sans cette précaution, la file concernée pourrait être laissée dans un état incohérent si au milieu du retrait d’un caractère par exemple, un autre arrive, provoquant l’exécution du code de l’interrupt handler du receive, et donc modifiant les informations d’état de la file. En masquant les interruptions, cet événement est mémorisé puis notifié dès que le SEI sera exécuté. Inutile de dire que ces sections critiques doivent être les plus courtes possibles.

volatile

Un certain nombre de variables sont déclarées avec l’attribut volatile. Qu’est-ce que cela signifie ?

Rien en rapport avec la grippe aviaire en tout cas !! C’est une indication au compilateur de ne pas chercher à optimiser l’assembleur généré en cachant la valeur dans un registre de travail, car cette valeur peut changer à tout moment. Chaque accès à la variable doit donc relire la valeur depuis la mémoire.

Pourquoi demander cela ? Tout simplement parce que les variables concernées sont partagées entre du code normal et du code d’interrupt handler, et qu’elles sont de surcroit modifiées dans ces handlers. Etant donné le côté hautement asynchrone des interruptions, cela veut dire que ces variables peuvent changer de valeur à tout moment. Donc, il faut systématiquement les relire.

Soyez bien attentifs à ce genre de détail : on a vu des robots faire n’importe quoi pour une variable qui aurait dû être déclarée volatile et qui ne l’a pas été. Et bonjour pour détecter la cause du problème quand on n’a pas l’habitude...

Conclusion

A noter que l’USART permet de faire des choses encore plus sophistiquées, comme par exemple de communiquer entre plusieurs ATmega en mode maître/esclave. Tous sont connectés en parallèle sur la mème ligne série, un peu comme sur un bus. Un mécanisme d’adressage est alors disponible pour que le maître désigne le destinataire des données. Attention cependant, il ne s’agit pas d’un bus à part entière, car on ne peut pas librement changer les rôles en cours de session. Pour en savoir plus, consultez la section Multi-processor
Communication Mode
page 148 du datasheet.


Liste des composants

Les mêmes que pour le montage précédent, plus ceux-ci :

- Etiquette -- Valeur -
C7 1µF polarisée
C8 1µF polarisée
C9 1µF polarisée
C10 1µF polarisée
C11 1µF polarisée
IC2 MAX232

Les capas polarisés utilisées ici sont des tantales : c’est plus petit. Ca doit marcher également avec des électrochimiques (qui seront peut-être moins chers).

Si votre MAX232 porte le suffixe "A" (MAX232A), les capas C7 à C11 peuvent (doivent ?) être remplacées par des 100nF polarisées également.


Suite à une question qui m’a été posée, voici quelques compléments concernant la connexion physique au PC. Plusieurs solutions existent comme toujours : on peut faire un câble équipé d’une prise DB9 femelle pour se brancher directement sur le PC.

Personnellement je préfère une autre approche, qui parait plus complexe, mais qui s’avère beaucoup plus versatile (à mon sens) :
 équiper mon montage d’une prise DB9 mâle, câblée à l’identique de celle du PC. Dans le cas de nos montages d’expérimentation, on équipera la DB9 mâle de fils d’une dizaine de centimètres environ, préparés à l’autre extrémité de manière à pouvoir s’enficher facilement dans les contacts de la plaquette.
 réaliser un câble dit "croisé" ou "null modem", reliant les deux. Ce câble est équipé de 2 prises DB9 femelles, et fait office de canal de communication entre 2 ordinateurs au sens large en connectant le signal transmit de l’un sur l’entrée receive de l’autre et vice-versa.

Reste à savoir quelles sont les broches à utiliser. Sur une prise DB9 mâle, nous allons nous intéresser aux signaux suivants :
 receive, sur la broche 2
 transmit, sur la broche 3
 masse, sur la broche 5
On peut ignorer le reste, car soit ce n’est pas connecté, soit cela correspond aux signaux de handshake hardware, qui sont rarement utilisés (en tout cas pas par les µcontrôleurs.

Pour ce qui est du câble croisé, c’est simple : on connecte entre elles les broches 5, et on relie la broche 2 de l’une à la broche 3 de l’autre et Lycée de Versailles.

Et comment on sait quelle est la broche n sur la prise ? Très simple : on prend une loupe et on cherche le numéro. Il est souvent gravé côté soudures, ou parfois côté broches. Ca dépend du fabricant.

Autre solution : Google étant ton ami comme toujours, taper "RS232 DB9 pinout", et servez-vous. Cette page-là est par exemple très claire et complète :

http://www.zytrax.com/tech/layer_1/cables/tech_rs232.htm

Et en plus, c’est la première.

Vos commentaires

  • Le 6 décembre 2013 à 10:47, par Julien H. En réponse à : S’ouvrir au monde

    A ce jour, cela reste le seul article complet pour l’initialisation des registres d’un ATmega8. Certains codes récents sont pour ATmega168 (celui des Arduino) et les registres ne sont plus les mêmes.

    Juste une précision pour ceux qui ne sont pas à 9600 bauds ou qui n’ont pas un quartz à 8 MHz. Notamment ceux qui utilisent l’oscillateur interne. Tout d’abord rassurez-vous : malgré les mauvaises langues qui disent que l’UART ne fonctionne pas sans quartz à 8 MHz, si ça fonctionne très bien.

    Il existe dans libc une façon de faire calculer la valeur de UBRRH et UBRRL en donnant la fréquence d’horloge et la vitesse de transmission :

    #define F_CPU  1000000
    #define BAUD 9600
    
    // ... le reste comme dans l'article
    
    #include <util/setbaud.h>
    
    void usart_init(void) {
    // ... le reste comme dans l'article sauf :
        UBRRH = UBRRH_VALUE;
        UBRRL = UBRRL_VALUE;
    
    // et on rajoutera même le coefficient X2 : 
        UCSRA = (1 << U2X);
    
    }

    Voilà, en espérant que ça serve à d’autres.

    Répondre à ce message

  • Le 11 février 2007 à 19:11, par ? En réponse à : S’ouvrir au monde

    Bonsoir

    Juste un mot ,
    Je viens de tester ton (votre) programme avec AVRStudio 4 et Hyperterminal 6.3, il marche correctement

    Bravo, voilà un exemple concrèt 😉)

    a+
    Raphael

    • Le 11 février 2007 à 19:36, par Eric P. En réponse à : S’ouvrir au monde

      Merci pour l’info et l’appréciation 😉

      Cordialement.

      PS : Le tutoiement est autorisé 🙂

    Répondre à ce message

Un message, un commentaire ?

modération a priori

Attention, votre message n’apparaîtra qu’après avoir été relu et approuvé.

Qui êtes-vous ?

Pour afficher votre trombine avec votre message, enregistrez-la d’abord sur gravatar.com (gratuit et indolore) et n’oubliez pas d’indiquer votre adresse e-mail ici.

Ajoutez votre commentaire ici

Ce champ accepte les raccourcis SPIP {{gras}} {italique} -*liste [texte->url] <quote> <code> et le code HTML <q> <del> <ins>. Pour créer des paragraphes, laissez simplement des lignes vides.