# Micro contrôleurs AVR/Les communications en tout genre

Nous avons l'intention dans ce chapitre d’aborder divers types de communication : série asynchrone, série synchrone, I2C et USB.

## Communication série asynchrone ou RS232

La partie du circuit spécialisée gèrant la liaison série (ou RS232) s’appelle une UART.
On rappelle que ce type de liaison série permet d'envoyer des informations sans horloge de référence. Elle est à un logique au repos. Une trame est composée des 8 bits à transmettre, précédé d'un bit de start ("0" logique), et suivi par au moins un bit de stop ("1" logique). Le bit de stop peut être doublé et éventuellement précédé par un bit de parité. Pour une parité paire, le bit de parité est mis à "0" quand le nombre de "1" est pair tandis qu'une parité impaire met à "0" pour un nombre impair de "1".
La vue de l'image ci-contre semble indiquer le contraire de ce que je suis en train d'expliquer. Mais n'oubliez pas que du point de vue électrique, un niveau logique "0" est représenté par une tension de +3 V à +25 V et un niveau logique "1" par une tension de −3 V à −25 V (codage NRZ). Ordinairement, des niveaux de +12 V et −12 V sont utilisés.
Comme nous allons utiliser pour nos essais des platines Arduino pour lesquelles la liaison série est réalisée à l'aide de l’USB, les problèmes de tension n'ont pas lieu.
La liaison série est très populaire à cause de son évolution justement avec l'USB et surtout que tous les systèmes d'exploitation proposent un logiciel pour communiquer par la liaison série. Il est appelé hyperterminal sous Windows. Même si nous utilisons essentiellement Linux pour nos essais, nous continuerons à l'appeler hyperTerminal.
Voici quelques brochages utiles qu’il est facile de retrouver sur Internet.
| rs232 basse tension | RXD | TXD |
| UNO | PD0(Arduino:0) | PD1 (Arduino:1) |
| LEONARDO | PD2 (Arduino:0) | PD3 (Arduino:1) |
| Pro Micro (Sparkfun) | PD2 (Arduino:0) | PD3 (Arduino:1) |
* Brochage UNO (PDF)
* Brochage pro-micro (Sparkfun)

### Les registres associés

Le « n » apparaissant dans ces dessins peut prendre la valeur 0 ou 1 dans l'ATMega328.
Les bits du registre UCSRnA sont :
* RXCn : réception complète
* TXCn : transmission complète
* UDREn : USART Data Register Empty (vide)
* FEn : Frame Error
* DORn : Data OverRun
* UPEn : USART Parity Error
* U2Xn : est expliqué dans le dessin
* MPCMn : Multi Processor Communication Mode
Les bits du registre UCSRnB sont :
* RXCIEn : interruption quand réception complète autorisée
* TXCIEn : interruption quand transmission complète autorisée
* UDRIEn : interruption quand UDRn est vide autorisée
* RXENn : autorisation de la réception
* TXENn : autorisation de la transmission
* UCSZn2 : taille de caractère quand combiné avec UCSZn1:0
* RXB8n : 9° bit de réception au cas où ce mode est choisi
* TXB8n : 9° bit de transmission au cas où ce mode est choisi

### Une librairie d'utilisation

Nous avons trouvé sur Internet la librairie suivante qui a été écrite pour un ATMega168. Nous l'avons fait fonctionner telle quelle sur un ATMega2560 et la platine Arduino correspondante. Nous la publions avec quelques modifications.
Cette librairie a été utilisée avec succès avec une platine Arduino UNO et MEGA2560. L'horloge est aussi à 16 MHz comme ce qui est supposé par cette librairie. L'exercice 8 ici fait une lecture d'une valeur supposée être fournie par deux caractères, valeur variant entre 00 et FF, pour régler le rapport cyclique.

### Exercices

Un exercice a déjà été proposé dans ce livre : PWM rapide dans le chapitre sur le timer 0.
En voici un autre assez proche.

#### Travail à réaliser en C

Pour cette partie, on utilise l'IDE Arduino mais le langage C pur (avec donc un main).

##### Changer la vitesse de transmission

