Table des matières:

Tutoriel Assembleur AVR 2: 4 étapes
Tutoriel Assembleur AVR 2: 4 étapes

Vidéo: Tutoriel Assembleur AVR 2: 4 étapes

Vidéo: Tutoriel Assembleur AVR 2: 4 étapes
Vidéo: Лекция 4. Архитектура AVR. Ассемблер 2024, Juillet
Anonim
Tutoriel Assembleur AVR 2
Tutoriel Assembleur AVR 2

Ce tutoriel est une continuation du "Tutoriel d'assemblage AVR 1"

Si vous n'avez pas suivi le didacticiel 1, vous devriez vous arrêter maintenant et commencer par celui-ci.

Dans ce tutoriel, nous continuerons notre étude de la programmation en langage assembleur de l'atmega328p utilisé dans les Arduino.

Tu auras besoin de:

  1. une planche à pain Arduino ou juste un Arduino normal comme dans le didacticiel 1
  2. une LED
  3. une résistance de 220 ohms
  4. un bouton poussoir
  5. fils de connexion pour faire le circuit sur votre planche à pain
  6. Manuel d'instructions: www.atmel.com/images/atmel-0856-avr-instruction-s…
  7. Fiche technique: www.atmel.com/images/Atmel-8271-8-bit-AVR-Microco…

La collection complète de mes tutoriels se trouve ici:

Étape 1: Construire le circuit

Construire le circuit
Construire le circuit

Vous devez d'abord construire le circuit que nous étudierons dans ce tutoriel.

Voici la façon dont il est connecté:

PB0 (broche numérique 8) - LED - R (220 ohm) - 5V

PD0 (broche numérique 0) - bouton-poussoir - GND

Vous pouvez vérifier que votre LED est bien orientée en la connectant à GND au lieu de PB0. Si rien ne se passe, inversez l'orientation et le voyant devrait s'allumer. Ensuite, reconnectez-le à PB0 et continuez. La photo montre comment mon arduino breadboard est connecté.

Étape 2: Rédaction du code d'assemblage

Rédaction du code d'assemblage
Rédaction du code d'assemblage

Écrivez le code suivant dans un fichier texte appelé pushbutton.asm et compilez-le avec avra comme vous l'avez fait dans le didacticiel 1.

Notez que dans ce code, nous avons beaucoup de commentaires. Chaque fois que l'assembleur voit un point-virgule, il saute le reste de la ligne et passe à la ligne suivante. C'est une bonne pratique de programmation (surtout en langage assembleur !) de commenter fortement votre code afin que lorsque vous y reviendrez à l'avenir, vous sachiez ce que vous faisiez. Je vais beaucoup commenter les choses dans les premiers tutoriels afin que nous sachions exactement ce qui se passe et pourquoi. Plus tard, une fois que nous serons un peu meilleurs dans le codage d'assemblage, je commenterai les choses un peu moins en détail.

;************************************

; écrit par: 1o_o7; date: 23 octobre 2014;************************************

.nolist

.include "m328Pdef.inc".list.def temp = r16; désigner le registre de travail r16 comme temp rjmp Init; première ligne exécutée

Initialiser:

ser temp; définissez tous les bits de temp sur 1. sortie DDRB, temp; mettre un bit à 1 sur l'E/S de direction des données; s'inscrire pour PortB, qui est DDRB, définit cela; broche en sortie, un 0 définirait cette broche comme entrée; donc ici, toutes les broches PortB sont des sorties (définies sur 1) ldi temp, 0b11111110; charge le numéro 'immédiat' dans le registre temporaire; si c'était juste ld alors le deuxième argument; devrait être un emplacement mémoire au lieu de DDRD, temp; mv temp à DDRD, le résultat est que PD0 est entré; et le reste sont des sorties clr temp; tous les bits de temp sont mis à 0 sur PortB, temp; définissez tous les bits (c'est-à-dire les broches) dans PortB sur 0V ldi temp, 0b00000001; charge le numéro immédiat sur temp out PortD, temp; déplacer la température vers PortD. PD0 a une résistance de rappel; (c'est-à-dire réglé sur 5V) car il a un 1 dans ce bit; le reste est à 0V depuis les 0.

Principale:

en temp, PinD; PinD contient l'état de PortD, copiez-le dans temp; si le bouton est connecté à PD0 ce sera; 0 lorsque le bouton est enfoncé, 1 sinon depuis; PD0 a une résistance de rappel, il est normalement à 5V sur PortB, temp; envoie les 0 et les 1 lus ci-dessus à PortB; cela signifie que nous voulons que la LED soit connectée à PB0,; lorsque PD0 est LOW, il règle PB0 sur LOW et tourne; sur la LED (puisque l'autre côté de la LED est; connecté à 5V et cela mettra PB0 à 0V donc; le courant circulera) rjmp Main; revient au début de Main

