La programmation des ATMEL AVR
Transcription
La programmation des ATMEL AVR
La programmation des ATMEL AVR JUILLOT Guillaume 2 septembre 2003 Table des matières Introduction I Les I.1 I.2 I.3 I.4 I.5 I.6 I.7 I.8 I.9 3 microcontrôleurs AVR pour les nuls Qu’est-ce qu’un microcontrôleur ? . . . . Les entrées/sorties d’un microcontrôleur Timers/Counters et génération de PWM Convertisseurs analogique/numérique . . Communication série USART . . . . . . Comparateur analogique . . . . . . . . . Watchdog Timer . . . . . . . . . . . . . Modes d’économie d’énergie . . . . . . . Interruptions internes et externes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 5 5 6 7 7 8 8 8 9 II La programmation des microcontrôleurs en C II.1 Débuter avec Imagecraft . . . . . . . . . . . . . . . . . . . . . . II.2 La séparation de code en plusieurs fichiers et quelques fonctions II.2.1 La définition de fonctions utiles à l’aide de #define . . . II.2.2 Rajouter un fichier à un projet . . . . . . . . . . . . . . II.3 Les interruptions avec ImageCraft AVR . . . . . . . . . . . . . II.4 L’assembleur dans un programme en C . . . . . . . . . . . . . . II.5 Quelques compléments sur le compilateur ICC AVR . . . . . . . . . . utiles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 11 13 14 14 15 16 16 III La simulation des progammes III.1 Premiers pas avec AVR Studio 4 III.2 Workspace . . . . . . . . . . . . . III.3 Programme en cours . . . . . . . III.4 Contrôle du déroulement . . . . . III.5 Choix de la vue . . . . . . . . . . III.6 Output view . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19 19 20 21 21 22 22 IV L’implémentation réelle sur le microcontrôleur IV.1 L’alimentation . . . . . . . . . . . . . . . . . . . . . . . . . . . . IV.2 L’horloge . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . IV.3 Le port de programmation . . . . . . . . . . . . . . . . . . . . . . IV.4 Comment implémenter notre programme dans le microcontrôleur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23 23 23 24 26 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Le mot de la fin 29 A Schéma de l’ATmega128 31 B Conversion hexadécimal/binaire/décimal B.0.1 Convention d’écriture . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33 33 1 2 TABLE DES MATIÈRES C Aide à la programmation C.1 Type de données . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . C.2 Gestion des entrées/sorties . . . . . . . . . . . . . . . . . . . . . . . . . . . C.3 Interruptions externes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . C.4 Timer/Counter0 (8 bits) . . . . . . . . . . . . . . . . . . . . . . . . . . . . C.5 Timer/Counter2 (8 bits) . . . . . . . . . . . . . . . . . . . . . . . . . . . . C.6 Timer/Counter1 et 3 (16 bits) . . . . . . . . . . . . . . . . . . . . . . . . . C.7 Interruptions pour Timers/Counters . . . . . . . . . . . . . . . . . . . . . C.8 Génération de PWM sur OC0 (8 bits) . . . . . . . . . . . . . . . . . . . . C.9 Génération de PWM sur OC2 (8 bits) . . . . . . . . . . . . . . . . . . . . C.10 PWM sur OC1A, OC1B, OC1C, OC3A, OC3B et OC3C (de 8 à 10 bits) C.11 USART . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35 35 35 35 36 36 37 37 37 38 38 39 Adresses Internet utiles 41 Index 43 Introduction Ce document a été écrit dans le cadre de la coupe 2003 de robotique e=M6. Il a pour but de vous présenter toutes les phases de développement d’un projet sur les microcontrôleurs de la famille AVR de chez ATMEL. Cette présentation est décomposée en quatre parties. La première sera une description aussi complète que possible des possibilités qu’offrent les microcontrôleurs choisis. Suivront les rudiments de la programmation en C et en assembleur, à l’aide du compilateur Imagecraft. Un aspect important est la simulation du programme, qui se fera à l’aide de AVR Studio 4 dans la troisième partie du présent document. Je terminerai en présentant la mise en pratique du programme en l’implémentant sur le microcontrôleur. Les exemples donnés seront valables pour un ATmega128. Il est possible qu’ils ne soient plus valables pour un autre microcontrôleur. Dans tous les cas il est vivement recommandé d’avoir une datasheet complète de l’ATmega128 sous les yeux pour mieux pouvoir suivre les exemples. Utilisez également la datasheet d’un autre microcontrôleur si vous souhaitez adapté les exemples au microcontrôleur de votre choix. 3 4 INTRODUCTION Chapitre I Les microcontrôleurs AVR pour les nuls Les microcontrôleurs de la famille AVR d’ATMEL possèdent de nombreuses caractéristiques différentes, aussi bien en termes de vitesse, mémoire, nombre d’entrés/sorties mais aussi au niveau des fonctions particulières qui sont disponibles. Il conviendra donc de choisir le microcontrôleur en fonction de l’utilisation qui est à faire - par exemple, il est conseillé d’éviter d’utiliser un ATmega128 si l’on ne cherche qu’à générer un PWM1 . I.1 Qu’est-ce qu’un microcontrôleur ? Un microcontrôleur est composé d’un microprocesseur, de mémoire ROM pour stocker le programme et d’une mémoire RAM volatile pour conserver les résultats intermédiaires lors de l’exécution du programme. Mais les microcontrôleurs possèdent des avantages incomparables pour l’informatique embarquée par rapport aux microprocesseurs. Ce sont d’abord les broches qui peuvent servirent d’entrées/sorties mais surtout toutes les fonctions périphériques. En effet, selon le microcontrôleur considéré, on peut trouver : – plusieurs Timers/Counters – Génération de signaux PWM – Nombreux convertisseurs analogiques/numériques – Communication série USART – Comparateur analogique – Watchdog Timer – Oscillateur RC interne – Modes d’économies d’énergie – Interruptions internes et externes I.2 Les entrées/sorties d’un microcontrôleur La plupart des pattes d’un microcontrôleur peuvent servir soit d’entrées, soit de sorties, et ce choix peut changer à tout instant comme le veut le développeur. Les entrées/sorties sont regroupées par huit et le groupement de huit pattes ainsi formé est appelé port et se voit attribué une lettre. Par exemple, les broches 44 à 51 forment le port A. 1 PWM = Pulse Width Modulation, c’est un signal rectangulaire caractérisé par le rapport cyclique (tempshaut /tempsbas ), c’est-à-dire avec un temps à 1 différent du temps à 0. 5 6 CHAPITRE I. LES MICROCONTRÔLEURS AVR POUR LES NULS Chaque patte est ensuite numérotée selon sa position au sein de ce port. Par exemple, la broche 46 est appelée PA52 . Trois registres3 servent à gérer les entrées/sorties : – DDRx4 : indique quelles sont les pattes considérées comme des entrées et celles considérées comme des sorties. Si le nième bit de DDRx (noté DDxn) est à 1, alors la nième patte du port x (notée Pxn) est considéré comme une sortie. Si DDxn est à 0, alors Pxn est considérée comme une entrée. – PORTx : la valeur du nième bit de PORTx (noté PORTxn) définit la valeur de sortie de Pxn. Si PORTxn=1, alors Pxn=+Vcc, si PORTxn=0, alors Pxn=Gnd. – PINx : la valeur du nième bit de PINx (noté PINxn) est la valeur qui est appliquée à l’entrée du microcontrôleur. Par exemple, si on applique +Vcc à Pxn, alors PINxn=1. La plupart des pattes possèdent des fonctions périphériques qui inhibent l’utilisation en tant qu’entrées/sorties. Si l’on utilise les fonctions périphériques, il ne faut en aucun cas modifier les bits DDRxn, PORTxn et PINxn correspondants. I.3 Timers/Counters et génération de PWM Les Timers/Counters, notés TCNTn où n est le numéro Timer/Counter, sont des compteurs cadencés selon une certaine fréquence définie. Lorsqu’il atteint son maximum (MAX5 ), il passe à 0 et recommence à compter. Ce passage de MAX à 0 peut s’accompagner d’une interruption interne (cf. Interruptions internes et externes en page 9). À ces Timers/Counters se rajoutent un comparateur dont on peut définir le seuil à l’aide du registre OCRn. Lorsque le TCNTn atteint la valeur de OCRn, alors la patte OCn change de valeur, passant de 0 à 1 ou de 1 à 0 selon la configuration puis retournant à la valeur initiale lorsque TCNTn passe de MAX à 0. Ces Timers/Counters peuvent donc avoir plusieurs utilisations. D’abord, le programmeur peut savoir le temps écoulé depuis qu’il a lancé le Timer/Counter. Ou encore, l’interruption de passage de MAX à 0 peut être utilisée pour temporisation, et commencer ou arrêter un programme au bout d’un certain temps. La dernière utilisation, très utile en robotique pour la commande de servomoteurs, est la génération de signaux PWM. En effet, si la patte OCn change de valeur lorsque le TCNTn atteint la valeur du comparateur OCRn, on obtient sur OCn un signal alternativement à 1 puis à 0 avec des durée à 0 et à 1 réglables. On a bien OCn qui est un signal PWM dont on peut définir le rapport cyclique en mettant la bonne valeur dans OCRn. Plusieurs registres sont utilisés pour configurer les Timers/Counters : TCCRn, OCRn, ASSR, TIMSK et TIFR. Leurs configurations sont compliquées, il convient de se reporter aux chapitres correspondants de la datasheet. La valeur du Timer/Counter, accessible via le registre TCNTn, peut être modifiée à tout instant afin de réinitialiser ma séquence à partir d’une certaine valeur, après avoir atteint MAX, le compteur recommencera à 0. À noter que la fréquence de comptage peut être un sous-multiple de la fréquence du microcontrôleur ou encore être imposée par une horloge externe connectée à une patte du microcontrôleur. Certains compteurs fonctionnent sur 16 bits. Ils présentent plusieurs avantages : ils peuvent compter plus loin donc plus longtemps ; lors d’une utilisation pour générer du PWM, la résolution est plus élevée ; et surtout ils possèdent deux comparateurs dont les seuils peuvent être réglés différement, ce qui permet d’obtenir sur les pattes OCnA et OCnB deux signaux PWM de même fréquence mais de rapport cyclique différent à l’aide d’un seul Timer/Counter. Dans le 2 Les plus perspicaces auront remarqué que l’ordre des numéros sur le port A est inversé par rapport aux numéros des pattes. En réalité, cela dépend du port, certains sont dans le bon sens, d’autres non. Voir dans la datasheet. 3 Un registre est une variable interne de huit bits, utilisée pour définir le comportement du microcontrôleur ou pour savoir dans quel état il se trouve. 4 x représente la lettre du port, par exemple DDRA ou DDRB ... 5 pour un compteur sur m bits, alors MAX = 2m − 1 I.4. CONVERTISSEURS ANALOGIQUE/NUMÉRIQUE 7 cas de Timer/Counter sur 16 bits, le registre TCNTn se décompose en deux registres TCNTnL et TCNTnH tandis que OCRn se décompose en OCRnAL et OCRnAH pour le premier comparateur et OCRnBL, OCRnBH pour le deuxième comparateur. I.4 Convertisseurs analogique/numérique Le monde réel étant continu, c’est-à-dire analogique, tandis que les microcontrôleurs fonctionnent en numérique, il faut bien avoir une interface entre les deux : le convertisseur analogique/numérique6 . Son fonctionnement est simple à comprendre : à intervalles de temps réguliers, la tension analogique est mesurée puis arrondie afin de pouvoir être convertie en numérique. La conversion est donc d’autant plus fidèle que le nombre de bits du codage est élevé et est inversement proportionnelle à la plage de tension analogique en entrée. Les microcontrôleurs de la famille AVR possèdent des convertisseurs embarqués donc il n’est pas nécessaire d’en concevoir ou d’en acheter. Par exemple, l’ATmega128 possède un CAN fonctionnant sur 10 bits. Ce CAN peut convertir jusqu’à 8 signaux analogiques par intermittence. Le temps maximum de conversion est de 260µs mais peut être diminué en réduisant la résolution (nombre de bits du codage). L’échelle de tension en entrée est par défaut [0, V cc] mais peut être passée à [0, Vref ] où Vref ≤ V cc est une tension appliquée à la patte Aref (no 62). A noter que les CAN peuvent être configurés pour ne faire qu’une conversion ou pour convertir en continu la tension analogique, la conversion étant alors réguilèrement mise à jour selon la durée de conversion. I.5 Communication série USART USART est l’abrévation pour Universal Synchronous and Asynchronous serial Receiver and Transmitter, c’est un système de communication point-à-point très flexible. Les données sont transmises par trames de 7 à 13 bits. La trame a le format suivant : D D D D D D D D D D D (IDLE) D St 0 D 1 D 2 D 3 D 4 D [5] D [6] D [7] D [8] D [P] Sp1 [Sp2] D (St/IDLE) D D D D D D D D D D D St : bit de départ, toujours à 0 n : bits de données P : bit de parité Sp : bit(s) de fin, toujours à 1 IDLE : pas de transfert, la ligne doit être à 1 Fig. I.1 – Trame USART Les éléments entre crochets sont facultatifs, leur présence est définie par le développeur. Le bit de parité permet de vérifier rapidement s’il y a eu une erreur de transmission mais n’est pas fiable à 100%. L’USART se décompose en trois parties : L’émetteur : se charge d’émettre sur la patte TXDn, à la fréquence configurée, la trame selon la valeur stockée dans le registre UDR ; il s’occupe donc automatiquement de la génération des bits Start, Stop et éventuellement Parity. Des interruptions peuvent être déclenchées lorsque la transmission est terminée. 6 en abrégé, CAN ou ADC en anglais pour Analog to Digital Converter 8 CHAPITRE I. LES MICROCONTRÔLEURS AVR POUR LES NULS Le récepteur : dès détection sur la patte RXDn d’un bit Start valide (c’est-à-dire à 0), chaque bit de la trame est alors échantilloné selon la fréquence configurée ou selon l’horloge sur XCK, et stocké dans un registre temporaire jusqu’à réception de la trame complète. Ensuite, la valeur reçue est déplacée dans le registre UDR accessible par le programme. Une interruption peut être déclenchée lors de la fin de réception pour prévenir qu’une trame est arrivée. Si le programme n’a pas lu une trame lorsqu’une deuxième trame arrive, la première trame est perdue. Si la transmission s’est mal déroulée, par exemple si la trame est non conforme, si le bit de parité n’est pas correct ou si des données reçues n’ont pas été lues, divers drapeaux7 passent à 1 et les données erronées sont effacées. Le générateur d’horloge : lors d’une utilisation en mode synchrone, un générateur est déclaré maı̂tre et l’autre esclave. Le maı̂tre génère le signal d’horloge selon le débit souhaité et l’envoit sur la patte XCK du microcontrôleur. L’horloge esclave reçoit ce signal d’horloge sur sa patte XCK et l’utilise pour échantilloner les trames ou les émettre au bon rythme. En mode asynchrone, l’USART en reception se charge de synchroniser son horloge sur la trame arrivante. La configuration doit être faite avant toute tentative d’envoi de données et ne devra pas être changée tant que des données restent à émettre/recevoir. Il faut bien veiller à ce que la configuration soit toujours identique sur les deux microcontrôleurs pour le format de la trame, le débit et qu’en mode synchrone les deux pattes SCK soient reliées et qu’un USART soit en maı̂tre et l’autre en esclave. I.6 Comparateur analogique Outre les convertisseurs analogiques/numériques, les microcontrôleurs AVR possèdent un comparateur analogique. Celui-ci compare les deux tensions appliquées aux pattes AIN0 et AIN1. Si AIN 0 < AIN 1 alors le bit ACO (5ème bit du registre ACSR) passe à 1. Sinon, ACO reste à 0. De plus, une interruption peut être déclenchée lors du passage à 1 de ACO. I.7 Watchdog Timer Le Watchdog Timer se charge de faire des RESET à intervalle de temps régulier. Il utilise un oscillateur RC interne en tant qu’horloge cadencée à 1MHz. Ce système permet de recommencer le même programme à intervalles de temps réguliers ou encore de se réveiller d’un mode d’économie d’énergie après un certain lapse de temps (voir paragraphe suivant). A noter que la mémoire RAM n’est pas perdu au cours de ce RESET, ce qui permet d’augmenter le temps entre deux réexécution du programme en tenant à jour un compteur qui est décrémenté à chaque Watchdog Reset jusqu’à ce qu’il atteigne 0 et alors on commence réellement le programme. I.8 Modes d’économie d’énergie Certaines applications ne nécessitent pas que le microcontrôleur tourne continuellement. Il serait donc souhaitable de le désactiver, de ”l’endormir”, pour qu’il consomme aussi peu d’énergie que possible. Ceci est possible grâce aux Sleep Mode. Plusieurs modes sont disponibles, afin de pouvoir ”endormir” ce dont on n’a pas besoin et de conserver ce qui doit l’être. Une fois configuré, il suffit pour entrer dans le mode d’énregie d’exécuter l’instruction SLEEP. La sortie du mode se fait via une interruption externe ou interne, selon le mode utilisé. 7 Un drapeau est un bit indiquant qu’un évènement s’est produit. I.9. INTERRUPTIONS INTERNES ET EXTERNES I.9 9 Interruptions internes et externes Certains évènements peuvent intervenir à n’importe quel instant et doivent être gérés sans attendre. Il est évident que de faire constament une batterie de tests pour voir si tel ou tel évènement s’est produit n’est pas performant du tout. Les interruptions sont LA solution à ce problème. En effet, dès qu’un évènement se produit, si l’interruption est correctement configurée, le microcontrôleur arrête immédiatement son exécution pour exécuter une autre portion de code gérant cet évènement. Dès que cette portion de code a fini d’être exécutée, le microcontrôleur retourne à son programme principal, à l’endroit où il l’avait quitté. Les interruptions possibles sont de deux types : externe : lorsqu’une des pattes passe dans un certain état ou tant que la patte est dans un état défini interne : par exemple lorsque qu’un compteur dépasse sa valeur maximale8 , lorsqu’un transfert via USART est terminé... Plusieurs choses sont à faire pour configurer et utiliser les interruptions : 1. Pour les interruptions internes, configurer le registre correspondant pour déclencher l’interruption ; par exemple pour déclencher une interruption sur un Overflow du Timer/Counter0, on configure le registre TIMSK en mettant le bit no 0, TOIE0, à 1. 2. Pour les interruptions externes, ce sont les registres EICRA, EICRB, EIMSK et EIFR qu’il faut configurer pour qu’un certain état sur une ou plusieurs des pattes INT0 à INT7 déclenche une interruption. 3. Il faut ensuite dire au microcontrôleur ce qu’il doit exécuter. Pour cela, les premiers blocs de mémoire sont alloués à un Reset and Interrupt Vector. À chaque type d’interruption est associé une adresse dans ce Vector où le développeur met une instruction ”jmp LABEL”, LABEL étant l’adresse de la mémoire où se trouve le code à exécuter. 4. Ensuite, on programme le code qui doit être exécuté en le mettant à l’adresse LABEL définie ci-dessus. Nous verrons dans le chapitre sur la programmation en C en page 15 comment ces deux étapes peuvent être réalisé facilement. 5. Il reste à activer la gestion des interruptions à l’aide de la commande assembleur sei. La gestion des interruptions peut être desactivée à l’aide de la commande assembleur cli. A noter que le retour au programme principal se fait via la commande assembleur reti et non via ret comme pour un retour depuis une sous-fonction classique. 8 on appele cela Overflow 10 CHAPITRE I. LES MICROCONTRÔLEURS AVR POUR LES NULS Chapitre II La programmation des microcontrôleurs en C Normalement, à ce niveau, vous vous dı̂tes : ”Que ça a l’air bien les microcontrôleurs, ce doit être trop dur à programmer.” Et bien, vous vous trompez ! Evidement si l’on était obligé de programmer en assembleur, ce serait galère. Mais heureusement, les microcontrôleurs AVR ont l’avantage de disposer de moultes compilateurs, ce qui permet de programmer en C, ce qui est, vous l’avouerez, tout de même plus facile. Parmis ces compilateurs, il y a entre autres1 : GCC AVR - compilateur sous license GNU c’est-à-dire libre d’utilisation ; Imagecraft - alias ICC AVR ; et CodeVision AVR. Si GCC AVR est gratuit, il a le gros désavantage de fonctionner sans interface graphique mais à l’aide de MakeFile. Les aficionados de la programmation en C sous Linux ne seront guère gênés mais moi si. C’est pourquoi j’ai choisi d’utiliser une version de démonstration d’Imagecraft, les limitations de cette démo n’étant guère gênant pour de petits projets. Pour les adeptes de Linux, reportez-vous aux rapports de mes prédecesseurs à cette adresse : http://resel.enst-bretagne.fr/club/em6/site_2002/index.php3?corps=doc. II.1 Débuter avec Imagecraft Après un détour sur http://www.imagecraft.com pour télécharger la démo d’Imagecraft et après l’avoir installé, on le lance et les choses sérieuses vont commencer. Si vous êtes prêts, nous allons pouvoir commencer. Un projet est souvent composé de plusieurs fichiers qu’il faut regroupé. Pour cela, on crée un ... Project ! Dans le menu Project, choisissez New. Créez un répertoire où vous voulez que le projet soit stocké puis donnez-lui son nom2 . Pour l’exemple qui suit, vous prendrez comme répertoire debut et comme nom de projet debut. Ouvrez le projet ainsi créé via Project/Open. Ensuite faites File/New et vous pouvez commencer à écrire du code. Maintenant que tout est prêt pour commencer à programmer, je vous propose de faire un programme qui consiste à faire clignoter une LED3 . D’abord, choisissons où sera connecté cette 1 Voir sur http://www.avrfreaks.net pour plus de détails AVR ne gère pas les noms de fichiers longs. Pour créer un projet dans un répertoire donc le chemin d’accès contient des caractères illicites pour des noms de fichiers courts, il faut donner dans la case Nom du fichier l’arborescence complète en format nom de fichier court. Par exemple C:\mesdoc~1\codeav~1\debut.prj 3 LED est le sigle anglais pour Diode Electro-Luminescente, c’est une petite ampoule. 2 ICC 11 12 CHAPITRE II. LA PROGRAMMATION DES MICROCONTRÔLEURS EN C DEL. Si personne ne s’y oppose, je propose la patte PA04 . Ensuite, il faut que le microcontrôleur sache quelle portion de code exécuter au démarrage. Pour cela, on inclue ce code dans la fonction main. Voici donc le début de notre code : void main(void){ Explications : main est l’entrée du programme, c’est vers là que se dirigera le microcontrôleur dès sa mise sous tension. void est un mot anglais signifiant ”rien”. Placé avant le nom de la fonction - ici main -, void indique que le programme ne renvoie pas de résultat comme par exemple pourrait le faire une fonction somme. Ensuite, entre paranthèses se trouve les paramètres de la fonction, c’està-dire ce que la fonction a besoin de connaitre. Ici void soit ”rien” mais une fonction somme aura besoin des deux nombres à additionner donc la fonction deviendrait : int somme(int n, int m)5 . Le symbole { sert à indiquer le début de la fonction. A la fin de la fonction, il faut mettre }. Nous avons vu dans le paragraphe sur les entrées/sorties, en page 5, que les pattes pouvaient servir à la fois d’entrée ou de sortie. Ici il faut dire au microcontrôleur que la patte PA0 est une sortie. Il faut donc mettre le bit 0 de DDRA à 1. La première instruction de notre programme est donc : DDRA=0x01; En effet 0x indique que le nombre est en héxadécimal. Donc 0x01=00000001 en binaire6 . On voit bien7 que le bit 0 de DDRA est mis à 1. PA0 est désormais une sortie pour le microcontrôleur. Le point-virgule ” ;” sert à indiquer que l’instruction est finie. Certains doivent se dire que c’est génial, que le compilateur comprend tout de suite ce que DDRA signifie. Et bien non. En réalité le registre DRRA, comme tous les registres, est un endroit de la mémoire dont l’emplacement change selon le microcontrôleur sur lequel on travaille. Il faut donc préciser au compilateur quel microcontrôleur est utilisé afin qu’il puisse remplacer DDRA par son adresse mémoire. Heureusement pour nous, pas besoin de redéfinir à chaque fois les adresses de tous les registres utilisés, il suffit de mettre au tout début du programme, avant mêma la fonction main, la déclaration suivante : #include <iom128v.h> La commande #include permet d’insérer dans le programme sur lequel on travaille des portions de code définis dans un autre fichier, en l’occurence iom182v.h Maintenant il faut allumer la LED. Pour cela, il faut mettre à 1 le bit 0 de PORTA. Ce qui se fait via : PORTA=0x01; Etant donnée la vitesse d’éxecution8 , il faut patienter un peu avant d’éteindre la LED. On utilise pour cela une boucle qui ne fait rien : for(i=0;i<10000;i++); La boucle for compte depuis i=0 tant que i<10000 en faisant i++ , c’est-à-dire en augmentant i de 1 à chaque boucle. i ne tombe pas du ciel et doit être déclaré au début de la fonction afin que le compilateur sache ce qu’est i. On mettra donc la déclaration suivante avant DDRA=0x01 ; : int i ; 4 Souvenez-vous, PA0 est la patte no 0 du port A, numéroté 51 = entier en anglais 6 Voir en annexe, page 33 pour la conversion héxadédimal/binaire/décimal 7 Les bits sont numérotés de droite à gauche. En effet, le bit 0 est le bit le moins important et est donc mis le plus à droite, tout comme les unités sont placés à droite des dizaines, eux-mêmes à droite des centaines... 8 A 16 MHz, il faut 62.5ns pour faire l’instruction précédente. 5 int II.2. LA SÉPARATION DE CODE EN PLUSIEURS FICHIERS ET QUELQUES FONCTIONS UTILES 13 Ainsi le compilateur sait que i est un entier. On peut désormais éteindre la LED et attendre à nouveau. On rajoute donc à la suite du programme les instructions : PORTA=0x00 ; f o r ( i =0; i <10000; i ++); Il faut maintenant recommencer à allumer, attendre, éteindre, attendre... Pour cela, on englobe les 4 dernières instructions dans un bloc : while ( 1 ) { les 4 instructions } Ainsi les 4 dernières instructions seront répétées éternellement. Il reste à finir la fonction main par } et le programme est terminé. Voici le programme au complet : #include <iom128v . h> void main ( void ) { int i ; DDRA=0x01 ; while ( 1 ) { PORTA=0x01 ; fo r ( i =0; i <10000; i ++); PORTA=0x00 ; for ( i =0; i <10000; i ++); } } Sauvegardez le code (File/Save) dans le fichier code.c par exemple. Ensuite, indiquez quel microcontrôleur est utilisé via le menu Project/Option/Target/Device Configuration. Indiquez maintenant que le fichier code.c fait partie du projet en cliquant doit sur Files dans la partie droite de votre écran et en sélectionnant Add Files... Il ne reste plus qu’à compilé à l’aide de la touche F9 ou via le menu Project/Make Project. Si aucune erreur n’apparait dans le bas de votre écran, vous avez programmé votre premier projet sur microcontrôleur AVR. Félicitations ! Vous pouvez passer à la simulation, en page 19, ou alors en apprendre plus sur la programmation C en continuant ci-dessous. II.2 La séparation de code en plusieurs fichiers et quelques fonctions utiles Vous avez peut-être remarqué que dans le programme précédent les registres DDRA et PORTA sont entièrement modifiés. Il y a des cas où cela n’est pas souhaité et où on souhaite passer le bit 0 à 1 sans modifier les autres bits. Nous allons donc pour cela créer des fonctions supplémentaires. Mais ces fonctions ont de grandes chances d’être nécessaires dans un autre projet, et on a pas trop envie de les réécrire le moment venu. Nous allons donc les mettre dans un fichier séparé qui sera inclus dans les projets qui en ont besoin. 14 CHAPITRE II. LA PROGRAMMATION DES MICROCONTRÔLEURS EN C II.2.1 La définition de fonctions utiles à l’aide de #define Comment faire pour passer le nème bit du registre x à 1 sans modifier les autres bits ? Le registre x est de la forme ( y , y , y , nème bit , y , y , y , y ) où y=0 ou 1. Il suffit de savoir9 que (y OU 0)=y et que (y OU 1)=1. Il suffit donc de faire x OU ( 0 , 0 , 0 , 1 au nème bit , 0 , 0 , 0 , 0 ) pour obtenir le registre x identique sauf le nème bit qui est passé à 1. Ceci se fait en C par l’instruction : x|=(1<<n); En effet, l’instruction x|=y réalise l’instruction x=(x OU y), car | est la traduction de OU en C. On peut de même écrire x+=3 pour augmenter x de 3. Ensuite (x<<m) rajoute m zéros à la fin de x et enlève les m premiers bits au début de x. Ainsi par exemple par exemple (6<<3) devient 00110000 car 6 en binaire est 00000110. Présentons maintenant la déclaration #define. Par exemple, #define x 3 placée au début du programme, entre les #include et la première fonction, indique au compilateur qu’il doit remplacer tout les x qu’il trouve par un 3. Ce remplacement ne doit se faire que si x n’est pas définit, on met donc cette déclaration dans un bloc : #ifndef nom du bloc de définition #define nom du bloc de définition liste des définitions #endif Une macro10 ne se termine pas par un point-virgule car celui-ci sera mis par le développeur dans son programme lors de l’appel de cet macro. Il est possible de faire une macro sur plusieurs lignes selon le format suivant : #define nom de la macro \ instruction1 ; \ instrution2 ; \ ... dernière instruction Cette fois des ; sont mis car, je le rappele, le compilateur ne fait que remplacer le nom de la macro par sa définition. II.2.2 Rajouter un fichier à un projet Nous allons donc maintenant définir 4 fonctions très utiles pour tout projet. Pour cela, après avoir relancé et ouvert le projet debut si vous l’aviez quitté depuis, faites File/New. Entrez ensuite les déclarations suivantes : #i f n d e f #define #define #define #define #define FONCTIONS UTILES FONCTIONS UTILES SET BIT ( o c t e t , b i t ) ( o c t e t |= (1<< b i t ) ) CLEAR BIT( o c t e t , b i t ) ( o c t e t &= ˜(1<< b i t ) ) IS SET ( o c t e t , b i t ) ( ( o c t e t & (1<< b i t ) ) ! = 0 ) IS CLEAR ( o c t e t , b i t ) ( ( o c t e t & (1<< b i t ) ) = = 0 ) #endif 9 L’opération 10 Une OU se fait bit à bit avec les règles suivantes : (0 OU 0)=0 ; (0 OU 1)=1 ; (1 OU 0)=1 ; (1 OU 1)=1 macro est une fonction écrite à l’aide d’un #define II.3. LES INTERRUPTIONS AVEC IMAGECRAFT AVR 15 Enregistrez ce fichier sous le nom fonctions utiles.h dans le répertoire debut. Pourquoi .h alors qu’avant c’était debut.c ? Et bien parce que la règle veut que les fichiers secondaires soient dans un ”header”. Mais dans un header, on ne peut écrire que des #define et non du code. Dans ce cas, on sépare les #define qu’on met dans un .h et les portions de code qu’on met dans un .c qui porte le même nom que le .h. Ensuite clic droit sur Header dans la partie droite de votre écran puis Add Files... pour ajouter les fonctions utiles au projet. Puis il faut dire au programme principal qu’il a le droit d’utiliser ce fichier. Cela se fait en mettant : #include ”fonctions utiles.h” juste après les autres #include. Les plus observateurs auront remarqué que l’ont a mis des guillemets au lieu de <> comme à la page 12. Les <> signifient que le fichier se trouve dans le répertoire où est installé le compilateur alors que les guillemets indiquent que le fichier est dans le même répertoire que le projet. On peut maintenant utiliser les macros SET BIT et CLEAR BIT dans la fonction main. Par exemple : SET BIT(DDRA,0); pour mettre à 1 le bit 0 de DDRA. II.3 Les interruptions avec ImageCraft AVR Nous avons vu, en page 9 que la gestion des interruptions est lourde à faire en assembleur. Heureusement, le C et Imagecraft sont là pour nous aider. Si les étapes 1,2 et 5 restent inchangées, voilà comment faire pour s’occuper des étapes 3 et 4. Pour l’exemple, nous allons allumer une LED sur PA0 dès que la patte PD0 passe à 1, PD0 étant la patte correspondant à INT0, l’interruption externe no 0. Lors du déclenchement d’une interruption, le programme principal est interrompu et une certaine fonction s’exécute. Dans notre cas, cette fonction va allumer la LED, appelons-la allume si vous le voulez bien. Si tous les #include ont été correctement fait, la fonction s’écrit facilement par : void a l l u m e ( ) { SET BIT (PORTA, 0 ) ; } Il faut maintenant dire au compilateur que c’est cette fonction qui doit être appelées lors de l’interruption externe. Pour cela, Imagecraft possède la déclaration : #pragma interrupt handler nom de la fonction:iv nom de l’interruption. Dans notre exemple, on aura donc : #pragma i n t e r r u p t h a n d l e r a l l u m e : iv INT0 ; Ainsi, l’interruption INT0 exécutera la fonction allume quand elle se déclenche. 16 CHAPITRE II. LA PROGRAMMATION DES MICROCONTRÔLEURS EN C Reste à écrire le reste et on obtient in fine : #include <iom128v . h> #include ” f o n c t i o n s u t i l e s . h” #pragma i n t e r r u p t h a n d l e r a l l u m e : iv INT0 void a l l u m e ( ) { SET BIT (PORTA, 0 ) ; } void main ( void ) { SET BIT (DDRA, 0 ) ; // l a p a t t e 0 du p o r t A e s t une s o r t i e CLEAR BIT(DDRD, 0 ) ; // l a p a t t e 0 du p o r t D e s t une e n t r e e EICRA = 0 x03 ; EICRB = 0 x00 ; EIMSK = 0 x01 ; TIMSK = 0 x00 ; ETIMSK = 0 x00 ; asm ( ” s e i ” ) ; while ( 1 ) ; } Les valeurs attribuées à EICRA, EICRB, EIMSK, TIMSK et ETIMSK sont trouvées d’après la datasheet. asm(”sei”) est expliqué dans le paragraphe suivant. Le compilateur se charge de terminer la fonction traitant une interruption par l’instruction reti au lieu d’un ret habituel, le développeur n’a rien à se soucié. Dans l’exemple donné, le microcontrôleur exécute sans cesse l’instruction while(1), c’est-à-dire ne fait rien. Dès que la patte PD0 passe à 1, la fonction allume() est appelée. Lorsqu’elle se termine, le microcontrôleur retourne où il s’était arrêté, c’est-à-dire à l’instruction while(1). II.4 L’assembleur dans un programme en C Il est parfois nécessaire d’insérer des commandes assembleur directement dans le code C. Cela se fait via l’instruction asm("instruction en assembleur") ; qui insère l’instruction en assembleur dans le code compilé. Voici quelques exemples les plus fréquents : asm ( ”nop” ) ; // i n s t r u c t i o n ne f a i s a n t r i e n pendant un c y c l e d ’ h o r l o g e asm ( ” s e i ” ) ; // l e s i n t e r r u p t i o n s s o n t p r i s e s en compte asm ( ” c l i ” ) ; // l e s i n t e r r u p t i o n s ne s o n t p l u s p r i s e s en compte Un autre cas où le recours à l’assembleur est nécessaire est lorsque l’on cherche à mettre une temporisation d’une durée précise. Dans ce cas, on n’a d’autres choix que de regarder le code compilé en assembleur pour voir combien de temps nos instructions durent afin d’adapter la longueur de la boucle de temporisation. II.5 Quelques compléments sur le compilateur ICC AVR Ce compilateur fournit un outil très utile pour tous développeurs : l’Application Builder qui se trouve dans le menu Tools. Cet outil écrit automatiquement les lignes de configurations du microcontrôleur selon les besoins que vous aurez spécifiés. Ainsi, il devient facile de démarrer un projet nécessitant la gestion de plusieurs Timers/Counters, d’interruptions multiples ou de II.5. QUELQUES COMPLÉMENTS SUR LE COMPILATEUR ICC AVR 17 communication USART sans avoir à ouvrir la datasheet du microcontrôleur pour trouver quels registres doivent être initialisés et avec quelles valeurs. J’ai remarqué quelques problèmes avec le compilateur au sujet de la gestion des entiers longs. En effet, si les opérations sur les int et les float ne posent aucuns problèmes, il n’en est pas de même pour les long. Si vous souhaitez faire des calculs sur de grands entiers, préférez donc l’utilisation de float partout. Ce problème est facilement repérable grâce à la simulation des programmes, ce que je vais vous présenter dans le chapitre suivant. 18 CHAPITRE II. LA PROGRAMMATION DES MICROCONTRÔLEURS EN C Chapitre III La simulation des progammes Après avoir écrit son code, on aimerait bien voir comment les instructions s’enchaı̂nent pour voir si tout fonctionne comme prévu. Cela se fait grâce à AVR Studio 4, logiciel gratuit développé par ATMEL. Courrez le télćharger sur http://www.atmel.com/atmel/products/prod203.htm puis installez-le. La simulation est conforme au comportement réel du microcontrôleur dans la mesure où le simulateur est correctement programmé. A l’heure où j’écris ces lignes, des bugs subsistent. Par exemple, la gestion des Timer/Counter, et particulièrement de la génération de signaux PWM, ne marche absolument pas. Il ne faut donc pas s’y fier dans ces domaines. De même, l’implémentation des programmes dans le microcontrôleur via le câble AVR ISP (voir en page 23) présente parfois certaines difficultés incompréhensibles. Ce dernier point peut être résolu en utilisant conjointement les version 3 et 4 de AVR Studio, la version 3 ne présentant aucun problème avec AVR ISP. Peut-être ces problèmes seront-ils résolus dans les futurs versions de AVR Studio 4 lorsque vous souhaiterez l’utiliser, alors essayez donc si la version 4 vous convient. III.1 Premiers pas avec AVR Studio 4 Après l’avoir lancé, faites File/Open et ouvrez le fichier debut.cof. Vous avez ensuite le menu suivant qui s’affiche1 : où vous choisissez AVR Simulator et ATmega128. 1 Si ce n’est pas la première fois que vous ouvrez le fichier, AVR Studio 4 vous signale que ce fichier est déjà associé à un projet et vous demande si vous voulez ouvrir ce projet. Répondez oui à moins que vous ne vouliez retomber sur la fenêtre affichée ci-dessus. 19 20 CHAPITRE III. LA SIMULATION DES PROGAMMES Après avoir cliquez sur Finish, l’écran suivant devrait apparaitre : Voyons à quoi servent les différentes parties de cet écran. III.2 Workspace C’est là que vous pouvez voir l’état de votre microcontrôleur. Vous pouvez ainsi voir les valeurs de tous les registres du microcontrôleur mais aussi les modifier en temps réel. A côté de chaque registre se trouvent des cases symbolisant les bits du registre. La première case est le bit 7 du registre2 et ainsi de suite. Si la case est coloriée, le bit est à 1, il est à 0 sinon. Dans l’exemple donné, PORTA=0x21, soit le bit 0 et 5 à 1. 2 Rappelez-vous que les bits sont représentés dans l’ordre inverse : page 12 III.3. PROGRAMME EN COURS 21 Un autre outil important de cette fenêtre est la Stop Watch. C’est un chronomètre qu’on remet à zéro en cliquant droit dessus et en choisissant Reset StopWatch. Cela permet de savoir le temps écoulé entre deux instructions quelconques. Attention, le calcul est fait à une certaine fréquence d’horloge, visible juste au-dessus de la Stop Watch. La fréquence de l’horloge peut être modifiée via le menu Debug/AVR Simulation Options. III.3 Programme en cours C’est là qu’on peut suivre où en est le programme dans son déroulement. La prochaine instruction à être exécutée est indiquée par la flèche jaune. C’est ici qu’on peut placer des Breakpoints3 . Pour mettre un Breakpoint ou en enlever un, il suffit de faire un clic droit sur l’instruction où l’on souhaite installer/enlever un Breakpoint puis de choisir Toggle Breakpoint. Un autre outil très utile accessible depuis cette fenêtre est de pouvoir suivre en temps réel la valeur des variables du programme. Pour cela, on selectionne cette variable, on clique droit et on choisit Add To Watch. C’est ce qui a été fait dans l’exemple pour la variable i. III.4 Contrôle du déroulement Cette barre contrôle le déroulement du programme. Les principaux boutons sont : Start et Stop Debugging : Comme vous devez vous en doutez, ces deux boutons permettent de commencer et d’arrêter le déroulement du programme. 3 Points d’arrêt, voir Contrôle du déroulement en page 21 22 CHAPITRE III. LA SIMULATION DES PROGAMMES Run : Permet d’exécuter le programme. Dans ce mode, le Workspace et la fenêtre Watch sont inacessibles. Ce mode se termine soit en cliquant sur Break, soit lorsque le logiciel rencontre un Breakpoint. Ce mode est très utile pour sortir de boucles d’attente de longue durée en mettant un Breakpoint sur la première instruction après la boucle. Associé à la Stop Watch, on peut savoir la durée de la boule (cf. page 21). Break : Permet d’interrompre l’exécution du programme lors qu’on est en mode Run ou AutoStep. Reset : Comme son nom l’indique, recommence le programme depuis le début. Step Into : Mode pas-à-pas. Exécute la seule instruction indiquée par la flèche jaune. Si cette instruction fait appel à une sous-fonction, la fenêtre du programme en cours s’ouvre sur cette sous-fonction et le développeur peut exécuter les instructions de cette sous-fonction en mode pas-à-pas. Step Over : Mode pas-à-pas également, mais à la différence de Step Into s’il y a appel d’une sous-fonction, celle-ci est exécutée entièrement sans que le développeur ne puisse suivre son déroulement. Step Out : Termine la fonction en cours d’exécution puis retourne au programme qui l’a appelée en redonnant la main au développeur. Run To Cursor : Exécute le programme jusqu’au curseur4 . AutoStep : Equivalent à Run mais ici le développeur voit le Workspace et la fenêtre Watch. Le déroulement est plus lent qu’en mode Run mais on peut voir ce qui se passe dans le microcontrôleur ou les variables du programme. III.5 Choix de la vue Register et Memory Window : Affichent la vue sélectionnée dans la fenêtre Workspace. Watch : Affiche la fenêtre Watch qui permet d’afficher la valeur de variables choisies, voir page 21. Toggle Disassembler Window : Permet d’afficher le code assembleur dans la fenêtre du programme en cours et dès lors d’exécuter les instructions en assembleur au pas-à-pas. Très utile pour voir comment le programme a été compilé notamment pour connaitre les instructions utilisées dans une boucle afin de pouvoir adapté le nombre d’occurences pour obtenir une durée précise. III.6 Output view Cette fenêtre permet de voir si la simulation se déroule bien, par exemple s’il n’y a pas eu d’erreurs de chargement du projet. En résumé, c’est là que seront affichés les messages provenant du simulateur. 4 La barre verticale qui clignote à l’endroit où le texte tapé est inséré Chapitre IV L’implémentation réelle sur le microcontrôleur Si tout marche comme il faut sur le papier et sur la simulation, passons aux choses sérieuses et voyons ce que ça donne dans la réalité. Et bien c’est là que les choses se gâtent et je dois avouer que le chemin jusqu’au microcontrôleur fonctionnel est long et semé d’embûches. Mais avec ce document, tout devrait aller (enfin, j’espère). Construisons donc une carte pour programmer cet ATmega128. Cette programmation ce fera via un câble AVR ISP, disponible chez de nombreux fournisseurs de matériel électronique. IV.1 L’alimentation Le microcontrôleur est alimenté en 0V - 5V, aussi stable que possible. Evidement, on relie les masses ensemble sur le 0V d’une part, et les Vcc ensemble sur le 5V d’autre part. De plus, doivent être aussi reliées à Vcc deux pattes : AV cc - l’alimentation du convertisseur analogique-numérique - et P EN - inutilisé pour la programmation via AVR ISP. IV.2 L’horloge Plusieurs solutions s’offrent à vous pour cadencer le microcontrôleur. On verra plus loin comment indiquer au microcontrôleur la solution choisie, en page 27 L’ocillateur interne : Certains microcontrôleurs de la famille AVR possèdent un oscillateur interne, par exemple l’ATmega128 en possède quatre fonctionnant à 1, 2, 4 ou 8 MHz. Cette solution offre l’avantage d’être facile à mettre en oeuvre (rien à faire) mais a l’inconvénient d’avoir une fréquence instable selon la tension d’alimentation, la température, etc... et surtout de ne pas pouvoir fonctionner jusqu’au 16 MHz maximum de l’ATmega128. L’horloge externe : Il suffit de fournir un signal carré sur la patte XTAL1, mais encore faut-il pouvoir le générer ce signal. L’oscillateur RC externe : Cette solution présente l’avantage de pouvoir aller plus haut en fréquence (jusque 12 MHz sur l’ATmega128) et de choisir des fréquences autres que celles de l’oscillateur interne mais présente encore le gros inconvénient d’être instable en température et tension d’alimentation. Le quartz ou le résonnateur : C’est la solution pour être précis, stable et atteindre les 16 MHz mais peut présenter des difficultés de mise en oeuvre. Les quartz à deux pattes se branche sur XTAL1 et XTAL2 avec deux condensateurs entre chaque pattes et la masse, comme indiqué sur le schéma ci-dessous. 23 CHAPITRE IV. L’IMPLÉMENTATION RÉELLE SUR LE MICROCONTRÔLEUR 24 Fig. IV.1 – Utilisation d’un quartz Et c’est ces capacités qui posent problèmes. Selon leur valeur, le quartz peut fonctionner ou non. Et leur valeur, identique pour les deux condensateurs, dépend du quartz, du microcontrôleur et des pistes qui rejoignent le tout. Et pour vérifier si le quartz fonctionne, l’oscilloscope n’est d’aucune utilité puisqu’il rajoute des capacités parasites, il est donc nécessaire d’utiliser un analyseur logique. En pratique, 22pF semble être une valeur qui marche bien, mais si ce n’est le cas, il va falloir tester plusieurs valeurs jusqu’à ce que ça fonctionne. En tous cas, limitez au maximum la longueur des pistes entre les condensateurs, le quartz et le microcontrôleur. Certains quartz à 4 pattes ainsi que les résonnateurs possèdent déjà des capacités intégrées mais fonctionneront à des fréquences moins précises et seront plus instables. Pour plus de renseignements, se reporter à http://www.avrfreaks.com/Freaks/ Articles/ColinOFlynn/intro.php. IV.3 Le port de programmation Les choses se compliquent encore plus maintenant car il faut déchiffrer la datasheet de l’ATmega128 et de l’AVR ISP, et ce ne sont pas des modèles de clarté. Le câble AVR ISP se branche sur la carte de votre microcontrôleur via un connecteur 6 ou 10 broches. Vu que la version 10 Fig. IV.2 – Connecteur du câble AVR ISP, à brancher sur la carte de microcontrôleur, vue 2D broches n’apporte rien, on utilisera le connecteur 6 broches. Et vu que ce dessin n’est pas très clair, en voilà un plus beau : IV.3. LE PORT DE PROGRAMMATION 25 Fig. IV.3 – Connecteur du câble AVR ISP, à brancher sur la carte de microcontrôleur, vue 3D Voyons comment connecter ce câble au microcontrôleur. Vtg et Gnd : se connectent à l’alimentation, Vtg étant bien entendu Vcc Reset : Comme la barre l’indique, cette patte du microcontrôleur doit être reliée en temps normal à Vcc mais doit pouvoir être forçée à 0 par AVR ISP. On utilise donc le montage suivant : Fig. IV.4 – Connection de la patte RESET au connecteur AVR ISP SCK : Se connecte à la broche nommée SCK du microcontrôleur soit PB1. MOSI et MISO : C’est là que les choses sont bizarres. On se dit qu’il suffit de connecter le MOSI du microcontrôleur sur le MISO du câble et inversement. Et bien non ! Allez savoir pourquoi, ATMEL a décidé et écrit au fin fond de la datasheet que pour l’ATmega128, MISO du câble se connecte à PDO soit PE1 et que le MOSI du câble se connecte à PDI soit PE0. Pour les autres microcontrôleurs le problème ne se pose pas. CHAPITRE IV. L’IMPLÉMENTATION RÉELLE SUR LE MICROCONTRÔLEUR 26 Si vous comptez utiliser les broches SCK, PDI et PDO du microcontrôleur pour les brancher à quelquechose d’autre, une petite précaution s’impose. Soit déconnecter ce quelquechose au moment de la programmation, soit faire le montage suivant pour chaque broche : Fig. IV.5 – Connection des pattes PDI et PDO au connecteur AVR ISP IV.4 Comment implémenter notre programme dans le microcontrôleur Vous allez devoir quitter ce tutorial quelques jours le temps de réaliser la carte et de souder les composants. Voici un site très utile sur la soudure de composants CMS : http://home.nordnet.fr/~fthobois/les_cms.htm Pour vérifier la bonne soudure des pattes, utilisez un multimètre en testeur de continuité mais attention, il faut éviter d’appuyer sur les pattes du microcontrôleur au risque de prendre un faux contact pour une bonne soudure. Ca y est, la carte est prête, tout est soudé, alors allons-y. Nous allons implémenter le programme debut fait dans la partie sur la programmation en C, en page 11. Pour cela, il faut bien sûr brancher le câble AVR ISP sur l’ordinateur, l’autre côté sur la carte électronique, puis lancer AVR Studio 4 1 et charger le projet debut comme indiqué à la page 19. Enfin, mettez sous alimentation votre montage. ATTENTION : il faut impérativement brancher le câble AVR ISP avant d’alimenter le circuit, sous peine de voir la câble griller. Maintenant, sous AVR Studio 4, cliquez sur le bouton suivant : et un menu composé de 6 onglets apparait. Voici les onglets, que nous allons regarder maintenant : Program : Choisissez dans Device le microcontrôleur que vous utilisez. Indiquez ensuite que vous programmez le microcontrôleur via ISP. Les menus Flash et EEPROM permettent de 1 ou AVR Studio 3, d’après ma remarque au début du chapitre 3, page 19, l’interface étant sensiblement la même IV.4. COMMENT IMPLÉMENTER NOTRE PROGRAMME DANS LE MICROCONTRÔLEUR 27 programmer les mémoires correspondantes, soit le programme chargé dans le simulateur, soit un programme désigné en chargeant le fichier .hex créé par le compilateur. La mémoire Flash est sauf exception, celle qui contient le programme. Fuses : Ce menu permet de configurer le microcontrôleur. Décochez d’abord la compatibilité ATmega103. Puis tout ce que vous avez à changer désormais est le type d’horloge que vous souhaitez utiliser. A noter que si vous souhaitez utiliser un quartz à plus de 8 MHz, il faut décocher ”CKOPT fuse”. LockBits : Attention, les changements ici peuvent être irréversibles. C’est ici qu’on peut bloquer définitivement la modification de certains paramètres, voire bloquer toute programmation ultérieure. Bref, on ne touche à rien. Advanced : Ici vous pouvez éventuellement spécifier le port série utilisé par AVR ISP, mais en Auto, ça semble marcher très bien. Sinon, un bon moyen de voir si le microcontrôleur répond est de lire sa signature et ainsi de s’assurer que le microcontrôleur est celui qu’on pensait. Board : Je n’ai pas vraiment compris l’utilité de cet onglet alors passons. Auto : Permet d’automatiser certaines tâches afin, d’un seul clic, de pouvoir exécuter plusieurs opérations à la suite. On peut ainsi automatiser des tâches répétitives. 28 CHAPITRE IV. L’IMPLÉMENTATION RÉELLE SUR LE MICROCONTRÔLEUR Le mot de la fin Voilà, vous savez tout ce que vous avez besoin de savoir pour faire un projet sur les microcontrôleurs AVR d’ATMEL. Je tiens à remercier tout ceux qui m’ont aidé et soutenu lors de la découverte de ces microcontrôleurs. Parmi eux, les membres de mon équipe : François BARRE, Vincent BORREL, Guillaume DUPUY, Thomas LEFÈVRE, Arnaud LESUEUR, Jean-Baptiste RICHARD et Benjamin SARDA ainsi que les anciens de la robotique à l’ENST Bretagne : Pascal LEGRAND, Samuel MESCOFF, Sébastien ROY, Luc SORGUE et Nicolas TORNERI. Si vous avez des commentaires, suggestions ou si vous avez un texte à rajouter à ce document, je me ferai un plaisir d’en tenir compte. Envoyez-moi un simple mail à [email protected] 29 30 LE MOT DE LA FIN Annexe A Schéma de l’ATmega128 31 32 ANNEXE A. SCHÉMA DE L’ATMEGA128 Annexe B Conversion hexadécimal/binaire/décimal B.0.1 Convention d’écriture En décimal, notre base habituel, le nombre est écrit comme d’habitude : 21 En héxadécimal, on le fait précéder d’un ”0x” : 0x15 En binaire, on le termine par un ”b” : 00010101b Conversion binaire/hexadécimal Pour convertir un octet binaire en une valeur hexadécimal, procédez ainsi : 1. Découper votre octet en deux parties de 4 bits 2. Convertir les deux morceaux obtenus selon le tableau suivant : binaire hexadecimal 0000 0 0001 1 0010 2 0011 3 0100 4 0101 5 0110 6 0111 7 binaire hexadecimal 1000 8 1001 9 1010 A 1011 B 1100 C 1101 D 1110 E 1111 F 3. Et pour finir on colle ensemble les deux symboles hexadécimal pour trouver la conversion. Par exemple : 01111101b étape1 y 0111 et 1101 étape2 y 7 et D étape3 y d’où 01111101b = 0x7D Suivez les étapes en sens inverse pour convertir un nombre hexadécimal en binaire. 33 ANNEXE B. CONVERSION HEXADÉCIMAL/BINAIRE/DÉCIMAL 34 Conversion hexadécimal/décimal 0x0. 0x1. 0x2. 0x3. 0x4. 0x5. 0x6. 0x7. 0x8. 0x9. 0xA. 0xB. 0xC. 0xD. 0xE. 0xF. 0x.0 0 16 32 48 64 80 96 112 128 144 160 176 192 208 224 240 0x.1 1 17 33 49 65 81 97 113 129 145 161 177 193 209 225 241 0x.2 2 18 34 50 66 82 98 114 130 146 162 178 194 210 226 242 0x.3 3 19 35 51 67 83 99 115 131 147 163 179 195 211 227 243 0x.4 4 20 36 52 68 84 100 116 132 148 164 180 196 212 228 244 0x.5 5 21 37 53 69 85 101 117 133 149 165 181 197 213 229 245 0x.6 6 22 38 54 70 86 102 118 134 150 166 182 198 214 230 246 0x.7 7 23 39 55 71 87 103 119 135 151 167 183 199 215 231 247 0x.8 8 24 40 56 72 88 104 120 136 152 168 184 200 216 232 248 0x.9 9 25 41 57 73 89 105 121 137 153 169 185 201 217 233 249 0x.A 10 26 42 58 74 90 106 122 138 154 170 186 202 218 234 250 0x.B 11 27 43 59 75 91 107 123 139 155 171 187 203 219 235 251 0x.C 12 28 44 60 76 92 108 124 140 156 172 188 204 220 236 252 0x.D 13 29 45 61 77 93 109 125 141 157 173 189 205 221 237 253 0x.E 14 30 46 62 78 94 110 126 142 158 174 190 206 222 238 254 0x.F 15 31 47 63 79 95 111 127 143 159 175 191 207 223 239 255 Annexe C Aide à la programmation C.1 Type de données Type Taille (octects) Domaine unsigned char 1 0...255 signed char 1 -128...127 char (*) 1 0...255 unsigned short 2 0...65535 (signed) short 2 -32768...32767 unsigned int 2 0...65535 (signed) int 2 -32768...32767 pointer 2 unsigned long 4 0...4294967295 (signed) long 4 -2147483648...2147483647 float 4 +/-1.175e-38...3.40e+38 double 4 +/-1.175e-38...3.40e+38 (*) ”char” est équivalent à ”unsigned char” C.2 Gestion des entrées/sorties DDRx : Data Direction Register, bit à 0 pour que la patte correspondante soit une entrée, à 1 pour une sortie PORTx : pour changer la valeur des pattes en sortie PINx : pour obtenir la valeur des pattes en entrée x est une lettre entre A et G indiquant le port considéré. C.3 Interruptions externes EICRA, External Interrupt Control Register A bit EIRCA 7 6 ISC3 5 4 ISC2 3 2 ISC1 ISCn 00 01 10 11 1 0 ISC0 35 Interruption de INTn sur niveau bas non disponible front descendant front montant 36 ANNEXE C. AIDE À LA PROGRAMMATION EICRB, External Interrupt Control Register B bit EIRCB 7 6 ISC7 5 4 ISC6 3 2 ISC5 1 0 ISC4 ISCn 00 01 10 11 Interruption de INTn sur niveau bas front montant et front descendant front descendant front montant EIMSK, External Interrupt Mask bit 7 6 5 4 3 2 1 0 EIMSK INT7 INT6 INT5 INT4 INT3 INT2 INT1 INT0 INTn à 1 pour que l’interruption externe correspondante soit prise en compte. C.4 Timer/Counter0 (8 bits) TCNT0, Timer Counter Register : indique la valeur courante du Timer/Counter0 OCR0, Output Compare Register : peut être utilisé pour générer une interruption en cours de comptage (voir C.7) TCCR0, Timer Counter Control Register TCCR0 Fréquence de comptage Durée totale (*) 0x00 Timer/Counter0 arrêté 0x01 clkI/0 16µs 0x02 clkI/0 / 8 128µs 0x03 clkI/0 / 32 512µs 0x04 clkI/0 / 64 1, 024ms 0x05 clkI/0 / 128 2, 048ms 0x06 clkI/0 / 256 4, 096ms 0x07 clkI/0 / 1024 16, 384ms (*) Durée entre deux retours à 0 pour un microcontrôleur cadencé à 16MHz C.5 Timer/Counter2 (8 bits) TCNT2, Timer Counter Register : indique la valeur courante du Timer/Counter2 OCR2, Output Compare Register : peut être utilisé pour générer une interruption en cours de comptage (voir C.7) TCCR2, Timer Counter Control Register TCCR2 Fréquence de comptage Durée totale (*) 0x00 Timer/Counter2 arrêté 0x01 clkI/0 16µs 0x02 clkI/0 / 8 128µs 0x03 clkI/0 / 64 1, 024ms 0x04 clkI/0 / 256 4, 096ms 0x05 clkI/0 / 1024 16, 384ms 0x06 Horloge externe sur T2 (front descendant) 0x07 Horloge externe sur T2 (front montant) (*) Durée entre deux retours à 0 pour un microcontrôleur cadencé à 16MHz C.6. TIMER/COUNTER1 ET 3 (16 BITS) C.6 37 Timer/Counter1 et 3 (16 bits) TCNTnH et TCNTnL, Timer Counter Register High et Low : indique la valeur courante du Timer/Counter OCRnAH, OCRnAL utilisés pour générer des interruptions en cours de comptage OCRnBH, OCRnBL (voir C.7) OCRnCH, OCRnCL TCCRnA, Timer Counter Control Register A = 0x00 TCCRnB, Timer Counter Control Register B TCCRnB Fréquence de comptage Durée totale (*) 0x00 Timer/Counter2 arrêté 0x01 clkI/0 4, 096ms 0x02 clkI/0 / 8 32, 768ms 0x03 clkI/0 / 64 262, 144ms 0x04 clkI/0 / 256 1, 048576s 0x05 clkI/0 / 1024 4, 194304s 0x06 Horloge externe sur Tn (front descendant) 0x07 Horloge externe sur Tn (front montant) (*) Durée entre deux retours à 0 pour un microcontrôleur cadencé à 16MHz C.7 Interruptions pour Timers/Counters TIMSK, Timer Counter Interrupt Mask bit 7 6 5 4 TIMSK Comp2 Over2 0 Comp1A 3 Comp1B 2 Over1 1 Comp0 0 Over0 ETIMSK, Extended Timer Counter Interrupt Mask bit 7 6 5 4 3 ETIMSK 0 0 0 Comp3A Comp3B 2 Over3 1 Comp3C 0 Comp1C Overn à 1 active l’interruption d’Overflow du Timer/Counter n, c’est-à-dire qu’une interruption est déclenché quand le Timer/Counter revient à la valeur 0 Compnx à 1 active l’interruption de comparaison lorsque le Timer/Counter n atteint la valeur du registre OCRnx C.8 Génération de PWM sur OC0 (8 bits) OCR0 haut OCR0, Output Compare Register : permet de définir le rapport cyclique η = temps tempsbas = 256 TCCR0, Timer Counter Control Register TCCR0 Fréquence du signal PWM Période (*) Commentaire 0x68 Timer/Counter0 arrêté A éviter 0x69 clkI/0 / 256 16µs 0x6A clkI/0 / 2048 128µs 0x6B clkI/0 / 8192 512µs 0x6C clkI/0 / 16384 1, 024ms 0x6D clkI/0 / 32768 2, 048ms 0x6E clkI/0 / 65536 4, 096ms 0x6F clkI/0 / 262144 16, 384ms Recommandé pour les servomoteurs (*) Période du signal PWM pour un microcontrôleur cadencé à 16MHz 38 ANNEXE C. AIDE À LA PROGRAMMATION C.9 Génération de PWM sur OC2 (8 bits) Attention, OC2 est commun avec une autre sortie PWM, OC1C haut OCR2, Output Compare Register : permet de définir le rapport cyclique η = temps tempsbas = TCCR2, Timer Counter Control Register TCCR2 Fréquence du signal PWM Période (*) Commentaire 0x68 Timer/Counter0 arrêté A éviter 0x69 clkI/0 / 256 16µs 0x6A clkI/0 / 2048 128µs 0x6B clkI/0 / 16384 1, 024ms 0x6C clkI/0 / 65536 4, 096ms 0x6D clkI/0 / 262144 16, 384ms Servomoteurs 0x6E Horloge externe sur T2 (front descendant) 0x6F Horloge externe sur T2 (front montant) (*) Période du signal PWM pour un microcontrôleur cadencé à 16MHz C.10 OCR2 256 PWM sur OC1A, OC1B, OC1C, OC3A, OC3B et OC3C (de 8 à 10 bits) Dans ce qui suit, n vaut 1 ou 3 et x vaut A, B ou C OCRnxH OCRnxL ⇒ forment par concaténation OCRnx=65536*OCRnxH+OCRnxL rapport cyclique ηnx = OCRnx où resolution ∈ {8, 9, 10} selon la configuration choisie 2resolution TCCRnA, Timer Counter Control Register A bit 7 6 5 4 3 2 1 0 TCCRnA COMnA COMnB COMnC WGMn COMnx 00 10 WGMn 01 10 11 Description OCnx non utilisé OCnx utilisé Résolution 8 bits 9 bits 10 bits TCCRnB, Timer Counter Control Register B TCCRnB 8 bits Fréquence PWM 9 bits 10 bits Fréquence à 16MHz Fréquence à 16MHz Fréquence à 16MHz 0x09 clkI/O resolution 2 clkI/O 256 16µs clkI/O 512 32µs clkI/O 1024 64µs 0x0A clkI/O 8 ∗ 2resolution clkI/O 2048 128µs clkI/O 4096 256µs clkI/O 8192 512µs 0x0B clkI/O 64 ∗ 2resolution clkI/O 16384 1, 024ms clkI/O 32768 2, 048ms clkI/O 65536 4, 096ms C.11. USART TCCRnB 39 8 bits Fréquence PWM 9 bits 10 bits Fréquence à 16MHz Fréquence à 16MHz Fréquence à 16MHz 0x0C clkI/O 256 ∗ 2resolution clkI/O 65536 4, 096ms clkI/O 131072 8, 192ms clkI/O 262144 16,384ms 0x0D clkI/O 1024 ∗ 2resolution clkI/O 262144 16,384ms clkI/O 524288 32, 768ms clkI/O 1048576 65, 536ms C.11 USART Dans ce qui suit, n vaut 0 ou 1 selon l’USART considéré. Format de la trame : voir page 7 UDRn, USARTn Data Register : destination des données reçues ou origine des données à envoyer UCSRnA, USARTn Control and Status Register A : non utilisé pour la configuration UCSRnB, USARTn Control and Status Register B bit 7 6 5 4 3 2 1 0 UCSRnB RXCIEn TXCIEn UDRIEn RXENn TXENn UCSZn2 RXB8n TXB8n ➫ RXCIEn : à 1 pour permettre le déclenchement d’une interruption lors d’une réception terminée ➫ TCCIEn : à 1 pour permettre le déclenchement d’une interruption lors d’une émission terminée ➫ UDRIEn : à 1 pour permettre le déclenchement d’une interruption lorsque l’émetteur est prêt à émettre ➫ RXENn : à 1 pour autoriser la réception ➫ TXENn : à 1 pour autoriser l’émission ➫ UCSZn2 : à 1 pour indiquer une trame de 9 bits, à 0 si 8 bits ou moins ➫ RXB8n : 9eme à la réception si utilisation d’une trame de 9 bits. A lire avant UDRn ➫ TXB8n : 9eme à l’émission si utilisation d’une trame de 9 bits. A écrire avant UDRn UCSRnC, USARTn Control and Status Register C bit 7 6 5 4 3 2 1 UCSRnC 0 UMSELn UPMn USBSn UCSZn 0 UCPOLn ➬ UMSELn : à 0 pour communication asynchrone, à 1 pour synchrone UPMn Bit de parité 00 désactivé ➬ UPMn : 10 pair 11 impair ➬ USBSn : à 0 pour une trame à 1 bit Stop, à 1 pour 2 bits Stop UCSZn Taille des données 00 5 ➬ UCSZn : 01 6 10 7 11 8 ou 9 selon UCSZn2 du registre USCRnB ➬ UCPOLn : inutile en mode asynchrone, peu utile en mode synchrone, à mettre à 0 40 ANNEXE C. AIDE À LA PROGRAMMATION UBRRnH UBRRnL ⇒ forment par concaténation UBRRn=65536*UBRRnH+UBRRnL UBRRn, USARTn Baud Rate Register : valeur maximale = 0x0FFF Calcul du débit Calcul de Mode en bits/s (bps) UBRRn Asynchrone U BRRn = f 16(U BRRn + 1) BAU D = f −1 16 ∗ BAU D Synchrone U BRRn = f 2(U BRRn + 1) BAU D = f −1 2 ∗ BAU D Exemples de configuration Communication asynchrone, trame de 8 bits avec un bit de parité pair, 9600 bps pour un microcontrôleur cadencé à 16MHz : UCSRnC = 0x26 ; UBRRnL = 0x67 ; UBRRnH = 0x00 ; – Emetteur, sans interruption : UCSRnB = 0x08 ; – Emetteur, avec interruption : UCSRnB = 0x48 ; – Récepteur, sans interruption : UCSRnB = 0x10 ; – Récepteur, avec interruption : UCSRnB = 0x90 ; Code C pour une trame de 8 bits ou moins, sans gestion d’interruptions – Emission : while ( ! ( UCSRnA & (1<<UDREn ) ) ) ; /∗ a t t e n t e de l i b e r a t i o n de l ’ e m e t t e u r ∗/ UDRn = d o n n e e a e n v o y e e r ; – Reception : while ( ! ( UCSRnA & (1<<RXCn ) ) ) ; /∗ a t t e n t e de r e c e p t i o n de donnees ∗/ d o n n e e r e c u e = UDRn; Adresses Internet utiles resel.enst-bretagne.fr/club/em6 Pour suivre l’évolution du robot de l’ENST Bretagne et obtenir une verion plus récente de ce tutorial. resel.enst-bretagne.fr/club/em6/site 2002/index.php3?corps=doc Pour les linuxiens, un tutorial sur la programmation des AVR avec AVR GCC. www.atmel.com Le fabricant des microcontrôleurs AVR. www.avrfreaks.com Simplement le meilleur site dédié aux microcontrôleurs AVR, si vous avez des questions, allez-y sans tarder. www.atmel.com/dyn/resources/prod documents/doc2467.pdf C’est ici que se trouve la datasheet de l’ATmega128. www.atmel.com/dyn/products/tools card.asp?tool id=2726 La datasheet du câble de programmation AVR ISP. www.imagecraft.com Pour obtenir une version de démonstration du compilateur utilisé dans ce tutorial. www.atmel.com/dyn/products/tools card.asp?tool id=2725 AVR Studio 4. Le programmateur/simulateur home.nordnet.fr/∼fthobois/les cms.htm Un site pour vous aider à souder des composants CMS. 41 42 ADRESSES INTERNET UTILES Index C (programmation en) .c et .h, 15 #define, 14 #include, 12 #pragma, 15 asm("..."), 16 et assembleur, 16 for, 12 guillemets, 12 i++, 12 int, 13 interruption, 15 macro, 14 main, 12 paramètres d’une fonction, 12 point-virgule, 12 void, 12 while, 13 Comparateur analogique, 8 Compilateurs, 11 Convertion analogique/numérique, 7 Reexécution de programme, 8 Registre, 6 DDRx, 6 OCRn, 6 PINx, 6 PORTx, 6 TCNTn, 6 Reset pour la programmation, 25 Watchdog Timer, 8 Simulation, 19 assembleur, 22 breakpoint, 21 configuration, 19 déroulement du programme, 21 StopWatch, 21 Workspace, 20 Sleep Mode, 8 sortie, 8 Timer/Counter, 6 16 bits, 6 Economie d’énergie, voir Sleep Mode Entrée/sortie, 5 USART, 7 Interruption en assembleur, 9 en C, 15 généralités, 9 Timer/Counter, 6 Watchdog Timer, 8 Mise en pratique, 23 alimentation, 23 configurer le microcontrôleur, 27 horloge, 23 implémentation du programme dans le microcontrôleur, 27 port de programmation, 24 quartz, 23 SCK, MISO, MOSI, PDI, PDO, 25 soudure des composants, 26 Mode d’économie d’énergie, voir Sleep Mode Port, 5 PWM, 6 43