La librairie présentée fonctionne pour une vitesse de transmission de 9600 bauds. Cette vitesse est choisie avec les registres UBRR0H et UBRR0L comme indiqué dans les commentaires.
Modifier la librairie pour la faire fonctionner à 19200 bauds. Écrire un programme complet qui permet de réaliser un test avec cette nouvelle vitesse dans l'hyperterminal Arduino.
SerialRead est une fonction bloquante ce qui permet de ne pas mettre de temporisation dans ce programme. Ce programme ne fait que recopier ce qui arrive par la liaison série dans la liaison série. Dans l'hyperterminal Arduino ce n’est pas spectaculaire, mais c’est bien l'Arduino qui fait ce travail de recopie.
Après cette mise en jambes, venons-en aux problèmes sérieux...

##### Changer la fréquence sur un buzzer

On désire changer la fréquence sonore d'un buzzer à l'aide du mode CTC du timer 2. La valeur envoyée pour déterminer la fréquence sera systématiquement envoyée par la liaison série sous forme de deux caractères 0,...,9,A,...,F. Dans ce genre de problème, la difficulté est de comprendre la différence entre des caractères et des valeurs.
* Écrire un sous-programme "void usart_puts_hexa(unsigned char val)" destiné à transformer la valeur val (hexadécimale) en deux caractères affichables (deux parcequ'on est sur 8 bits). Tester avec un programme principal.
La difficulté pour un étudiant, dans ce genre de sous-programme, est de bien faire la distinction entre valeur numérique et son affichage sous forme de chaîne de caractères.
Faites un effort pour comprendre l'apparition de '0' et de '7' !
Nous allons maintenant réaliser l'opération inverse : lire deux caractères (en représentation hexadécimale) qui arrivent par la liaison série et les transformer en une valeur numérique correspondante sur un octet.
* Un sous-programme "unsigned char usart_gets_hexa" sera donc chargé de lire ces deux caractères d’en vérifier la syntaxe et d’en retourner la valeur. Écrire ce sous-programme et le tester avec le sous-programme de la question précédente. On affichera "--" en cas d'erreur de syntaxe. TOUT SE PASSE PAR LA LIAISON SERIE. Pouvez-vous modifier la fonction un peu naïve ci-dessous pour qu'elle gère correctement les erreurs (car elle ne le fait pas !!!!) même si elle fonctionne parfaitement :
Il y a plusieurs manière de gérer les erreurs :
* retourner une structure avec un champ réservé aux erreurs
* retourner un entier sur 16 bits. Les 8 bits de poids fort seront réservés aux erreurs
Le "GG" de tout à l’heure ne passera plus mais le fait de tester tab[0] et tab[1] après calcul est une erreur. En effet quelques caractères pourront passer les tests alors qu’ils ne sont pas corrects. Ce doit être le cas de tous les caractères entre '9' et 'A'. Pour le moment nous considérons ce sous-programme comme assez fiable pour nos besoins. Nous le modifierons plus tard.
Et l’apothéose finale ....
* Changer votre programme en utilisant les sous-programmes développés pour la question précédente pour qu’il réalise une fréquence audible sur le bit OC0A du PORTB variant en fonction de la valeur reçue (par la liaison série).
Le bit OC0A correspond au bit B6 pour l'ATMega328 de l'Arduino UNO.

#### Exercice 2 : Commande d'un servomoteur

Un potentiomètre est branché sur un Arduino sur l'entrée A0. Un servomoteur est branché sur un deuxième Arduino (sortie (9). Les deux Arduinos sont reliés entre eux par une liaison série. Écrire les deux programmes en C pour les deux Arduinos pour que le potentiomètre commande le servomoteur par l'intermédiaire de la liaison série.
Indications :
* la résolution de ce problème nécessite quelques connaissances sur la conversion analogique numérique. La broche A0 dont il est question ici est l'entrée AN0. Ceci peut sembler être une information redondante mais elle est seulement vraie pour les Arduino UNO (pas LEONARDO par exemple).
* La commande d'un servomoteur par un potentiomètre est étudiée dans le chapitre sur la conversion analogique numérique.
* La broche (9) de l'Arduino est PD1. Mais elle est aussi OC1A. Cela signifie clairement que le timer que l'on utilisera pour commander le servomoteur est le timer1
* La commande d'un servomoteur par le timer1 a déjà été traité comme exercice 3 dans le chapitre sur le TIMER1
Voici le code de l'Arduino sur lequel est branché le potentiomètre :
Voici maintenant la partie code de l'Arduino sur lequel est branché le servomoteur :

### Cas particulier de l'Arduino Leonardo

La particularité de l'Arduino Leonardo est la gestion de la liaison série par l'USB.
Nous allons utiliser du code du site pjrc.com : USB serial
Voir aussi WIKI IUT Troyes

## Communication série synchrone ou SPI

Un article de wikipédia Serial Peripheral Interface présente les communications SPI qui nous intéressent dans cette section.
Interface SPI décrit des programmes que nous reprendrons.

### Description du protocole

Le protocole SPI est synchrone : une horloge synchronise l'échange de données. Dans tout échange on définit un Maître qui réalise l'horloge et un Esclave. L'esclave (il peut y en avoir plusieurs) est choisi avec une entrée spéciale appelée SS (Slave Select). À ce point, nous connaissons deux broches du SPI : l'horloge appelée SCK et SS. Il en manque deux pour être complet :
* MISO : Master in Slave Out : un des esclaves envoie des données au maître
* MOSI : Master Out Slave In : le maître envoie des données à un des esclaves qu'il a choisi avec le Slave Select)
Ces deux broches laissent entendre un échange full duplex (complet dans les deux sens). Voici donc comment les choses se passent avec un Maître et un Esclave :
Ce schéma ne représente que le protocole électrique. En général il y a un protocole logique au-dessus, c'est-à-dire un moyen pour le maître de demander certaines données et pas d'autres à l'esclave. Cela se fait avec une série d'octets prévue par le constructeur de l'esclave. Il vous faudra donc lire la documentation de l'esclave (en général un capteur) pour connaître ce protocole.
Si vous avez plusieurs esclaves une configuration de ce genre est à choisir :
Comme nous allons utiliser des cartes Arduino pour nos expérimentations, nous allons commencer par donner leurs brochages.

### Quelques brochages pour les cartes Arduino UNO, Leonardo et Mega2560

Voici résumé dans un tableau le brochage des cartes qui nous intéressent :
| SPI | MISO | MOSI | SCK | SS |
| UNO / NANO | PB4 (Arduino:12) | PB3 (Arduino:11) | PB5 (Arduino:13) | PB2 (Arduino:10) |
| LEONARDO | PB3 (ICSP:1) | PB2 (ICSP:4) | PB1 (ICSP:3) | --aucune-- |
| MEGA2560 | PB3 (Arduino:50) | PB2 (Arduino:51) | PB1 (Arduino:52) | PB0 (Arduino:53) |
| Pro Micro (Sparkfun) | PB3 (Arduino:14) | PB2 (Arduino:16) | PB1 (Arduino:15) | --aucune-- |
Quel est l'impact sur le Leonardo du manque de broche dédiée à SS ? En principe aucun parce que cette broche SS n'est utile que si l'on désire utiliser le Leonardo en esclave. Vous pouvez utiliser n'importe quel autre bit de port pour sélectionner des esclaves. Mais il faudra les gérer par vous-même.

#### Particularité du Leonardo

Comme on peut le voir dans ce tableau, les broches utilisées pour SPI sur la Leonardo ne sont pas reliées aux connecteurs mais directement sur le programmateur ICSP. Pour éviter des recherches sur internet, nous rappelons la connectique associée des six broches avec les deux photos ci-contre.
Voici par exemple ci-contre la connectique ICSP présente sur une carte Arduino pour laquelle MISO et MOSI sont inversés par rapport à la figure ci-dessus.

### Mise en œuvre matérielle

Le problème de la polarité d'horloge est important : un maître et un esclave peuvent être incapabes de se comprendre s'ils n'ont pas la même polarité. Le dessin de wikipédia est redonné ici comme référence.
Les registres correspondants à la description matérielle sont maintenant présentés et seront suivi par un schéma de l'architecture correspondante. En ce qui concerne le registre SPCR :
* SPIE: pour autoriser les interruptions SPI
* SPE: SPI Enable, doit être positionné à 1 pour toute opération SPI
* DORD: Data Order, 1 pour une transmissin du poids faible en premier
* MSTR: Master/Slave Select, 1 pour le positionnement en maître. Ce bit nécessite d’être en accord avec SS (Slave Select)
* CPOL: Clock Polarity, 1 pour SCK haut quand idle, 0 pour SCK bas quand idle.
* CPHA: Clock Phase, détermine si les données sont échantillonnées sur le premier front ou le deuxième front de SCK.
Pour le registre SPSR :
* SPIF: est le drapeau d'interruption
* WCOL: Write COLlision Flag
Le reste est donné dans le dessin.

### Travail en C sur le SPI

Les programmes C donnés dans cette section n'ont pas encore été testés. Nous enlèverons cette remarque dès que ce ne sera plus le cas.
De toute façon il vous faudra adapter le brochage en fonction de la platine Arduino utilisée.
Il est facile de trouver un programme d'exemple en C sur internet. En voici un exemple serial peripheral interface in avr microcontrollers :
Trouvez un esclave SPI et essayez ce programme. Si aucun esclave SPI n'est disponible, réalisez-en un avec une autre platine Arduino.
On ne vous demande pas de comprendre la partie interruption SPI de ce programme. Seule la partie Timer0 est à comprendre puisqu’il faut correctement brancher une LED dont la luminosité doit changer en fonction de ce qui est envoyé par le SPI.

#### Exercice 2

En vous aidant de la documentation du brochage des quelques Arduino plus haut, on vous demande de :
1°) modifier le programme ci-dessus (uniquement void SPISlaveInit(void)) pour qu’il fonctionne correctement avec un MEGA2560 avec une horloge SPI égale à la fréquence système divisée par 8.
2°) trouver le mode de fonctionnement correspondant ?
3°) Étant pointilleux sur les principes, on vous demande de modifier aussi le sous-programme de réception, sachant que SPIF est un drapeau comme son nom l'indique.

