*Title: Compte-rendu de la formation AVR *Author: Marcel & Vinzoo & djerem ----------------------------------------------- *TOC ----------------------------------------------- * Présents [Romain Becquet] [Clément Demonchy] [Jérémy Dufour] [Brice Gagnage] [Vincent Genèves] [Thomas Lambert] [Christophe Le Blanc] [Nicolas Schodet] * Généralité Les AVR sont comme des PCs mais en plus petits. Ils ont de nombreux avantages : [Simple] On les câble et pouf ça marche. Pas besoin d'ajouter des composants. Il suffit juste de le programmer pour contrôler les entrées-sorties. [Libre] Ils sont compatibles avec les logiciels libres. [Interface] On peux les faires fonctionner avec pleins d'autres composants. *Image-center: schema_avr.png ~Schéma structurel d'un AVR~ Les AVR s'alimentent avec du 5V. Deux entrées de l'AVR peuvent être utilisées pour le faire fonctionner à partir d'une horloge externe, généralement générée par un quartz. Cela permet d'augmenter la vitesse de l'AVR tout en le rendant plus précis. La communication de l'AVR avec un PC peut s'établir via le port série. Attention : il faut penser à convertir le -12/+12V du PC en 0/5V pour l'AVR (utilisation d'un max232). * Les Entrées-Sorties Les entrées-sorties sont regroupées par port, chacun composé de 8 pattes. La gestion des ports s'effectue donc par mot de 8 bits. Chaque port dispose de 3 registres de 8 bits, chaque bit correspondant à une patte du port : [PIN] C'est un registre de lecture. Il reflète l'état courant du port. Il est donc utilisé pour lire les entrées-sorties du port. Il est accessible en lecture seulement ; [DDR] Il permet de sélection la direction du port : * en entrée : dans ce cas là, il vaut 0. Cas par défaut pour tous les ports ; * en sortie : dans ce cas là, il vaut 1. [PORT] Il a une fonction double : il permet soit d'activer un ''pull-up'' si on est en entrée, soit de définir la valeur des pattes, si on est en sortie. #---SET-CONTROL tableparser=new ! Utilisation du registre PORT ! !-------------------------------! ! DDR ! PORT ! Résultat ! !-------------------------------! ! 0 ! 0 ! Pas de pull-up. ! ! ! ! Haute impédance. ! !-------------------------------! ! 0 ! 1 ! Présence d'une ! ! ! ! pull-up. ! !-------------------------------! ! 1 ! 0 ! Sortie = 0 ! !-------------------------------! ! 1 ! 1 ! Sortie = 1 ! !-------------------------------! ** Exemple d'utilisation du port A *** Avec une diode Une diode est branchée sur la patte A0 du port A. On veut la faire clignoter. Voici l'algorithme du programme qu'il faudrait utiliser : DDRA = 1 /* active le mode sortie sur la patte 0 du port A */ while (1) /* on boucle à l'infinie */ { PORTA = 1 /* allume la diode : passe la patte 0 du port A à la valeur 1 */ Delay(tps) /* attend de tps (histoire de voir la diode allumée. Fonction delay déjà programmé chez SI2E */ PORTA = 0 /* éteint la diode : passe la patte 0 du port A à la valeur 0 */ Delay(tps) /* attend */ } Pensez à protéger votre diode d'une résistance. *** Avec deux diodes Maintenant, on souhaite ajouté une deuxième diode, que l'on va brancher sur le port A3 et qui clignoterait en opposition de phase avec la première diode. Si on reprend le même raisonnement que précédemment, il nous suffirait de recopier le code et de changer : DDRA = 1 -> DDRA = 8 /* 2³ = 8, on veut mettre 1 dans le 3ème bit */ PORTA = 1 -> PORTA = 0 PORTA = 0 -> PORTA = 8 Mais en faisant ça, on va changer les autres bits du PORTA : en effet mettre PORTA à 8, donnera 00001000 en binaire ce qui nous fera perdre le bit 0 qui valait 1. Cela aura donc pour conséquence de ne plus allumer la diode branchée sur A0. Pas très pratique. Afin d'éviter de changer les bits des autres pattes du port, il faut donc utiliser les fonctions logiques : [Mettre un bit à 1] Il faut faire un ''OU'' ; [Mettre un bit à 0] Il faut faire un ''ET'' du registre PIN avec un mot de 8 bits qui aura des 1 partout sauf pour le bit qu'on veut changé. /* Passer de 1 à 0 le 3ème bit du PORTA. ~8 correspond à l'inverse de 8. En binaire, ça donne l'inverse de 00001000, c'est à dire 11110111. Notez que seul le 3ème bit est à 0 car c'est celui là qu'on veut changer */ PORTA &= ~8 /* Passer de 0 à 1 le 3ème bit du PORTA */ PORTA |= 8 Il existe une macro ''_BV'' (définie dans |common/io.h|) qui permet de travailler directement avec le rang du bit que l'on veut changer. L'exemple précédent devriendrait alors : PORTA &= ~_BV(3) /* 1 << 3 = 8 */ PORTA |= _BV(3) Notez que l'on peut aussi travailler avec plusieurs bits d'un coup. Par exemple si on veut passer les bits 2 et 3 de PORTA à 1, il suffit de faire : PORTA |= 12 /* 2³ + 2² = 12 */ /* Pareil en utilisant la macro _BV */ PORTA |= _BV(2) + _BV(3) Essayez d'utiliser cette macro car, de manière génénale, elle rend le code plus lisible. * Des périphériques tout fait dans l'AVR L'AVR contient des périphériques tout fait comme par exemple : * un convertisseur analogique-numérique : il possède un registre utilisé à cet effet ; * un port série : il possède des registres de configuration, de réception, d'envoi... ; * port I2C/TWI ; * watchdog timer pour faire redémarrer l'AVR quand il plante ; * PWM ; * ... * Le mode polling et les interruptions ** Le mode polling Voici comment fonctionne le mode polling avec un port série par exemple : Est ce que j'ai un bit d'arrivée sur le port série ? Si non, je continue la suite du programme ; Si oui, je le traite de suite ; On doit donc entre chaque instruction du programme, regarder l'entrée du port série pour vérifier la présence de données. Il y a donc une perte de temps assez inutile à faire ces vérifications. Pour résoudre ce problème, on utilise les interruptions. ** Les interruptions Il existe sur l'AVR des pattes de certains ports qui peuvent générer des interruptions : lorsque qu'elle changent d'état, il y a génération d'une interruption qui sera traité ou non, selon son niveau. Reprenons l'exemple précédent pour le port série avec des interruptions : J'exécute mon programme tranquillement, comme si de rien n'était ; Pouf une interruption est déclenchée car il y a une réception d'un bit sur le port série ; Mon programme est interrompu et envoyé dans une fonction de traitement de l'interruption : je récupère la donnée reçue sur le port série, je la traite... ; Une fois l'interruption finie, je retourne là où je m'étais arrêté dans mon programme. Les interruptions sont traitées de manières linéaires, c'est à dire que si deux interruptions ont été déclenchées en même temps ou avec un temps d'écart très faible, on ne pourra pas savoir laquelle est arrivé avant. Précautions à prendre pour les interruptions : [Les variables volatiles] Elles permettent de demander systématiquement au compilateur de ne pas optimiser ces variables pour que l'AVR aille bien chercher sa valeur dans la mémoire à chaque fois. Elles sont obligatoires pour les variables qui servent à la fois dans les interruptions et dans le reste du code pour que la bonne valeur soit bien vue dans les deux parties du code ; [Autoriser les interruptions] Il faut utiliser la fonction ''sei ()'' dans le code pour les activer ; [Interdire les interruptions] Il faut utiliser la fonction ''cli ()'' dans le code pour les désactiver. * Interrupteur en entrée Il est possible de mettre un interrupteur en entrée sur les AVR. Mais lorsque l'interrupteur est ouvert, il faut que la patte soit branchée. Elle ne peut rester dans le vide. Pour cela, il suffit d'activer le mode ''pull-up'' de la patte correspondante. Attention au rebond ! * Les AVR chez SI2E ** Les AVR qu'on utilise [ATMEGA8] 20 E/S, 8k de flash, environ 6 euros. [ATMEGA35] Comme l'ATMEGA8, mais avec plus d'E/S. [ATMEGA64] 64k flash, CMS. [ATMEGA128] 128k flash, CMS. ** Le code AVR Je ne peux que vous inviter à aller regarder de plus près le code dans le CVS et dans le SVN dans |n/avr|. Vous y trouverez les programmes des différentes cartes (asservissement, E/S, ...), de la documentation, un simulateur pour faire fonctionner sur PC et même des exemples. ** Encore plus de pratique Voyons de plus près la structure du dossier |n/avr| du SVN : |-- common | >>> Quelques macro globales, à inclure dans les programmes |-- doc | >>> De la documentation, dont celle-ci |-- make | >>> Les Makefiles utilisées pour la compilation `-- modules | >>> Pleins de petits modules qui sont déjà tout fait |-- adc | >>> Module pour la conversion analogique vers digital |-- host | >>> Simulateur AVR pour PC |-- math | >>> Pour faire des calcules en virgule fixe, génération de nombres aléatoires |-- proto | >>> Pour la gestion du protocole du port série |-- twi | >>> Pour la gestion du bus I2C |-- uart | >>> Pour la gestion du port série ? `-- utils >>> Des petites fonctions comme delay, reset... Sachez qu'il y a aussi un |Makefile| à faire et un fichier |avrconfig.h| qui permet par exemple de définir l'horloge de l'AVR que vous utilisez. * Logiciels Les logiciels pouvant compiler du code pour AVR sont : GCC-AVR, WIN AVR, AVR studio... * Autres documentations Pour plus d'informations, il vous reste l'internet et aussi le serveur local du robot pour accéder aux datasheets des différents AVR qui contiennent plein de choses ({+http://robot/local/datasheet/avr/+} qui ne fonctionne que depuis le local). * Remerciement Merci à Ni d'avoir pris le temps de nous faire cette formation alors qu'il était malade.