Cours Assembleur ATMEGA32 en PDF


Télécharger Cours Assembleur ATMEGA32 en PDF

★★★★★★★★★★3.5 étoiles sur 5 basé sur 1 votes.
Votez ce document:

Télécharger aussi :


Cours Assembleur ATMEGA32

Introduction

Ce document est la suite du document Assembleur Microcontrôleur ATMEL ATMEGA que j’ai déjà réalisé. Vous trouverez dans ce document plusieurs chapitres sur l’initiation au microcontrôleur ATMEGA et sur l’utilisation des instructions assembleurs.

Un rappel sommaire des opérations logiques (ET, OU, NON, OU Exclusif) et la conversion décimale hexadécimale et binaire sera aborder.

Le fonctionnement des indicateurs du registre de statut SREG avec le positionnement en fonction des opérations arithmétiques et logiques.

Et enfin l’apprentissage de l’assembleur en lui-même avec des exemples commentés et des références aux deux autres documents déjà ‘francisée’.

Le mode d’adressage et les opérations de saut et sous-programme avec exemple.

La présentation du programme d’assemblage AVR est vue en dernier pour faire le teste des exemples.

Bien entendu, vous pouvez me signaler les erreurs éventuelles qui se seraient glissées dans ce document à ‘l’issus de mon plein gré !’.

Mon mail à changé pour cause de pollution, c’est maintenant :  .

Très bonne lecture à vous !                                                      Jean-Noël, en mai 2005 Salvador de Bahia, Brésil.

Le Plan Mémoire

Le cœur AVR combine 32 registres spéciaux travaillants directement avec l'Unité Arithmétique de Logique

ALU, qui représente le registre d’accumulateur A (B ou D) dans les microcontrôleurs classiques. L’accumulateur est le cœur du système, c’est lui qui s’occupe des opérations de calcul et de comparaison logique.

Ces registres doivent être considérés par l’utilisateur comme des variables mémoires d’un octet (8 bits) pouvant prendre une valeur comprise entre 0 et 255. Ces variables sont notées ‘rxx’ ou xx prend les valeurs 00 à 31, donc ‘r0’ à r31’. La variable par défaut est la variable ‘r16’ qui peut être utilisé par toutes les instructions.

Ces registres spéciaux sont en accès direct avec le cœur du microcontrôleur par l’intermédiaire d’une simple instruction qui est exécutée en un seul cycle d'horloge. Cela signifie que pendant un cycle d'horloge l’Unité Arithmétique et Logique ‘ALU’ exécute l'opération et le résultat est stocké, après coup, dans le registre de sortie, le tout dans un seul cycle d'horloge.

L'architecture résultante est plus efficace en réalisant des opérations jusqu'à dix fois plus rapidement qu’avec des microcontrôleurs conventionnels CISC.

Les registres spéciaux sont dit aussi registre d'accès rapide et 6 des 32 registres peuvent être employés comme trois registre d'adresse 16 bits pour l'adressage indirects d'espace de données (X, Y & Z). Le troisième Z est aussi employé comme indicateur d'adresse pour la fonction de consultation de table des constantes, mais nous reviendrons dessus ultérieurement.

Les 32 registres sont détallés dans le tableau qui suit avec l’adresse effective dans la mémoire SRAM :

Bit 7 à 0

Adresse

Registre Spéciaux

R0

$00

R1

$01

Rn

$xx

R26

$1A

Registre X Partie Basse

R27

$1B

Registre X Partie Haute

R28

$1C

Registre Y Partie Basse

R29

$1D

Registre Y Partie Haute

R30

$1E

Registre Z Partie Basse

R31

$1F

Registre Z Partie Haute

Comme vous l’avez compris, trois types de mémoire sont utilisés dans la série ATMEGA, la mémoire programme FLASH, la mémoire de donnée SRAM et la mémoire morte de type EEPROM.

La mémoire programme

La mémoire programme permet de stoker et de faire fonctionner le microcontrôleur, il contient de 4 à 256 Ko de programme selon le modèle du microcontrôleur. Le nombre d’écriture sur cette mémoire est limité à