### Lecture des données provenant d'une caméra Pixy2

La caméra Pixy2 est très utile si vous voulez faire des challenges de robotique mobile. Elle est capable d'apprendre à reconnaître des objets, de les traquer et surtout de fournir un certain nombre de données sur le position et la taille des objets reconnus. Par exemple, dans la coupe de France des IUT GEII, le problème est de reconnaître des balles de tennis et de les mettre du côté adverse. C'est typiquement ce qui peut être fait à l'aide d'une caméra Pixy. Mais l'utilisation d'autres caméras est naturellement possible. Une vidéo de cette coupe de France est disponible maintenant ainsi que son Règlement 2020.
Nous allons étudier dans cette section la communication SPI avec la caméra. Il faut savoir qu'il existe une librairie C++ fonctionnant directement sur Arduino et qui s'installe directement dans l'environnement Arduino. La caméra est fournie avec un câble qu'il est possible de brancher directement sur le programmateur ICSP d'une carte Arduino. Le protocole utilisé avec ce câble est évidemment le protocole SPI mais il a une particularité, c'est qu'il n'a pas besoin d'une broche supplémentaire pour faire le "Slave Select". Il est possible d'utiliser d'autres protocoles série et en particulier le SPI avec Slave Select mais aussi la communication série RS232 ou l'i2c.
En toute franchise, nous en sommes arrivés à court-circuiter la librairie Arduino parce que nous avons commencé à résoudre ce problème dans un FPGA et que le processeur que l'on utilise dans le FPGA n'est pas complètement compatible avec un Arduino. Nous avons donc voulu le réaliser avec une carte Arduino parce que des essais directs à partir du FPGA n'ont pas été concluants. Pour tout vous dire, les essais sur Arduino nous ont pris un certain temps aussi... et c'est pour cela que nous publions ici nos résultats.
Pourquoi avons-nous eu autant de difficultés ?
* première raison, nous ne possédons pas d'outils pour déboguer le SPI (oscilloscope ou autre)
* deuxième raison, après avoir résolu partiellement la première raison en affichant des résultats par la liaison série, nous ne nous sommes pas aperçu tout de suite que les deux octets d'entête de la réponse arrivaient mais avec du retard. Une fois que nous avons remarqué ce détail, nous avons écrit un sous-programme qui laisse tomber ce qu'il reçoit par le SPI tant qu'il n'a pas reçu ces deux octets.
* troisième raison, nous n'avons pas détecté dans le code source de la librairie Arduino ce type d'attente/synchronisation.
Il est maintenant grand temps de passer à la pratique.