Notez que cette fois, nous avons non seulement beaucoup plus de commentaires dans notre code, mais nous avons également une section d'en-tête qui donne des informations sur qui l'a écrit et quand il a été écrit. Le reste du code est également séparé en sections.

Après avoir compilé le code ci-dessus, vous devez le charger sur le microcontrôleur et voir qu'il fonctionne. La LED doit s'allumer pendant que vous appuyez sur le bouton, puis s'éteindre à nouveau lorsque vous lâchez prise. J'ai montré à quoi cela ressemble dans l'image.

Étape 3: Analyse ligne par ligne du code

Je vais sauter les lignes qui ne sont que des commentaires car leur objectif va de soi.

.nolist

.include "m328Pdef.inc".list

Ces trois lignes incluent le fichier contenant les définitions de registre et de bit pour l'ATmega328P que nous programmons. La commande.nolist indique à l'assembleur de ne pas inclure ce fichier dans le fichier pushbutton.lst qu'il produit lorsque vous l'assemblez. Il désactive l'option de liste. Après avoir inclus le fichier, nous réactivons l'option de liste avec la commande.list. La raison pour laquelle nous faisons cela est que le fichier m328Pdef.inc est assez long et nous n'avons pas vraiment besoin de le voir dans le fichier de liste. Notre assembleur, avra, ne génère pas automatiquement un fichier de liste et si nous en voulons un, nous l'assemblerons à l'aide de la commande suivante:

avra -l pushbutton.lst pushbutton.asm

Si vous faites cela, il générera un fichier appelé pushbutton.lst et si vous examinez ce fichier, vous constaterez qu'il affiche le code de votre programme ainsi que des informations supplémentaires. Si vous regardez les informations supplémentaires, vous verrez que les lignes commencent par un C: suivi de l'adresse relative en hexadécimal de l'endroit où le code est placé en mémoire. Essentiellement, il commence à 000000 avec la première commande et augmente à partir de là avec chaque commande suivante. La deuxième colonne après la place relative en mémoire est le code hexadécimal de la commande suivi du code hexadécimal de l'argument de la commande. Nous discuterons plus en détail des fichiers de liste dans les futurs didacticiels.

.def temp = r16; désigner le registre de travail r16 comme temp

Dans cette ligne, nous utilisons la directive assembleur ".def" pour définir la variable "temp" comme égale au "registre de travail" r16. Nous utiliserons le registre r16 comme celui qui stocke les numéros que nous voulons copier dans divers ports et registres (qui ne peuvent pas être écrits directement).

Exercice 1: essayez de copier un nombre binaire directement dans un port ou un registre spécial comme DDRB et voyez ce qui se passe lorsque vous essayez d'assembler le code.