10.000, largement suffisant pour la majorité des applications. La figure 1 donne un exemple de l’adressage de la mémoire FLASH du modèle ATMEGA 32.

Figure 1, Adressage de la mémoire FLASH.

La mémoire de donnée

La mémoire de donnée contient les 32 registres de travail, les 64 registres de commande et la mémoire SRAM pour les variables du programme de 2048 octets pour le modèle ATMEGA 32. La figure 2 présente les relations entre espace physique et registre. 

Figure 2, la mémoire de donnée avec la SRAM.

La fin de la mémoire SRAM est utilisée par la pile pour stocker les données de celle-ci en remontant dans la mémoire. De ce fait, la fin de la SRAM n’est pas réellement disponible en fonction de l’usage que vous faite de la pile.

La mémoire morte

La mémoire morte est de type EEPROM d’accès plus complexe contiendra la configuration du programme et les données importantes qui seront sauvées pendant l’absence de courant électrique. On peut écrire jusqu’à 100.000 fois dans l’EEPROM. La taille de l’EEPROM est fonction du modèle de microcontrôleur ATMEGA (de 256 bits à 8 Ko).

Base et Opération Logiques

Voici un petit rappel sur les bases hexadécimales, binaire et les opérations logiques pour en comprendre le fonctionnement.

Attention : les opérateurs logiques ici employés sont des références internationales, mais ne sont malheureusement pas les mêmes que ceux utilisés par l’assembleur pour des raisons de droit principalement.

Les Opérations Booléennes

L’opérateur ET (&)

L’opérateur ET est une multiplication booléenne qui si les deux valeurs sont à 1 donnera un résultat à 1 :

Opérateur 1

Opérateur 2

ET Logique

0

0

0

0

1

0

1

0

0

1

1

1

L’opérateur OU (!)

L’opérateur OU est une addition booléenne qui si l’une des deux valeurs est à 1 donnera un résultat à 1 :

Opérateur 1

Opérateur 2

OU Logique

0

0

0

0

1

1

1

0

1

1

1

1

L’opérateur OU Exclusif (¯ )

L’opérateur OU Exclusif est une addition booléenne exclusive qui si l’une des deux valeurs seulement est à 1 donnera un résultat à 1 :

Opérateur 1

Opérateur 2

OU Exc. Logique

0

0

0

0

1

1

1

0

1

1

1

0

L’opérateur NON (¯)

L’opérateur NON est une négation booléenne qui inverse la valeur, un 1 donnera un résultat à 0 et vis versa :

Opérateur

NON Logique

0

1

1

0

La combinaison des opérations est possible, par exemple un ET et un NON donnera un NONET qui inversera le résultat tout simplement. Dans le document qui suit, les valeurs NON seront misent en rouge.

Les Bases 16, 10 et 2

Les chiffres et les nombres ne sont pas représentés tel quel dans la mémoire du microcontrôleur, ils sont converties en information binaire (en base 2) sur 8 bits (Bit = élément binaire valant 0 ou 1), qui donne un octet (octet ensemble de 8 bits) et sont représentées de la manière suivant : b00000000. On peut avoir jusqu'à 256 valeurs différentes avec ce système, de 0 à 255 avec 8 bits d’informations.

Inconvénient majeur, ce n’est pas très pratique d’aligner des lignes de 0 et de 1 pour écriture un programme. La conversion en hexadécimale (base 16) à donc été pris comme référence pour les systèmes à microcontrôleur et à microprocesseur à 8 bits et plus. Il reste que le binaire est pratique pour le contrôle des Port d’entrée/sortie ou pour localiser rapidement une information dans un registre. La table qui suit donne un exemple de conversion simple et rapide :

Décimale

Binaire

Hexadécimale

0

b0000

$0

1

b0001

$1

2

b0010

$2

3

b0011

$3

4

b0100

$4

5

b0101

$5

6

b0110

$6

7

b0111

$7