#### Protocole d'échange

La connaissance du protocole d'échange entre la caméra et votre processeur est importante. Ce protocole ne dépend pas de la façon matérielle choisie : i2c, SPI sans SS, SPI avec SS, rs232. Voici comment il est documenté dans le document officiel (très partiellement traduit ici). Nous allons examiner par exemple le cas de la recherche de la résolution de la caméra.
* La demande réalisée par votre processeur doit être conforme à la requête standard pour ce type de données :

##### Requête

| Byte | Description | Value(s) |
| 0 - 1 | 16-bit sync | 174, 193 (0xc1ae) |
| 2 | Type de paquet | 12 |
| 3 | Longueur de données | 1 |
| 4 | Type (non utilisé réservé pour les versions futures) | 0 - 255 |
Cette suite d'octets est obligatoirement à envoyer si vous voulez obtenir la réponse correspondant à la résolution de l'image en pixels de la part de la caméra Pixy2. Cette résolution pouvant être réglée à l'aide de PixyMon, il vous faut être sûr de la connaître si vous voulez positionner correctement un objet (par rapport à vous) dont les coordonnées vous sont retournées par la caméra.
* La réponse de la caméra doit être conforme à :

##### Réponse

| Byte | Description | Value(s) |
| 0 - 1 | 16-bit sync | 175, 193 (0xc1af) |
| 2 | Type de paquet | 13 |
| 3 | Longueur des données | 2 |
| 4 - 5 | 16-bit checksum | sum of payload bytes |
| 6 - 7 | 16-bit Largeur d'image (en pixels) | 0 - 511 |
| 8 - 9 | 16-bit Hauteur d'image(en pixels) | 0 - 511 |
Nous allons maintenant nous intéresser à la façon de gérer ce type de données en langage C. Deux façons nous viennent à l'esprit :
* Utilisation d'un type structure en C
* Utilisation d'un tableau
Les deux méthodes ont leurs avantages et inconvénients. L'intérêt d'une structure est qu'elle permet d'accéder aux champs directement par leurs noms. Son inconvénient est qu'en général les sous-programmes utilisés pour envoyer ou recevoir des données utilisent des tableaux. Il faudra donc transtyper et ceci vous vaudra un warning de la part du compilateur.
L'inconvénient du tableau est qu'il ne faut pas vous tromper pour mettre les données : regardez comment est mis la valeur 0xC1AE dans le tableau. Vous aurez évidemment le même problème pour retrouver les données.
Nous vous laissons choisir.

