C Avr Progra
C Avr Progra
Halim Djerroud
[email protected]
7 février 2017
1
Table des matières
1 Microcontrôleur AVR 3
1.1 Architecture microcontrôleur AVR . . . . . . . . . . . . . . . . . 3
1.2 Diagramme microcontrôleur AVR . . . . . . . . . . . . . . . . . . 3
1.3 La description des broches . . . . . . . . . . . . . . . . . . . . . . 5
1.4 Caractéristiques . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
1.5 Le plan mémoire . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
3 Registres 8
3.1 Convention d’utilisation des registres Rx avec gcc . . . . . . . . . 8
5 La pile 12
6 Entrées Sorties 13
7 watchdog 14
8 L’assembleur AVR 15
8.1 L’assembleur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
8.2 Structure d’un programme assembleur . . . . . . . . . . . . . . . 15
8.2.1 Section données . . . . . . . . . . . . . . . . . . . . . . . . 16
8.2.2 Section texte . . . . . . . . . . . . . . . . . . . . . . . . . 16
9 Chaine de compilation 17
9.1 Compilation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
9.2 Téléversement du programme . . . . . . . . . . . . . . . . . . . . 17
9.3 Makefile . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
9.4 Utilisatation des C et Assembleur . . . . . . . . . . . . . . . . . . 19
9.5 Organisation du projet . . . . . . . . . . . . . . . . . . . . . . . . 19
9.5.1 Visibilité des fonctions . . . . . . . . . . . . . . . . . . . . 20
9.5.2 Les variables . . . . . . . . . . . . . . . . . . . . . . . . . 20
10 Exemple code en C 21
2
1 Microcontrôleur AVR
1.1 Architecture microcontrôleur AVR
L’architecture des microcontrôleur de AVR, est basé sur sur conception Har-
vard. Ce type d’architecture sépare physiquement la mémoire programme de la
mémoire de données. L’accès à chacune des deux mémoires s’effectue via deux
bus différents. L’avantage de ce type d’architecture contrairement a l’architec-
ture Von Neumann, elle permet de transférer les données et les instructions
à exécuter simultanément. Ainsi l’unité centrale de traitement (CPU) permet
d’avoir accès simultanément a l’instruction en cours d’exécution et les données
associées.
Dans l’architecture que nous avons choisi pour ce cours (ATmega 328) La
mémoire programme est relié sur un bus 16 bits, par contre la mémoire de
données qui ne compte qu’un bus de 8 bits.
Les microcontrôleur AVR sont conçu au tour de la technologie RISC (en an-
glais Reduced Instruction Set Computer), cette technologie consiste à déplacer
les complexités majeures du hardware vers le software, alors que la technolo-
gie CISC (en anglais Complex Instruction Set Computer) fait exactement le
contraire. La conception de la technologie RISC fait en sort de minimiser le
nombre d’instructions disponibles ce qui a pour avantage de rendre la majeure
partie des instructions exécutables en un seul cycle d’horloge, de plus un nombre
d’instruction réduit permet de simplifier la conception des fonctions d’optimi-
sation des compilateurs. Alors que l’architecture RISC offre une large gamme
d’instructions ce qui permet effectivement de réduction du nombre d’instruc-
tions nécessaires pour exécuter le programme, mais augmente la complexité du
interne microcontrôleur ce qui a pour effet d’augmenter moyennement le nombre
de cycles d’horloge nécessaires pour exécuter une instruction. Dans ce cas, la
fréquence de travail du système est réduite car il faut introduire une phase
d’interprétation du code machine à travers des microcodes.
3
de Programme, ce dernier contient l’adresse de la prochaine instruction qui va
être stocker dans le registre d’instruction.
L’une des tâches les plus importantes du microcontrôleur est de décoder l’ins-
truction dans le registre d’instructions afin de contrôler les différents éléments
du microcontrôleur, dans le schéma précédent nous avons représenté cette unité
de contrôle dans un bloc que nous avons appelé Décodeur d’Instruction.
La mémoire de données est de type SRAM (en anglais : Static Random Access
Memory) peux être adressée de deux façons différente, soir directement par une
adresse qui provient du registre d’instruction, on appel se mode d’adressage
4
l’adressage direct, ou-bien via une adresse qui provient du bloc de registre, on
appel se mode d’adressage l’adressage indirect.
Un autre registre aussi important appelé Registre d’état (en anglais Status
Register)il est sur 8bits, ce registre permet de récolter certains informations sur
l’état du microcontrôleur par exemple il permet de savoir si une division par zéro
s’est produite ou par exemple une addition qui se termine avec un débordement
etc.
1.4 Caractéristiques
Le processeur AVR a une architecture Harvard c’est-à-dire que le programme
est séparé des données. La mémoire des données comprend :
— 32 registres de l’adresse 0 à l’adresse 31. Ces registres sont des cases
mémoires qui sont directement accessibles par le processeur.
— des registres E/S de l’adresse 32 à 95 Celui qu’il faut retenir est celui à
l’adresse 95 qui est le registre d’état nommé SREG (Status REGister)
— un pointeur de pile (Stack Pointer) aux adresses 93 et 94 : quand on fait
un appel à un sous-programme la première chose que fait cette instruction
est d’empiler l’adresse de retour, c’est-à-dire écrire l’adresse de retour
dans la mémoire de manière à récupérer à la fin d’exécution du sous-
programme. La pile est descendante avec post-décrémentation : le push
se fait par post-décrémentation et pop, par pré-incrémentation.
5
1.5 Le plan mémoire
Mémoire programme
Mémoire données
6
2 Cycle d’exécution d’une instruction AVR
2.1 Recherche de l’instruction
(en anglais : Instruction Fetch)
7
3 Registres
8
Règles à respecter :
1. les paramètres sont alignés sur des numéros pairs Par exemple, si on
a la fonction suivante : uint8 t fonc(char a, char b), si le pre-
mier paramètre est R24 (R24:R25), le deuxième paramètre sera R22
(R22:R23) Si on a fonc(uint32 t a, uint32 t b), a sera dans R22
R23 R24 R25 et b dans R18 R19 R20 R21
2. le premier paramètre est le numéro plus élevé
3. on utilise la convention little endian
9
4 Registre d’état : Status Register (SREG)
Le registre SREG contient des indicateurs liés aux opérations et le bit d’au-
torisation générale des interruptions. Les bits de ce registre sont : Z (Zero), C
(Carry), S (Sign) ...
Bit 7 – I – Global Interrupt Enable Ce bit est mis à la valeur logique haute
(c’est-à-dire 1) pour activer l’utilisation des interruptions. Ce bit est mis à la
valeur logique basse (c’est-à-dire 0) après une demande d’interruption. Il est
remis à 1 par l’instruction RETI au retour d’une routine d’interruption.
Bit 6 – T – Bit Copy Storage Les instructions de copie des bits (BLD, bit
lu et BST, bit emmagasiné) utilisent le bit T comme source et destination dans
les opérations effectuées sur chaque bit d’un registre. Un bit d’un registre peut
être copié dans le bit T par l’instruction BST alors que le bit T peut être copié
dans un autre registre à travers l’instruction BLD.
Bit 5 – H – Half Carry Flag Ce bit indique qu’une opération arithmétique a
généré une retenue ou un dépassement.
Bit 4 – S – Sign Bit Le bit S est donné par un OR exclusif entre le flag
négatif N et celui complémenté à 2 du flag V. Il indique le signe de la donnée
après avoir exécuté une opération arithmétique.
Bit 3 – V – Overflow flag Ce bit contient le résultat d’overflow et est en
complément à deux. Traduit littéralement, overflow veut dire débordement.
C’est une condition dans laquelle une opération arithmétique fournit un résultat
de grandeur supérieure au maximum qu’un registre ou une position de mémoire
peut contenir.
10
Bit 2 – N – Negative flag
Bit 1 – Z – Zero flag
Bit 0 – C – Carry flag Ces trois bits indiquent, respectivement, au terme
d’une opération mathématique ou logique, si le résultat est négatif, si le résultat
est égal à 0, si l’opération a donné lieu, en plus du résultat, à une retenue. Le
registre d’état (Status Register) n’est pas automatiquement sauvegardé lorsque
l’on appelle une routine d’interruption.
init:
ldi r16,0 ;
11
5 La pile
La pile est principalement utilisée pour stocker des données temporaires, pour
stocker des variables locales et pour stocker des adresses de retour après des
interruptions et des appels de sous-routine. La pile est mise en œuvre à mesure
que les emplacements de mémoire de plus en plus élevés se situent à des niveaux
inférieurs. Le Registre pointeur de pile (Stack Pointer) pointe toujours vers le
haut de la pile.
init:
ldi r16,lo8(RAMEND)
out SPL,r16
ldi r16,hi8(RAMEND)
out SPH,r16
12
6 Entrées Sorties
Port A (PA7. . . PA0) : C’est un port de I/O bidirectionnel. Toutes les pattes
du port ont des résistances internes de pull-up. Le buffer de sortie est en mesure
de fournir jusqu’à 20 mA de courant, suffisant pour piloter un afficheur à Led.
Les pattes sont en haute impédances quand une condition de reset devient active,
ou bien lorsque l’horloge n’est pas active. Ce port est utilisé comme multiplexer
d’entrée/ sortie pour les données et les adresses quand une SRAM externe est
reliée (broches 32 à 39).
Port B (PB7. . . PB0) : C’est un port de I/O bidirectionnel. Toutes les pattes
du port ont des résistances internes de pull-up. Le buffer de sortie est en mesure
de fournir jusqu’à 20 mA de courant. Les pattes du port sont en haute impédance
quand une condition de RESET devient active, ou bien quand l’horloge n’est
pas active (broches 1 à 8).
Port C (PC7. . . PC0) : C’est un port de I/O bidirectionnel. Toutes les pattes
du port ont des résistances internes de pull-up. Le buffer de sortie est en mesure
de fournir jusqu’à 20 mA de courant. Les broches sont en haute impédance
quand une condition de RESET devient active, ou bien quand l’horloge n’est
pas active. Lorsqu’on branche de la SRAM externe, ce port est utilisé comme
bus adresses en sortie vers la SRAM (broches 21 à 28).
Port D (PD7. . . PD0) : C’est un port de I/O bidirectionnel. Toutes les pattes
du port ont des résistances internes de pull-up. Le buffer de sortie est en mesure
de fournir jusqu’à 20 mA de courant. Les broches sont en haute impédance
quand une condition de RESET devient active, ou bien quand l’horloge n’est
pas active (broches 10 à 17).
13
7 watchdog
Le watchdog est un temporisateur particulier qui est utilisé dans les systèmes
à microprocesseur comme sécurité pour éviter que le programme n’aille dans une
impasse et donc que le système ne se bloque dans une situation non prévue par
le programmeur. En pratique, le watchdog intervient et effectue le reset du mi-
crocontrôleur si celui-ci n’est pas effectué par l’instruction “WDR” (WatchDog
Reset) dans le temps établi par les broches 0, 1 et 2 du registre “WDTCR”. Le
watchdog des microcontrôleurs AVR est temporisé par une horloge interne à 1
MHz, ce qui nous permet de comprendre qu’il peut fonctionner également en
l’absence de l’horloge du système car il est indépendant de celui-ci. Le dispositif
est programmé à travers le registre “WDTCR” grâce à l’utilisation des cinq
premiers bits. Essayons maintenant d’en comprendre le fonctionnement en les
analysant de façon détaillée.
14
8 L’assembleur AVR
8.1 L’assembleur
Les fichiers assembleur portent l’extension “.S”, ces fichiers sont de type texte
ils contiendrons le code source. Votre code assembleur peut être écrit sur un ou
plusieurs fichiers. Vous pouvez utiliser un simple éditeur de texte pour écrire
votre code par exemple emacs, gedit, kate etc, ou-bien utiliser un IDE (en an-
glais : Integrated development environment ), ce dernier vous propose des fonc-
tions avancées par exemple que assembler l’ensemble des fichier dans un projet,
une coloration syntaxique, compilation, téléversement etc.
L’étape suivante, une fois le code écrit, est de rassembler ces fichiers et les
traduire dans un langage compréhensible par le microcontrôleur. Cette étape
se fait avec l’outil assembleur, qu’on appel communément le compilateur. Le
compilateur d’une manière générale il vérifie les fichiers en entré, si tout est
correct, alors il génère un fichier exécutable. Cependant si une erreur est détectée
lors du processus de compilation alors le processus s’arrête et une ou plusieurs
erreurs sont affichées avec les détails tel que le fichier dans lequel l’erreur s’est
produite, le numéro de ligne ainsi que les causes possibles ;
Le fichier exécutable généré à l’étape précédente ne peut pas être chargé direc-
tement sur le microcontrôleur, pour cela nous rajoutons une étape supplémentaire
qui permet de traduire cet exécutable au format binaire chargeable sur le mi-
crocontrôleur.
.end ;; fin
15
8.2.1 Section données
Cette section est utilisée pour définir les structure de données qui seront
manipulés dans la partie code, ces données sont sauvegardées en mémoire.
Cette ligne contient trois éléments, le premier élément mon texte : est
appelé Label, c’est un identifiant qui permet de référencer l’adresse en mémoire
où la donnée ”Mon texte” est stockée en mémoire . Le second élément est une
directive, elle est destinée au compilateur afin de lui expliquer que ce qui suit
est une suite de caractères ASCII suivit d’un ’0x00’, c’est-à-dire un String,
autrement la deuxième partie permet de définir le type de la donnée.
...
.section .text
.global main
main :
push 24
push 27
...
...
pop 27
pop 24
RET
.end
16
9 Chaine de compilation
9.1 Compilation
Un programme assembleur porte l’extension “.s” Pour assembler un pro-
gramme on utilise la commande avr-as l’option -g, que nous avons ajouté, per-
met d’insérer dans le code généré les symboles de debug, cette option est seule-
ment nécessaire dans le cas où vous allez utiliser le dans la suite le débogueur
GDB (avr-gdb). L’option -mmcu permet de choisir le type du microcontrôleur
pour lequel nous allons générer le code objet.
avr-as -g -mmcu=atmega328p -o hello.o hello.s
Après l’exécution de cette commande un fichier objet est généré dans le nom
est spécifié par l’option -o . Il contient le code binaire mais il n’indique aucun
emplacement mémoire spécifique. Cela se fait à l’étape suivante par l’éditeur de
liens qui lie tous les objets sources et localise le code à une adresse physique
spécifique.
avr-ld -o hello.elf hello.o
La commande avr-ld est appelé l’éditeur de lien, elle permet d’assembler un
ou plusieurs fichier objet dans un seul fichier binaire. Le résultat de cette com-
mande est un fichier elf (Executable and Linkable Format, format exécutable
et liable), ce format de binaire n’est généralement pas supporté par les micro-
contrôleurs. Avant de pouvoir le téléverser sur le microcontrôleur, il doit être
converti dans le format Intel HEX (extention des fichies .hex). Il s’agit d’un for-
mat de fichier ASCII pour les données binaires. Il est généralement largement
utilisé pour la programmation de microcontrôleurs. La conversion de format se
fait avec l’outil objcopy :
avr-objcopy -O ihex -R .eeprom hello.elf hello.hex
l’option de -O permet de choisir le type du fichier en sortie .
17
9.3 Makefile
Afin de vous facilité la génération de votre binaire et le réléversement vous
pouvez vous servir du makefile suivant :
TARGET = $(notdir $(CURDIR))
SOURCES = $(wildcard *.S)
OBJECTS = $(SOURCES:.S=.o)
MCU = atmega328p
#MCU = atmega16
## settings for Uno
USBDEV = /dev/ttyACM0
BAUD = 115200
# settings for Duemilanove
#USBDEV = /dev/ttyUSB0
#BAUD = 57600
AS = avr-as
CC = avr-gcc
LD = avr-ld
OBJDUMP = avr-objdump
CPP = avr-cpp
CFLAGS = -g -mmcu=$(MCU)
ASFLAGS = -g -mmcu=$(MCU)
LDFLAGS = -Tdata=0x800100 -nostdlib
#LDFLAGS = -Tbss=0x800100 -Tdata=0x800300
AVRDUDE = avrdude
AVRDUDE_CONF = /etc/avrdude.conf
all: $(TARGET).hex
$(TARGET).elf: $(OBJECTS)
$(CC) $(LDFLAGS) $(OBJECTS) -o $@
$(TARGET).hex: $(TARGET).elf
avr-objcopy -O ihex -R .eeprom $(TARGET).elf $(TARGET).hex
upload: $(TARGET).hex
$(AVRDUDE) -C $(AVRDUDE_CONF) -p $(MCU) -c arduino -P $(USBDEV) -b $(BAUD) \
-D -U flash:w:$(TARGET).hex:i
dump: $(TARGET).elf
$(OBJDUMP) -d $(TARGET).elf
clean:
rm -f $(TARGET).elf $(TARGET).hex $(OBJECTS)
18
9.4 Utilisatation des C et Assembleur
L’utilisation de l’assembleur permet d’optimiser vos codes et vous produira un
programme rapide, ou lorsque vous voulez utiliser des instructions spécifiques à
l’architecture et non descriptibles par une syntaxe de haut niveau par exemple
en C. Ou tout simplement effectuer des calcules plus efficacement par exemple
additionner deux nombres de 128 bits, dans ce cas la tâche s’avère difficile en C
mais très facile à coder en assembleur.
Bien que cela ne soit pas strictement requis (vous pouvez utiliser un éditeur
de texte et un compilateur en ligne de commande), tous les fichiers sources
assembleur doivent avoir une extension ”.S”. Cela permettra à l’interface du
compilateur C d’appeler automatiquement l’assembleur et l’éditeur de liens au
besoin. En outre, le préprocesseur C sera automatiquement invoqué en permet-
tant à l’utilisation de constantes symboliques.
Un des fichiers source devra produire un fichier objet ”principal” pour l’éditeur
de liens afin que l’éditeur de liens connaisse le point d’entrée du programme. Le
plus commun est un fichier C avec une fonction appelée ”main()”. Un fichier
Assembleur avec un sous-programme nommé ”main” et déclaré global (en uti-
lisant la directive ”.global”) produira également un module nommé ”main”. La
combinaison de C et d’assemblage dans un seul projet soulève plusieurs ques-
tions en fonction des besoins du programme qu’on souhaite écrire :
— Comment une fonction C peut-elle être rendue visible à une routine as-
sembleur de sorte que la routine assembleur puisse appeler la fonction C ?
19
— Comment les variables sont-elles passées au code assembleur ?
Dans la suite de cette section nous allons essayer de répondre a ces questions
et donner des exemples pratique pour chaque cas.
/* Code C */
.extern ma_function_C
Une routine du langage assembleur doit être déclarée comme globale dans
le code assembleur afin qu’elle soit visible par le programme C. Cela se fait en
utilisant la directive ”.global” :
;; Code assembleur
.global ma_function_assembleur
/* Code C */
extern unsigned char ma_function_assembleur (unsigned char, unsigned int);
20
10 Exemple code en C
http ://www.atmel.com/webdoc/avrassembler/avrassembler.wbd irectives.html
Programme C
#include <avr/io.h>
#include <util/delay.h>
while (1) {
/* set all pins of PORTB high */
PORTB = 0xFF;
_delay_ms(500);
Compilation
avr-gcc -Os -DF_CPU=16000000UL -mmcu=atmega328p -c -o exemple_1.o exemple_1.c
Fichier Hexadécimal
avr-objcopy -O ihex -R .eeprom exemple_1.elf exemple_1.hex
Téléversement
avrdude -C /etc/avrdude.conf -p atmega328p -c arduino -P /dev/ttyACM0 \
-b 115200 -D -U flash:w:exemple_1.hex:i
21