8

b1000

$8

9

b1001

$9

10

b1010

$A

11

b1011

$B

12

b1100

$C

13

b1101

$D

14

b1110

$E

15

b1111

$F

Comme on peut le voir, l’hexadécimal ou encore ‘l’hexa’ est constitué de chiffre et de lettre, avec un chiffre en hexa on représente 4 bits, il faut donc 2 chiffres hexa pour faire un octet, celui de gauche sera le poids fort du nombre et celui de droite sera le poids faible :

Décimal

Poids Fort

Poids Faible

Hexa

165

$A

$5

$A5

37

$2

$5

$25

Le signe de l’hexa est le dollar ‘$’ et le signe du binaire est le ‘b’, il n’y a pas de signe pour le décimal.

Pour convertir un chiffre de l’hexa en décimale, il suffit de multiplier par 16 le poids fort et d’ajouter le poids faible : $A = 10 et 10 x 16 = 160 + $5 = 165, vraiment simple non.

Pour faire l’inverse c’est un peu plus dur, il faut diviser le nombre décimal par 16 qui donnera le poids fort et le reste de la division donnera le poids faible.

165

16

10

5

Ceci reste valable pour les valeurs inférieures à 255 bien sûr.

Pour les valeurs supérieures, il faut au préalable diviser le nombre par 256 et reprendre le même principe pour les deux parties, d’autre méthode existe, mais je me limiterais à ce rappel qui est suffisant dans la majorité des cas.

Les Registres d’Etat SREG

Le registre indispensable au système pour fonctionner est le registre d’état SREG. En effet, ce registre permet de réaliser les opérations de branchements qui autorisent des applications complexes.

Registre SREG (Status Register)

Le registre SREG ou registre d’état sert principalement avec les fonctions arithmétiques et logiques pour les opérations de branchements. Il indique et autorise aussi le fonctionnement des interruptions. Il est modifié par le résultat des manipulations mathématiques et logiques. C’est le principal registre qui sert à effectuer les branchements conditionnels après une opération arithmétique ou logique. Il peut être lu et écrit à tout moment sans aucune restriction.

Adresse

7

6

5

4

3

2

1

0

$3F

I

T

H

S

V

N

Z

C

L/E

L/E

L/E

L/E

L/E

L/E

L/E

L/E

L/E

I Global Interrupt Enable sert à activer (1) ou interdire (0) toutes les sources d’interruptions. Si ce bit n’est pas activé alors que vous avez programmé des interruptions, elles ne seront pas prises en compte.

T Copy Storage joue un rôle de tampon lors de manipulation de bits avec les instructions BLD et BST.

H Half Cary signale qu'une demi retenue a été réalisée lors de l’emploi d’une instruction arithmétique. S Sign bit bit de signe résultant d’un OU exclusif avec le bit N etV.

VOverflow bit indique un dépassement de capacité lors des opérations arithmétique et logique.

NNegative bit signale que le résultat de la dernière manipulation arithmétique est négatif.

Z Zéro bit le résultat de la dernière manipulation arithmétique est égal à zéro. C Cary bit l'opération arithmétique a donné lieu à une retenue.



Et bien maintenant, voici comment cela marche avec quelques exemples simples.

 

Le programme minimum

Pour commencer, je vais présenter et commenter le programme minimum que le microcontrôleur à besoin pour fonctionner. Cet exemple permet de faire clignoter une LED branché sur le port A0 du microcontrôleur.

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

;COMPOSANT

: ATMEL AVR 8 Bits (RISC) - ATMega32            Quartz = 8 MHz

;PROGRAMME

:

;VERSION         

:  V 1.00

;DATE              

:  10/05/2005

;DERNIERE MAJ.

: xx/xx/2005

;DESCRIPTION

:  Exemple de clignotement d’une LED sur port A0

;AUTEUR          

: Jean-Noël