#### Exercice 3

Nous nous proposons d'écrire un programme C capable de gérer le SPI sans "slave select" (puis avec) pour récupérer les données de position de de largeur et grosseur de l'objet détecté. Les données seront envoyées par la liaison série pour les tests.
* Chercher dans cette section et dans la précédente du code pour utiliser la liaison série et la liaison SPI. Éventuellement vous pouvez simplifier le code sachant que la liaison série ne sera utilisée que dans un sens.
* C'est votre processeur qui sera le maître de la liaison SPI. La caméra Pixy2 sera donc votre esclave.
** Dans un premier temps on utilisera la liaison SPI sans "Slave Select". Votre maître SPI est sensé avoir une horloge à 2 MHz (horloge processeur / 8) au maximum et être en mode 3 (bits CPOL=1 et CPHA=1 du registre SPCR. Modifier votre programme d'initialisation trouvé plus haut pour répondre à ces contraintes.
** Écrire maintenant des sous-programmes génériques pour écrire un ou plusieurs octets ainsi pour demander une lecture d'octets réponses de la Pixy2.
** Examinez dans la documentation appropriée le protocole d'échange pour getResolution et pour getBlocks(sigmap, maxBlocks) et construisez des types de données pour réaliser correctement ces protocoles ainsi que les sous-programmes correspondants. Testez.
** Arrivés ici, vous remarquez que la communication ne se fait pas correctement. Pour un fonctionnement correct, on vous demande d'écrire un sous-programme qui commence à lire les données de la Pixy que quand il vient de recevoir l'entête de la réponse 0xC1AF sachant que la réception se fait d'abord par 0xAF puis par 0xC1. Il serait très bien de mettre un compteur qui limite à 50 les données lues en attente de 0xC1AF. En cas de limite dépassée, on sort du sous-programme avec la valeur 0 pour dire que l'on a rien lu. Dans le cas contraire, c'est le nombre d'octets lus qui est retourné.
* Modifier le programme précédent pour utiliser le Slave Select. On rappelle que n'importe quelle broche de l'Arduino peut servir à cela. L'utilisation de la broche notée SS ("Slave Select") dans la documentation n'est obligatoire que si votre processeur est un esclave SPI.
Nous donnons en vrac du code fonctionnel utilisant le protocole SPI sans Slave Select dans un Arduino Uno.
Étant donné que l'entête retournée est toujours la même 0xC1AF il est possible de supprimer le paramètre du sous-programme correspondant :
Il vous faut alors modifier tous les appels à ce sous-programme.
La structure de ce sous-programme est discutable : en principe il faut attendre les deux octets 0xAF et 0xC1 d'affilés alors que là ils peuvent être séparés par des octets indésirables ! La résolution et le test de ce problème sont laissés au lecteur.