This commit is contained in:
Geoffrey Frogeye 2017-05-16 07:24:29 +02:00
parent b0e0a8d9a4
commit c8ada584c2
8 changed files with 401 additions and 187 deletions

View file

@ -1,101 +1,194 @@
% Compte-rendu TP2
% DJERABA Taky PREUD'HOMME Geoffrey
% 03/05/2017
# Compte-rendu TP1
## DJERABA Taky PREUD'HOMME Geoffrey
# TP2
Ce TP a pour but de nous familiariser avec la gestion des interruptions et du watchdog sur l'Atmega 2560.
## Un bouton/une LED et un chien de garde (TP20)
!include(TP20.txt lang=asmplus)
!include(TP20.txt lang=avrasmplus)
Ce programme allume la LED lors de l'appui sur un bouton, et ce jusqu'à une seconde après l'avoir relâché. Si on rappuie sur le bouton pendant l'intervalle des 1s, le compteur est remis à zéro.
## Chenillard par interruption (TP21)
```pseudo
On nous demande ici la même chose qu'au TP11, les différence étant :
- Les changement doivent se faire sur une interruption et non plus dans une boucle
- On peut tourner dans les deux sens et non plus dans un seul
On écrira alors notre code dans des parties d'interruption, et se contentera de duppliquer le code en "changeant le sens" pour gérer le sens inverse. La seule partie commune étant l'actualisation de la valeur des ports, on créera une fonction pour cette dernière.
On utilise le registre `port` comme un booléen. On considère qu'une valeur paire correspond à vrai, et une valeur impaire correspond à faux. On peut donc vérifier si le port est vrai ou faux en comparant son bit de point faible avec 0 ou 1. L'intérêt réside surtout dans le fait qu'on peut inverser l'état du booléen avec une simple incrémentation ou décrémentation.
La boucle principale du code reste sur l'instruction sleep, ce qui permet au microprocesseur de ne rien faire tant qu'il n'y a pas d'interruption, et ainsi économiser de l'énergie.
```avrpseudo
chen ← 0b00000001
port ← 0x00 ; pair : port A, impair : port B
boucle: jump boucle ; On attend là si rien ne se passe
port ← 0x00 ; pair : chenillard sur le port A, impair : port B
boucle:
sleep ; On attend là si rien ne se passe
jmp boucle
horaire:
Si (port & 0x01) == 0 alors DecalerGauche chen
Si (port & 0x01) == 1 alors DecalerDroite chen
Si chen != 0 saut boucle
Si (port & 0x01) == 0 alors chen ← 0b00000001
Si (port & 0x01) == 1 alors chen ← 0b10000000
Incrementer port ; On change de port
call afficher
Retourner
Si (port & 0x01) == 0 alors DecalerGauche chen
Si (port & 0x01) == 1 alors DecalerDroite chen
Si chen != 0 saut boucle
Si (port & 0x01) == 0 alors chen ← 0b00000001
Si (port & 0x01) == 1 alors chen ← 0b10000000
Incrementer port ; On change de port
call afficher
Retourner
antihoraire:
Si (port & 0x01) == 0 alors DecalerDroite chen
Si (port & 0x01) == 1 alors DecalerGauche chen
Si chen != 0 saut boucle
Si (port & 0x01) == 0 alors chen ← 0b10000000
Si (port & 0x01) == 1 alors chen ← 0b00000001
Incrementer port ; On change de port
call afficher
Retourner
Si (port & 0x01) == 0 alors DecalerDroite chen
Si (port & 0x01) == 1 alors DecalerGauche chen
Si chen != 0 saut boucle
Si (port & 0x01) == 0 alors chen ← 0b10000000
Si (port & 0x01) == 1 alors chen ← 0b00000001
Incrementer port ; On change de port
call afficher
Retourner
afficher:
Si (port & 0x01) == 0 alors PortB@IO ← 0x00
Si (port & 0x01) == 1 alors PortA@IO ← 0x00
Si (port & 0x01) == 0 alors PortA@IO ← chen
Si (port & 0x01) == 1 alors PortB@IO ← chen
Retourner
Si (port & 0x01) == 0 alors PortB@IO ← 0x00
Si (port & 0x01) == 1 alors PortA@IO ← 0x00
Si (port & 0x01) == 0 alors PortA@IO ← chen
Si (port & 0x01) == 1 alors PortB@IO ← chen
Retourner
```
On a pu constater qu'éxecuter une instruction de décalage en tant qu'instruction conditionelle (j'entend par là ce qui est éxécuté lorsque la condition d'un `si` est vraie) n'est pas possible avec l'assembleur étendu (bien que tout à fait réalisable en assembleur standard). On a donc triché un peu et utilisé le fait que multiplier ou diviser un nombre par sa base (ici 2) permet de décaler ses chiffres vers la gauche ou vers la droite repectivement.
!include(TP21.txt lang=avrasmplus)
## Compteur (TP22)
1)
On nous demande de faire l'affichage d'un compteur qui s'incrémente toutes les secondes.
```pseudo
L'affichage sur un afficheur 7 segments avait déjà été réalisé au TP précédent, on peut reprendre le tableau de correspondances nombre ↔ représentation sur les 7 segments.
Pour les différentes étapes proposées, voici ce qu'il faut faire :
## Comptage de 1 digit sur le simulateur
On considère un registre d0 stockant ce digit (et non sa représentation sur le 7 segment). On configure le Watchdog de façon à ce qu'il effectue une interruption toutes les secondes. L'interruption se chargera d'incrémenter ce registre et de le remettre à 0 au cas où il viendrait à dépasser 9, puis d'envoyer sa représentation sur le port A.
```avrpseudo
d0 ← 0
Configurer Watchdog: Interruption toutes les secondes sur wd
boucle: jump boucle
boucle:
sleep
jmp boucle
wd:
Incrementer d0
Si d0 > 10 jump d0 ← 0:
; Logique
Incrementer d0
Si d0 > 10 saut d0 ← 0:
; Affichage
Sortir afficheur[d0] sur portA
Retourner
```
4)
```pseudo
## Comptage de 1 digit sur la maquette
Le code ne doit à priori pas changer, il faut juste s'assurer qu'un bit parmi PC5 à PC7 soit à un afin que le digit soit bien affihé sur l'un des trois segments.
Cependant, on se rend compte que le Watchdog sur la carte se comporte bizarrement : en effet il compte approximativement 100 fois plus vite que sur le simulateur, pour exactement le même code. Nous n'avons pas réussi à résoudre ce problème, cependant nous aurions pu utiliser le timer (qui lui semble fonctionner) qui incrémente un registre, et éxecuter le code de l'interruption dès que ce registre dépasse une certaine valeur ($\frac{1}{\text{periode du timer}}$) correspondant à une seconde.
## Comptage de 3 digits sur le simulateur
À d0 viennent s'ajouter d1 et d2, correspondant aux digit de poids plus élevés. Ici, dès qu'un digit dépasse 9, il se remet à 0 et incrémente le suivant.
Il suffit ensuite d'afficher chaque digit sur son port correspondant.
```avrpseudo
d2 ← 0
d1 ← 0
d0 ← 0
select ← 0x01
io ← 0x00
Configurer Watchdog: Interruption toutes les secondes sur wd
Configurer timer0: Interruption toutes les 8-16 ms sur tm
boucle: jump boucle
boucle:
sleep
jmp boucle
wd:
Incrementer d0
Si d0 < 10 Retourner
d0 ← 0
Incrementer d1
Si d1 < 10 Retourner
d1 ← 0
Incrementer d2
Si d2 < 10 Retourner
d2 ← 0
Retourner ; Comportement si >999 : on revient à 000
Incrementer d0
Si d0 < 10 Saut afficher
d0 ← 0
Incrementer d1
Si d1 < 10 Saut afficher
d1 ← 0
Incrementer d2
Si d2 < 10 Saut afficher
d2 ← 0
; Comportement si >999 : on revient à 000
tm:
PortC@IO ← select
Si select = 0x01 alors io ← afficheur@ROM[d0]
Si select = 0x02 alors io ← afficheur@ROM[d1]
Si select = 0x03 alors io ← afficheur@ROM[d2]
DecalerGauche io
Si io > 0x04 alors io ← 0x01
Retourner
afficher:
Sortir afficheur[d2] sur portC
Sortir afficheur[d1] sur portB
Sortir afficheur[d0] sur portA
Retourner
```
## Comptage de 3 digits sur la maquette
C'est ici un peu plus compliqué, en effet le port A est utilisé pour chacun des afficheurs 7 segments. On peut cependant sélectionner l'afficheur avec le port C (PC5 à PC7). L'astuce consiste à afficher un afficheur à la suite et ce suffisament rapidement (un affichage peut se faire entre 8 et 16 ms) pour que l'œil ait l'impression qu'ils soient tous affichés en même temps. On utilisera pour ça un timer, qui toutes les 4 ms ($<\frac{16}{3}$) changera le digit affiché.
On stockera le digit en cours d'affichage dans un registre `select`. Il contiendra en réalité la configuration du PortC à envoyer pour prendre l'afficheur voulu. Il suffit ensuite de le décaler vers la droite ou vers la gauche et de recommencer (tel un chenillard) à chauque interruption de timer.
```avrpseudo
d2 ← 0
d1 ← 0
d0 ← 0
select ← 0x80
Configurer Watchdog: Interruption toutes les secondes sur wd
Configurer timer0: Interruption toutes les 4 ms sur tm
boucle:
sleep
jmp boucle
wd:
Incrementer d0
Si d0 < 10 Retourner
d0 ← 0
Incrementer d1
Si d1 < 10 Retourner
d1 ← 0
Incrementer d2
Si d2 < 10 Retourner
d2 ← 0
Retourner ; Comportement si >999 : on revient à 000
tm:
PortC@IO ← select
Si select = 0x80 alors PortA ← afficheur[d0]
Si select = 0x40 alors PortA ← afficheur[d1]
Si select = 0x20 alors PortA ← afficheur[d2]
DecalerGauche select
Si select = 0 alors select ← 0x80
Retourner
```
En assembleur étendu, cela donne :
!include(TP23.txt lang=avrasmplus)
## Minuteur d'un four (TP23)
Cet exercice nous propose de rassembler ce que l'on a appris dans ce TP dans un seul programme. L'algorithme étant plutôt simple, il sera décrit en commentaire du programme.
Quelques notes cependant :
- On utilise le fait qu'une décrémentation d'un registre déjà à 0 provoque un dépassement et sa valeur repasse à 255, et le flag du dépassement est mis à 1. Pour savoir si un nombre était à 0 avant de faire une décrémentation, on vérifiera si le nombre n'est plus dans son intervalle de définition "normal" (0-9 pour un chiffre). Il aurait été plus "propre" de vérifier le drapeau, mais plus coûteux (utilise un masque et une comparaison).
- Le watchdog est initialisé au début du programme et n'est plus réinitialisé après, son interruption associée a un effet en fonction de si le four est en route ou non. Cela a deux désavantages : le four compte entre 0 et 1 seconde de moins que nécessaire en fonction de quand a été appuyé le bouton marche par rapport à l'état du watchdog, et une interruption est effectuée chaque seconde même si le four est en mode veille (ce qui n'est pas une bonne idée d'un point de vue énergétique). Il aurait été plus judicieux d'activer le watchdog lorsque de l'appui sur le bouton marche et de l'arrêter lors de la fin de décompte.
- Notre four affiche 0 pendant une seconde alors que la plupart des fours affichent 1 pendant une seconde et puis s'arrêtent. Ce comportement n'a pas été spécifié dans le cahier des charges, on en a donc profité pour simplifier notre logique.
!include (TP23.txt lang=avrasmplus)

View file

@ -1,14 +1,10 @@
.equ PINA = 0x00 ; définition des adresses des ports
.equ PINA = 0x00 ; définition des adresses des ports
.equ DDRA = 0x01
.equ PORTA = 0x02
.equ PINB = 0x03
.equ DDRB = 0x04
.equ PORTB = 0x05
.equ EIMSK = 0x3D
.equ EICRA = 0x69
.equ EICRB = 0x6A
.equ SREG = 0x3F
.equ WDTCSR = 0x60
@ -23,10 +19,10 @@
; Vecteur RESET
jmp debut
.org 0x0002 ; Définition du code à éxecuter lors des interruption
.org 0x0002 ; Définition du code à éxecuter lors des interruption
jmp horaire
.org 0x0004 ; Définition du code à éxecuter lors des interruption
.org 0x0004 ; Définition du code à éxecuter lors des interruption
jmp antihoraire
.org 0x0080
@ -38,8 +34,7 @@ debut:
EIMSK <- 0b00000011 ; On active les interruptions PD0 et PD1 sur front descendant
EICRA <- 0b00001010
EICRB <- 0b00000000
; On active les interruptions au niveau du µP
SREG <- 0b10000000
; On active les interruptions au niveau du µP
chen <- 0b00000001
chen <- 0xAA
@ -48,7 +43,7 @@ debut:
sei
boucle:
sleep ; On ne fait rien jusqu'à la prochaine interruption
sleep ; On ne fait rien jusqu'à la prochaine interruption
jump boucle
horaire:
@ -84,4 +79,4 @@ afficher:
si (port & 0x01) == 1 alors PORTA@IO <- 0x00
si (port & 0x01) == 0 alors PORTA@IO <- chen
si (port & 0x01) == 1 alors PORTB@IO <- chen
ret
ret

View file

@ -1,15 +1,10 @@
.equ PINA = 0x00 ; définition des adresses des ports
.equ PINA = 0x00 ; définition des adresses des ports
.equ DDRA = 0x01
.equ PORTA = 0x02
.equ PINB = 0x03
.equ DDRB = 0x04
.equ PORTB = 0x05
.equ PINC = 0x06
.equ DDRC = 0x07
.equ PORTC = 0x08
.equ SREG = 0x3F
.equ WDTCSR = 0x60
.equ TCCR0A = 0x24
.equ TCCR0B = 0x25
@ -29,10 +24,10 @@
; Vecteur RESET
jmp debut
.org 0x0018
.org 0x0018 ; Interruption du watchdog
jmp wd
.org 0x002E
.org 0x002E ; Interruption du timer
jmp tm
.org 0x0080
@ -48,22 +43,19 @@ debut:
select <- 0x01
cli
SREG <- 0b10000000
; Configuration des ports
DDRA@IO <- 0xFF
DDRB@IO <- 0xFF
DDRC@IO <- 0xFF
; Watchdog toutes les secondes
WDTCSR <- 0x10
WDTCSR <- 0b01000110
; Timer toutes les 8-16ms
;TCCR0A@IO <- 0x00
;TCCR0B@IO <- 0x04
;TIMSK0 <- 0x01
;TIFR0 <- 0x01
; Timer toutes les 2 ms
TCCR0A@IO <- 0x00
TCCR0B@IO <- 0x04
TIMSK0 <- 0x01
TIFR0 <- 0x01
sei
boucle:
@ -72,28 +64,26 @@ boucle:
wd:
inc d0
si d0 < 10 saut finwd
si d0 < 10 saut affichage
d0 <- 0
inc d1
si d1 < 10 saut finwd
si d1 < 10 saut affichage
d1 <- 0
inc d2
si d2 < 10 saut finwd
si d2 < 10 saut affichage
d2 <- 0
finwd:
PORTA@IO <- afficheur@ROM[d1]
PORTC@IO <- 0x40
affichage:
; Affichage Simulateur
; PORTC@IO <- afficheur@ROM[d0]
; PORTB@IO <- afficheur@ROM[d1]
; PORTA@IO <- afficheur@ROM[d2]
reti
tm:
;PortC@IO <- select
;si select = 0x80 alors PortA@IO <- afficheur@ROM[d0]
;si select = 0x40 alors PortA@IO <- afficheur@ROM[d1]
;si select = 0x20 alors PortA@IO <- afficheur@ROM[d2]
;lsl select
;si select = 0 alors select <- 0x20
;PORTC@IO <- afficheur@ROM[d0]
;PORTB@IO <- afficheur@ROM[d1]
PORTA@IO <- afficheur@ROM[d2]
PORTC@IO <- 0x40
reti
PortC@IO <- select
si select = 0x80 alors PortA@IO <- afficheur@ROM[d0]
si select = 0x40 alors PortA@IO <- afficheur@ROM[d1]
si select = 0x20 alors PortA@IO <- afficheur@ROM[d2]
lsl select
si select = 0 alors select <- 0x20
reti

View file

@ -1,20 +1,11 @@
.equ PINA = 0x00 ; définition des adresses des ports
.equ PINA = 0x00 ; définition des adresses des ports
.equ DDRA = 0x01
.equ PORTA = 0x02
.equ PINB = 0x03
.equ DDRB = 0x04
.equ PORTB = 0x05
.equ PIND = 0x09
.equ DDRD = 0x0A
.equ PORTD = 0x0B
.equ SREG = 0x3F
.equ WDTCSR = 0x60
.equ TCCR0A = 0x24
.equ TCCR0B = 0x25
.equ TIMSK0 = 0x6E
.equ TIFR0 = 0x35
.equ EIMSK = 0x3D
.equ EICRA = 0x69
@ -54,13 +45,12 @@ afficheur:
; 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
debut:
d1 <- 0
d0 <- 0
route <- 0
d1 <- 0 ; Digit dizaines
d0 <- 0 ; Digit unités
route <- 0 ; 1 : Four en marche, 0 : Four à l'arrêt
cli
SREG <- 0b10000000
; Configuration des ports
DDRA@IO <- 0xFF
DDRB@IO <- 0xFF
@ -80,22 +70,22 @@ debut:
call affiche
boucle:
sleep
sleep ; Le programme ne fonctionne que sur interruption
jump boucle
b1s:
inc d0
si d0 < 10 saut finb1s
d0 <- 0
b1s: ; Bouton +1 seconde
inc d0 ; Incrémente le chiffre des unités
si d0 < 10 saut finb1s ; Si il reste inférieur à 10, on affiche juste
d0 <- 0 ; Sinon, on le remet à 0 et on fait comme si on avait appuyé sur le bouton +10 secondes
jump b10s
finb1s:
call affiche
reti
b10s:
inc d1
si d1 < 10 saut finb10s
d0 <- 9
b10s: ; Bouton +10 secondes
inc d1 ; On incrémente le chiffre des dizaines
si d1 < 10 saut finb10s ; Si il reste inférieur à 10, on affiche juste
d0 <- 9 ; Sinon, on bloque à 99
d1 <- 9
finb10s:
call affiche
@ -106,30 +96,30 @@ affiche:
PORTB@IO <- afficheur@ROM[d0]
ret
marche:
route <- 1
marche: ; Bouton marche
route <- 1 ; Met la minuterie en route
PORTD@IO <- 0x80
reti
arret:
route <- 0
arret: ; Bouton annuler
route <- 0 ; Remet tout dans sa position d'origine
d0 <- 0
d1 <- 0
call affiche
reti
wd:
si route = 0 saut finwd
dec d0
si d0 < 10 saut prefinwd
d0 <- 9
si route = 0 saut finwd ; Si on est pas en route, ne rien faire
dec d0 ; Si on est en route, décrémenter le compteur
si d0 < 10 saut prefinwd ; Si le chiffre des unités était strictement supérieur à 0, juste actualiser l'affichage
d0 <- 9 ; Si le chiffre des unités était à 0, il passe à 9 et on décrémente celui des dizaines
dec d1
si d1 < 10 saut prefinwd
route <- 0
si d1 < 10 saut prefinwd ; Si le chiffre des dizaines était strictement supérieur à 0, juste actualiser l'affichage
route <- 0 ; Sinon, c'est qu'on était à 00, donc il faut éteindre le four
PORTD@IO <- 0x00
d0 <- 0
d0 <- 0 ; On remet à 00 (avant ces lignes d0 = 9 et d1 = 255)
d1 <- 0
prefinwd:
call affiche
finwd:
reti
reti

125
TP2/a Normal file
View file

@ -0,0 +1,125 @@
.equ PINA = 0x00 ; définition des adresses des ports
.equ DDRA = 0x01
.equ PORTA = 0x02
.equ PIND = 0x09
.equ DDRD = 0x0A
.equ PORTD = 0x0B
.equ WDTCSR = 0x60
.equ EIMSK = 0x3D
.equ EICRA = 0x69
.equ EICRB = 0x6A
.equ RAMEND = 0x21FF
.equ SPH = 0x3E ; initialisation de la pile
.equ SPL = 0x3D
.def d1 = r20
.def d0 = r21
.def route = r22
.org 0x000
; Vecteur RESET
jmp debut
.org 0x0002
jmp b10s
.org 0x0004
jmp b1s
.org 0x0006
jmp marche
.org 0x0008
jmp arret
.org 0x0018
jmp wd
.org 0x0080
afficheur:
.DB 0x7E, 0x0C, 0x37, 0x9F, 0x4D, 0xDB, 0xFB, 0x0E, 0xFF, 0xDF
; 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
debut:
d1 <- 0 ; Digit dizaines
d0 <- 0 ; Digit unités
route <- 0 ; 1 : Four en marche, 0 : Four à l'arrêt
cli
; Configuration des ports
DDRA@IO <- 0xFF
DDRB@IO <- 0xFF
DDRD@IO <- 0x80
; Interruptions
EIMSK <- 0b00001111 ; On active les interruptions PD[3:0] sur front descendant
EICRA <- 0b10101010
EICRB <- 0b00000000
; Watchdog toutes les secondes
WDTCSR <- 0x10
WDTCSR <- 0b01000110
sei
call affiche
boucle:
sleep ; Le programme ne fonctionne que sur interruption
jump boucle
b1s: ; Bouton +1 seconde
inc d0 ; Incrémente le chiffre des unités
si d0 < 10 saut finb1s ; Si il reste inférieur à 10, on affiche juste
d0 <- 0 ; Sinon, on le remet à 0 et on fait comme si on avait appuyé sur le bouton +10 secondes
jump b10s
finb1s:
call affiche
reti
b10s: ; Bouton +10 secondes
inc d1 ; On incrémente le chiffre des dizaines
si d1 < 10 saut finb10s ; Si il reste inférieur à 10, on affiche juste
d0 <- 9 ; Sinon, on bloque à 99
d1 <- 9
finb10s:
call affiche
reti
affiche:
PORTA@IO <- afficheur@ROM[d1]
PORTB@IO <- afficheur@ROM[d0]
ret
marche: ; Bouton marche
route <- 1 ; Met la minuterie en route
PORTD@IO <- 0x80
reti
arret: ; Bouton annuler
route <- 0 ; Remet tout dans sa position d'origine
d0 <- 0
d1 <- 0
call affiche
reti
wd:
si route = 0 saut finwd ; Si on est pas en route, ne rien faire
dec d0 ; Si on est en route, décrémenter le compteur
si d0 < 10 saut prefinwd ; Si le chiffre des unités était strictement supérieur à 0, juste actualiser l'affichage
d0 <- 9 ; Si le chiffre des unités était à 0, il passe à 9 et on décrémente celui des dizaines
dec d1
si d1 < 10 saut prefinwd ; Si le chiffre des dizaines était strictement supérieur à 0, juste actualiser l'affichage
route <- 0 ; Sinon, c'est qu'on était à 00, donc il faut éteindre le four
PORTD@IO <- 0x00
d0 <- 0 ; On remet à 00 (avant ces lignes d0 = 9 et d1 = 255)
d1 <- 0
prefinwd:
call affiche
finwd:
reti