L’entête permet de savoir sur quoi l’on travail et de renseigner l’utilisateur sur le type de programme.

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

             ;                         DIRECTIVES D'ASSEMBLAGE

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

            ;.DEVICE          atmega32        ;Type de Microcontrôleur (définit dans l'include)

             .INCLUDE        "" ;Fichier de définition du microcontrôleur

Les directives d’assemblage sont indispensables au programme qui va transformer le code assembleur en code machine que seul comprend le processeur. Je vous recommande de regarder le fichier ‘’ avec un éditeur de texte sans le modifier, cela vous donnera une idée de son contenu.

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

             ;                         PORTS D'ENTREES/SORTIES

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

             ; Port A0           Branché sur une LED

;     A1 à A7 Libre ; Port B0 à B7 Libre ; Port C0 à C7  Libre

; Port D0 à D7 Libre

L’utilisation des entrés/sorties connecter sur le processeur avec les déférents périphériques faire référence de manière sûr.

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

             ;                          CONSTANTES

;***************************************************************************** ; Pas de constante

Les constantes peuvent être utilisées dans le programme pour faire référence à des valeurs irrémédiablement fixes.

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

             ;                          DEFINITIONS DE REGISTRES

;***************************************************************************** ; r0 à r15            Libre

             ; r16 à r20          Utilisé par le programme

             ; r21 à r25         Libre

             ; r26 à r31         Réservé pour registre X, Y, Z

L’utilisation des registres dans le programme permet de classer et d’utiliser correctement ceux-ci sans nuire à la lisibilité du programme.

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

             ;                          ORGANISATION RAM

;***************************************************************************** .DSEG

             .ORG     $60      ; début de la mémoire disponible pour l’utilisateur (vous)

;Ici on peut définir des variables locales d’un octet pour stocker des valeurs quelconques. vAlumer:           .BYTE 1          ;Led alumé si = 1, éteinte si 0

Ici, nous rentrons dans l’espace RAM ou l’on peut définir des adresses mémoires par des noms simples pour stocker des données. L’adresse RAM commence toujours à $60, avant il y a les registres spéciaux et  les registres d’entrées/sorties (voir le plan mémoire).

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

             ;                          INTERRUPTIONS ET RESET

;***************************************************************************** ; Même si les interruptions ne sont pas utilisées ces définitions doivent être déclarées

.CSEG                          

;Segment de Code

.ORG $0000                 

;Positionnement au début de la mémoire

       jmp RESET             

;Reset Handler

      jmp EXT_INT0        

;IRQ0 Handler

      jmp EXT_INT1       

;IRQ1 Handler

      jmp EXT_INT2       

;IRQ2 Handler

      jmp TIM2_COMP 

;Timer2 Compare Handler

      jmp TIM2_OVF  

;Timer2 Overflow Handler

      jmp TIM1_CAPT  

;Timer1 Capture Handler

      jmp TIM1_COMPA 

;Timer1 CompareA Handler

      jmp TIM1_COMPB 

;Timer1 CompareB Handler

      jmp TIM1_OVF  

;Timer1 Overflow Handler

      jmp TIM0_COMP 

;Timer0 Compare Handler

      jmp TIM0_OVF  

;Timer0 Overflow Handler

      jmp SPI_STC          

;SPI Transfer Complete Handler

      jmp USART_RXC 

;USART RX Complete Handler

      jmp USART_UDRE 

;UDR Empty Handler

      jmp USART_TXC 

;USART TX Complete Handler

      jmp ADC_COMP 

;ADC Conversion Complete Handler

      jmp EE_RDY          

;EEPROM Ready Handler

      jmp ANA_COMP  

;Analog Comparator Handler

      jmp TWI                  

;Two-wire Serial Interface Handler

      jmp SPM_RDY       

;Store Program Memory Ready Handler

Les vecteurs d’interruption ne sont pas fixés dans le processeur, c’est à nous de le faire par cette déclaration. Cela est fortement recommandé, même si vous n’utiliser pas les interruptions. Le premier branchement que fait le processeur est un RESET, donc si le vecteur n’est pas défini, le programme ne fonctionnera jamais ! Si une interruption à lieu de manière inopportune et que le vecteur n’est pas déclarer le processeur se bloque. ;*****************************************************************************

             ;                          PROGRAMME PRINCIPAL (RESET)

;***************************************************************************** ;Initialise la pile en bas de la mémoire RAM en adresse 16 bits

RESET: ldi

r16, HIGH(RAMEND)

;Charge la valeur haute de l’adresse en fin mémoire RAM

               out

SPH, r16                      

;Positionne le pointeur de pile haut sur cette adresse 

               ldi

r16, LOW(RAMEND)

;Charge la valeur basse de l’adresse en fin mémoire RAM

               out

SPL, r16                       

;Positionne le pointeur de pile bas sur cette adresse

Le programme va donc exécuter le saut sur le branchement RESET, celui-ci va commencer par initialiser la pile à la fin de la mémoire RAM, sur une adresse à 16 bits, donc pour cela on utilise les directives de compilations ‘HIGH’ et ‘LOW’ qui extrait les parties haute et basse de l’adresse 16 bits. On commence toujours par la partie haute puis la partie basse ensuite.

L’instruction ‘ldi’ permet de lire une variable mémoire et de la transférer dans un registre spécial (d’accès rapide), soit r16 = partie haute de RAMEND. Pour la première ligne du programme.

L’instruction ‘out’ permet d’écrire dans les registres d’entrées/sorties, soit SPH = r16 (SPH adresse haute de la pile).

;-----------------------------------------------------------------------------

;Initialise du WatchDog (Chien de garde) pour éviter de bloquer le processeur (Facultatif)

               wdr                                          ;Mis à 0 du compteur du Watchdog              ldi    r16, $0F                       ;WDE = 1 avec base de temps maximum 1900 ms        out       WDTCR, r16  ;Ecriture du registre du WatchDog

Le WatchDog (chien de garde) est un programme interne au processeur qui surveille le fonctionnement du processeur, si celui-ci s’arrête d’une manière ou d’une autre, le WatchDog s’en aperçoit et relance le processeur depuis le début (exécution d’un Reset). L’instruction ‘wdr’ doit être placée judicieusement dans le programme pour remettre à zéro le compteur, ceci autant de fois que nécessaire.

;-----------------------------------------------------------------------------

            ;Initialisation Port A     ;PA0 = Led – PA1 à PA7 libres

  ser  r16  ;Port en sortie (les bits du port sont mis à 1, soit en sortie)   out  DDRA,  r16 ;Ecriture sur le   clr r16  ;Port en bas (les bits du port sont mis à 0, Led éteinte)   out  PORTA, r16 ;Port A mis à zéro

L’initialisation du Port A est ensuite effectuée en utilisant la variable rapide ‘r16’. Celle-ci est positionnée à 0 ou à 255 par respectivement les instructions ‘clr’ et ‘ser’.

Pourquoi utiliser la variable ‘r16’ et non la variable ‘r0’, la raison en est fort simple, les variables ‘r16’ à ‘r32’ sont utilisables par toutes les instructions alors que les variables ‘r0’ à ‘r15’ on des restrictions d’utilisation pour certaines instructions et il faudrait vérifier à chaque fois que la variable peut être utilisée.

Les constantes DDRA et PORTA sont définies dans le fichier des définitions du processeur, renseigné au début du programme dans la fonction ‘.include’. Elles remplacent l’adresse exacte des registres des entrées/sorties et permettent de rendre plus lisible le programme en langage assembleur.

;-----------------------------------------------------------------------------

;Fin d’initialisation

                            wdr                              ;Réamorce le watchdog

                            jmp     Debut              ;Fin d'initialisation, saute au début

La fin de l’initialisation du programme renvoie vers le point de départ du programme par l’instruction ‘jmp’ qui est un saut inconditionnel vers une étiquette* ‘Début’. On en profite pour remettre à zéro le WatchDog.

;_____________________________________________________________________________

;Interruptions non utilisées

EXT_INT0:       

;IRQ0

EXT_INT1:       

;IRQ1

EXT_INT2:       

;IRQ2

TIM2_COMP:

;Timer2 Comparaison

TIM2_OVF:       

;Timer2 Overflow

TIM1_CAPT:

;Timer1 Capture

TIM1_COMPA:

;Timer1 CompareA

TIM1_COMPB:

;Timer1 CompareB

TIM1_OVF:      

;Timer1 Overflow

TIM0_COMP:

;Timer0 Compare

TIM0_OVF:      

;Timer0 Overflow

SPI_STC:          

;SPI Transfer Complete

USART_RXC:

;USART RX Complete

USART_UDRE:

;UDR Empty

USART_TXC:

;USART TX Complete

EE_RDY:          

;EEPROM Ready

ADC_COMP:

;ADC Conversion Complète

ANA_COMP:

;Analog Comparator

TWI:                 

;Two-wire Serial Interface

SPM_RDY:       

;Store Program Memory Ready

               nop       

            ;Ne rien faire dans cette interruption

               reti        

            ;Fin de l’interruption

Les vecteurs d’interruption étant définis, il renvoie sur des étiquettes* qu’ils faut définir une fois dans le programme, même si les interruptions ne sont pas utilisés. 

Si une interruption est déclanchée, l’exécution des instructions sera faite, soit ‘nop’ et ‘reti’.

nop’ est une instruction qui ne fait rien, elle consomme un cycle d’horloge c’est tout.

reti’ est l’instruction qui signale la fin d’un sous-programme d’interruption. Elle est indispensable en fin de programme lors de l’appel d’une interruption.

*Une étiquette est la représentation d’une adresse physique dans la mémoire programme. Elle est constituée d’un mot suivi par : ‘Deux points’. Cela permet d’écrire des programmes avec des branchements sans connaître l’adresse d’arrivée du saut à l’avance, pratique non. C’est le programme d’assemblage qui s’occupera de localiser l’adresse du saut et de la remplacer en lieu et place de l’étiquette.

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

;_____________________________________________________________________________

;Programme principal

;_____________________________________________________________________________

             Debut:                                       ;Programme principal

;Exemple, cligotement de la LED

 wdr    ;Réamorce le WatchDog  in r16, PORTA ;Lire Port A  ldi r17, PA0 ;Le port A0

   eor r16, r17     ;Inversion du bit 0 de r16        out PORTA, r16 ;Ecrir Port A0

Nous arrivons enfin au programme principal, celui qui doit faire ceux pour quoi il est fait, faire clignoter une LED à un rythme définit.

En premier lieu, on remet le WatchDog à zéro avec l’instruction ‘wdr’, cela évitera au processeur de faire un reset inutile. Cette opération doit être faite au moins une fois par second, sinon, le processeur exécute un Reset automatique.

Pour faire clignoter la LED, on doit savoir si elle est allumée ou éteinte, pour ce faire, il faut lire l’état du

Port A et en extraire l’information, allumé si égale à 1, sinon éteinte. C’est le but de l’instruction ‘in r16, PORTA’. L’instruction ‘in’ permet de lire le contenu d’un Port d’entrée/sortie, elle ne fait que cela. C’est l’équivalent en basic de l’attribution d’une variable ; r16 = PortA.



Ensuite, on chargera le bit qui spécifie la position du Port A0 avec l’instruction ‘ldi r17, PA0’. L’instruction ‘ldi’ permet de charger la variable ‘r17’ par le contenu de la constante PA0 qui est défini dans le fichier des définitions du processeur, équivalent Basic de Set r17 = PA0.

L’instruction ‘eor’ effectue un OU Exclusif entre la variable ‘r16’ et ‘r17’, ce qui a pour effet d’inverser le contenu binaire de la valeur de r16 « Port A0 » (voir les opérateurs logiques au début). L’opération est la suivante :

r16’ = b00000000 et ‘r17’ = b00000001, l’on fait un ‘eor’r16’ prendra la valeur b00000001. Si l’on refait l’opération, ‘r16’ reprendra la valeur b00000000, on à bien inversion du bit 0 donc du Port A0 ! Génial ! Puis on modifie le Port A avec l’instruction ‘out’ avec le nouveau contenu de la variable r16 ; la LED change d’état et s’allumera au cycle d’horloge suivant.

Ensuite il faut attendre un certain temps, car sinon, le processeur est trop rapide pour que l’œil humain puis voir le clignotement de la LED, il faut faire attendre le processeur avec une boucle d’attente !

;Boucle d’attente de quelques dixième de seconde

      ldi    r18, 250

;Charge le temps d’attente primaire (125 µs)

        ldi    r19, 200

;Charge le temps d’attente secondaire (25 ms)

        ldi    r20, 20 

;Charge le temps d’attente tertiaire (1/2 seconde)

Attente:             

;Attente 0,125 ns (Fréquence oscillateur à 8MHz)

       dec r18         

;Décrément de 1 de la variable r18 (125 ns)

      nop               

;Attente d’un cycle d’horloge (125 ns)

      brne Attente

;Boucle sur Attente jusqu’à l’obtention d’un zéro dans r18 (250 ns)

Pour calculer le temps que va mettre la boucle avec un quartz à 8 Mhz, il faut tout d’abord connaître le temps que dur un cycle d’horloge, soit 1s / 8.000.000 = 125 ns (nano seconde) par cycle d’horloge CPU.

Ensuite il faut connaître le nombre de cycle que chaque instruction consomme, soit  dec = 1 cycle, nop = 1 cycle et brne = 2 cycles si saut et 1 cycle sans saut. Soit au total 4 cycles d’horloge tant que la boucle n’est pas finit puis 3 quant le test est vrai (r18 = 0).

Donc au maximum on à 4 * 125 ns = 500 ns par boucle, on le multiplie par 250 boucles ce qui donne 125 µs (micro seconde), il faut donc refaire une autre boucle (secondaire) pour avoisiner la demi seconde.

;Boucle secondaire qui réutilise la boucle primaire de 125 µs pour attendre 25 ms

   ldi        r18, 250           ;Réinitialise la boucle primaire     dec r19                        ;Décrémente de 1 la boucle secondaire

                   brne Attente            ;Boucle sur Attente jusqu’à l’obtention d’un zéro dans r19

Donc on à 200 * 125 µs = 25 ms (milliseconde) pour la boucle secondaire, il faut donc refaire une autre boucle (tertiaire) pour avoisiner la demi seconde.

;Boucle tertiaire qui réutilise la boucle secondaire et primaire de 25 ms

   ldi        r19, 200           ;Réinitialise la boucle secondaire     dec r20                        ;Décrémente de 1 la boucle tertiaire

                   brne Attente            ;Boucle sur Attente jusqu’à l’obtention d’un zéro dans r20

Nous avons enfin un temps d’attente de 500 ms, plus ou moins quelques ns perdue dans les opérations intermédiaires, donc 20 * 25 ms = 500 ms soit une demi seconde, ce que nous cherchions à faire.

                   rjmp Debut              ;Boucle infini sur le programme principal

;_____________________________________________________________________________

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

C’est la fin du programme, on reboucle sur le programme principal avec l’instruction ‘rjmp’, qui fait un branchement inconditionnel court sur le programme de début. On aurait pu utiliser ‘jmp’, mais le nombre de cycle d’horloge est un peu plus important et l’occupation en mémoire aussi, avec un octet de plus. L’instruction ‘rjmp’ permet de faire des branchements courts sur moins de 128 octets de déplacement uniquement, le compilateur vous signalera le cas de débordement si le déplacement est supérieur à cette valeur.

Vous pouvez remarquer que le processeur va très vite et qu’il nous faut trois compteurs d’attente pour aboutir à une attente de quelques secondes !

Il est évident que d’autres solutions plus économiques en temps CPU existent, par exemple en utilisant les compteurs ou encore les interruptions, mais cela donne une première idée de la forme d’un programme assez simple.

Nous verrons dans un proche avenir la suite de ces exemples.

Jeu d’Instruction

L’ATMEGA à un jeu de 131 instructions qui seront détaillées dans la suite de ce document. 

Le code instruction est à utiliser dans les programmes écrit en assembleur, l’opérant spécifie les variables ou constantes utilisées par l’instruction, le registre SREG est modifier par l’instruction en fonction du résultat de celle-ci, et le nombre de cycle CPU est donnée en dernier.

Les instructions sont présentées dans les tableaux qui suivent par type :

Code

Opérant

Description

Opération

Registre

Cycle

Instructions Arithmétiques et Logique

 

 

ADD

Rd, Rr

Addition sans Retenue

Rd = Rd + Rr

Z C N V S H

1

ADC

Rd, Rr 

Addition avec Retenue

Rd = Rd + Rr + C

Z C N V S H

1

ADIW

Rd, k 

Addition Immediate 16 bits

Rd+1:Rd = Rd+1:Rd + k

Z C N V S

2 (1)

SUB

Rd, Rr 

Soustraction sans Retenue

Rd = Rd – Rr

Z C N V S H

1

SUBI

Rd, k 

Soustraction Immédiate sans Retenue

Rd = Rd – k

Z C N V S H

1

SBC

Rd, Rr 

Soustraction avec Retenue

Rd = Rd – Rr – C

Z C N V S H

1

SBCI

Rd, K 

Soustraction Immédiate avec Retenue

Rd = Rd – k – C

Z C N V S H

1

SBIW

Rd, K 

Soustraction Immédiate 16 bits

Rd+1:Rd = Rd+1:Rd – k

Z C N V S

2 (1)

AND

Rd, Rr 

ET Logique 

Rd = Rd & Rr

Z N V S

1

ANDI

Rd, k 

ET Logique Immédiat 

Rd = Rd & k

Z N V S

1

OR

Rd, Rr 

OU Logique

Rd = Rd ! Rr

Z N V S

1

ORI

Rd, k 

OU Logique Immédiat 

Rd = Rd ! k

Z N V S

1

EOR

Rd, Rr 

OU Exclusif

Rd = Rd ? Rr

Z N V S

1

COM

Rd 

Complément à 1

Rd = $FF – Rd

Z C N V S

1

NEG

Rd 

Négation (Complément à 2)

Rd = $00 – Rd

Z C N V S

1

SBR

Rd, k 

Mise à 1 dans Registre (OU)

Rd = Rd ! k

Z N V S

1

CBR

Rd, k 

Mise à 0 dans Registre (ET)

Rd = Rd & ($FF - k)

Z N V S

1

INC

Rd

Incrément

Rd = Rd + 1

Z N V S

1

DEC

Rd

Décrément

Rd = Rd – 1

Z N V S

1

TST

Rd

Test à Zéro ou Négatif

Rd = Rd ! Rd

Z N V S

1

CLR

Rd

Effacement du Registre

Rd = $00

Z N V S

1

SER

Rd

Mis à 1 du Registre

Rd = $FF

-

1

MUL

Rd, Rr 

Multiplication non Signé 

R1:R0 = Rd x Rr (UU)

Z C

2 (1)

MULS

Rd, Rr 

Multiplication Signée

R1:R0 = Rd x Rr (SS)

Z C

2 (1)

MULSU

Rd, Rr 

Multiplication Signée avec non

Signé

R1:R0 = Rd x Rr (SU)

Z C

2 (1)

FMUL

Rd, Rr 

Multiplication Fractionnaire non

Signé 

R1:R0=(Rd x Rr)<<1 (UU)

Z C

2 (1)

FMULS

Rd, Rr 

Multiplication Fractionnaire

Signée 

R1:R0=(Rd x Rr)<<1(SS)

Z C

2 (1)

FMULSU

Rd, Rr

Multiplication Fractionnaire Signée avec non Signé 

R1:R0=(Rd x Rr)<<1(SU)

Z C

2 (1)

Code

Opérant



487