Un registre contient un octet (8 bits) d'information. Essentiellement, il s'agit généralement d'une collection de SR-Latchs, chacun étant un "bit" et contenant un 1 ou un 0. Nous pourrons en discuter (et même en créer un!) Plus tard dans cette série. Vous vous demandez peut-être ce qu'est un "registre de travail" et pourquoi nous avons choisi r16. Nous en discuterons dans un futur tutoriel lorsque nous plongerons dans le bourbier des internes de la puce. Pour l'instant, je veux que vous compreniez comment faire des choses comme écrire du code et programmer du matériel physique. Ensuite, vous aurez un cadre de référence à partir de cette expérience qui rendra les propriétés de mémoire et de registre du microcontrôleur plus faciles à comprendre. Je me rends compte que la plupart des manuels d'introduction et des discussions le font dans l'autre sens, mais j'ai découvert que jouer à un jeu vidéo pendant un certain temps pour avoir une perspective globale avant de lire le manuel d'instructions est beaucoup plus facile que de lire le manuel en premier.

rjmp Init; première ligne exécutée

Cette ligne est un "saut relatif" vers l'étiquette "Init" et n'est pas vraiment nécessaire ici puisque la commande suivante est déjà dans Init mais nous l'incluons pour une utilisation future.

Initialiser:

ser temp; définissez tous les bits de temp sur 1.

Après l'étiquette Init, nous exécutons une commande "set register". Cela définit tous les 8 bits du registre "temp" (dont vous vous souvenez est r16) à 1. Temp contient donc maintenant 0b11111111.

sortie DDRB, temp; définir un bit sur 1 dans le registre d'E/S de direction des données

; pour PortB, qui est DDRB, définit cette broche comme sortie; un 0 définirait cette broche comme entrée; donc ici, toutes les broches PortB sont des sorties (définies sur 1)

Le registre DDRB (Data Direction Register for PortB) indique quelles broches sur PortB (c'est-à-dire PB0 à PB7) sont désignées comme entrée et lesquelles sont désignées comme sortie. Puisque nous avons la broche PB0 connectée à notre LED et le reste non connecté à quoi que ce soit, nous définirons tous les bits sur 1, ce qui signifie qu'ils sont tous des sorties.

température ldi, 0b11111110; charger le numéro « immédiat » dans le registre temporaire

; si c'était juste ld alors le deuxième argument serait; doit être un emplacement mémoire

Cette ligne charge le nombre binaire 0b11111110 dans le registre temp.

hors DDRD, temp; mv temp à DDRD, le résultat est que PD0 est entré et

; le reste sont des sorties

Maintenant, nous définissons le registre de direction des données pour PortD à partir de temp, puisque temp contient toujours 0b11111110, nous voyons que PD0 sera désigné comme une broche d'entrée (puisqu'il y a un 0 à l'extrême droite) et le reste est désigné comme sorties car il y a 1 est dans ces endroits.

clr temp; tous les bits de temp sont mis à 0

sortie PortB, temp; régler tous les bits (c'est-à-dire les broches) dans PortB à 0V

Tout d'abord, nous "effacons" le registre temp, ce qui signifie que tous les bits sont mis à zéro. Ensuite, nous le copions dans le registre PortB qui définit 0V sur toutes ces broches. Un zéro sur un bit PortB signifie que le processeur gardera cette broche à 0V, un un sur un bit fera que cette broche sera définie sur 5V.

Exercice 2: Utilisez un multimètre pour vérifier si toutes les broches de PortB sont réellement à zéro. Est-ce qu'il se passe quelque chose de bizarre avec PB1 ? Une idée pourquoi cela pourrait être? (similaire à l'exercice 4 ci-dessous puis suivez le code…) Exercice 3: Supprimez les deux lignes ci-dessus de votre code. Le programme fonctionne-t-il toujours correctement ? Pourquoi?

ldi temp, 0b00000001; charger le numéro immédiat dans temp

out PortD, temp; déplacer la température vers PortD. PD0 est à 5V (a une résistance pullup); puisqu'il a un 1 dans ce bit, le reste est à 0V. Exercice 4: supprimez les deux lignes ci-dessus de votre code. Le programme fonctionne-t-il toujours correctement ? Pourquoi? (Ceci est différent de l'exercice 3 ci-dessus. Voir le schéma de brochage. Quel est le paramètre DDRD par défaut pour PD0 ? (Voir page 90 de la fiche technique

Tout d'abord, nous "chargeons immédiatement" le numéro 0b0000001 dans temp. La partie "immédiate" est là puisque nous chargeons un numéro direct dans temp plutôt qu'un pointeur vers un emplacement mémoire contenant le numéro à charger. Dans ce cas, nous utiliserions simplement "ld" plutôt que "ldi". Ensuite, nous envoyons ce numéro à PortD qui définit PD0 à 5V et le reste à 0V.

Maintenant, nous avons défini les broches comme entrée ou sortie et nous avons configuré leurs états initiaux comme 0V ou 5V (LOW ou HIGH) et nous entrons maintenant dans notre programme "boucle".

Principal: en temp, PinD; PinD contient l'état de PortD, copiez-le dans temp

; si le bouton est connecté à PD0 alors ce sera; un 0 lorsque le bouton est enfoncé, 1 sinon depuis; PD0 a une résistance de rappel, il est normalement à 5V

Le registre PinD contient l'état actuel des broches PortD. Par exemple, si vous avez connecté un fil 5V à PD3, puis au cycle d'horloge suivant (ce qui se produit 16 millions de fois par seconde puisque nous avons le microcontrôleur connecté à un signal d'horloge de 16MHz) le bit PinD3 (à partir de l'état actuel de PD3) deviendrait un 1 au lieu d'un 0. Donc, dans cette ligne, nous copions l'état actuel des broches dans temp.

sortie PortB, temp; envoie les 0 et les 1 lus ci-dessus à PortB

; cela signifie que nous voulons que la LED soit connectée à PB0, donc; lorsque PD0 est FAIBLE, il règlera PB0 sur FAIBLE et tournera; sur la LED (l'autre côté de la LED est connecté; à 5V et cela mettra PB0 à 0V pour que le courant circule)

Maintenant, nous envoyons l'état des broches dans PinD à la sortie PortB. En fait, cela signifie que PD0 enverra un 1 à PortD0 à moins que le bouton ne soit enfoncé. Dans ce cas, puisque le bouton est connecté à la terre, cette broche sera à 0V et elle enverra un 0 au PortB0. Maintenant, si vous regardez le schéma de circuit, 0V sur PB0 signifie que la LED s'allumera puisque l'autre côté est à 5V. Si nous n'appuyons pas sur le bouton, pour qu'un 1 soit envoyé à PB0, cela signifierait que nous avons 5V sur PB0 et également 5V de l'autre côté de la LED et donc il n'y a pas de différence de potentiel et aucun courant ne circulera et donc le La LED ne brillera pas (dans ce cas, c'est une LED qui est une diode et donc le courant ne circule que dans un sens, peu importe mais peu importe).

rjmp Principal; revient au début

Ce saut relatif nous ramène à notre étiquette Main: et nous vérifions à nouveau PinD et ainsi de suite. Vérifier tous les 16 millionièmes de seconde si le bouton est enfoncé et régler PB0 en conséquence.

Exercice 5: Modifiez votre code pour que votre LED soit connectée à PB3 au lieu de PB0 et voyez que cela fonctionne. Exercice 6: Branchez votre LED sur GND au lieu de 5V et modifiez votre code en conséquence.

Étape 4: Conclusion

Dans ce tutoriel, nous avons étudié plus en détail le langage d'assemblage de l'ATmega328p et appris à contrôler une LED avec un bouton-poussoir. En particulier, nous avons appris les commandes suivantes:

ser register met tous les bits d'un registre à 1

Le registre clr met tous les bits d'un registre à 0

dans le registre, le registre e/s copie le numéro d'un registre e/s vers un registre de travail

Dans le prochain tutoriel, nous examinerons la structure de l'ATmega328p et les différents registres, opérations et ressources qu'il contient.

Avant de continuer avec ces tutoriels, je vais attendre et voir le niveau d'intérêt. S'il y a un certain nombre de personnes qui aiment vraiment apprendre à coder des programmes pour ce microprocesseur en langage assembleur, alors je continuerai et construirai des circuits plus compliqués et utiliserai un code plus robuste.

Conseillé: