CR TP1 rédigé

This commit is contained in:
Geoffrey Frogeye 2017-05-03 08:29:25 +02:00
parent 23da83c351
commit d3dce08dcc
10 changed files with 174 additions and 147 deletions

3
.gitignore vendored
View File

@ -1,4 +1,7 @@
*.tmp
*.pdf
*.html
*.hex
*.lst
*.err
!template.html

View File

@ -1,6 +1,8 @@
.PHONY: default cleantmp clean
default: $(subst md,pdf,$(shell ls *.md))
default: $(subst md,pdf,$(wildcard *.md))
SOURCES=$(wildcard *.asm) $(wildcard *.txt)
%.pdf: %.html
wkhtmltopdf --title "$* - DJERABA Taky PREUD'HOMME Geoffrey" "$<" "$@"
@ -8,11 +10,11 @@ default: $(subst md,pdf,$(shell ls *.md))
%.html: %.tmp ../template.html
pandoc -f markdown+hard_line_breaks "$<" --template ../template.html -o "$@"
%.tmp: %.md
%.tmp: %.md $(SOURCES)
markedpp "$<" > "$@"
cleantmp:
rm -rf $(subst md,html,$(shell ls *.md)) *.tmp
rm -rf $(subst md,html,$(wildcard *.md)) *.tmp
clean: cleantmp
rm -rf $(subst md,pdf,$(shell ls *.md))
rm -rf $(subst md,pdf,$(wildcard *.md))

View File

@ -2,13 +2,152 @@
% DJERABA Taky PREUD'HOMME Geoffrey
% 02/05/2017
# Un bouton / une LED (TP10)
!include (TP10.pc lang=asm)
Ce programme allume la LED situé sur PA0 quand le bouton situé à PA1 est appuyé
# TP1
Ce TP est une introduction à la programmation de l'ATMEGA 2560 et à la simulation du microcontrôleur tout en rappelant les bases de la programmation en assembleur AVR8.
# Chenillard (TP10)
!include (TP11.pc lang=asm)
## Un bouton / une LED (TP10)
!include (TP10.asm lang=asm)
Ce programme permet d'allumer la LED située sur PA0 lorsque le bouton situé sur PA1 est appuyé.
Le programme fait les initialisations habituelles et celles de la configuration de l'entrée / sortie. Ensuite, il lit le port A qu'il stocke dans le registre r16 afin de récupérer l'état du bouton. Pour "copier" le bit correspondant au bouton vers celui correspondant à la sortie, on décale simplement le mot lu depuis le port (que l'on a stocké sur r16) vers la droite. Il ne reste simplement plus qu'à renvoyer le contenu de r16 sur le port A afin de d'allumer la LED si nécessaire. Le programme recommence depuis la lecture du port A ce indéfiniement et suffisament rapidement pour que l'affichage soit instantané.
Étant donné que PA1, normalement connecté sur un bouton (une entrée) dans l'algorithme, est connecté à une LED (une sortie) ne pouvant donc pas être controlée, le programme ne peut fonctionner qu'en mode simulation, donc pour tester que l'envoi du programme sur la carte fonctionne bien, on écrasera la valeur de r16 avec 0x01 avant l'envoi pour tester si la LED s'allume correctement.
## Chenillard (TP11)
On nous demande ici d'allumer des LED à la suite dans un ordre donné. On ne considérera que dans un premier temps une séquence allant de PA0 jusque PA7.
Une approche typique en algorithmique serait de procéder de cette façon :
```pseudo
TantQue vrai
Pour i allant de 0 à 8
PortA ← 0x00
PortA[i] ← 0b1
FinPour
FinTantQue
```
Cette méthode, bien que réalisable, est assez complexe (pour le moment) à réaliser en assembleur. On préfèrera utiliser un registre de 8 bits dont un des bits exactement est à 1 (les autres sont donc à 0). On utilisera ensuite un décalage logique (ici vers la gauche) pour déplacer ce bit "volant". Le décalage logique "remplissant" l'octet avec des 0, aucune autre LED ne sera allumée. Il suffit ensuite d'envoyer cet octet sur le port A pour voir une LED "volante".
La question se pose de savoir ce qui se passe lorsque le bit "volant" "sort" de l'octet. Il ne revient pas de l'autre coté, l'octet est donc 0x00. Il faut donc vérifier que si le registre passe à cette valeur, il faut remettre le bit "volant" de l'autre coté, et ainsi créer une boucle infinie.
Pour que les LED ne clignotent pas trop vite, on utilisera les instructions que l'on nous a fourni pour faire une temporisation, que l'on stockera en tant que fonction pour un accès plus rapide. Il suffit pour cela de veiller à ce que la pile soit bien initialisée, de mettre une étiquette sur la première instruction et rajouter instruction `RET` à la suite (tout en s'assurant que ces instructions ne soient pas placées de sorte à ce qu'elles soient executées sans passer par un appel de fonction).
On aurait donc un programme de ce type :
```pseudo
debut: r17 ← 0b00000001
boucle: Sortir r17 sur portA
Appeler pause
DecalerGauche r17
Si r17 != 0
Saut boucle
FinSi
; Sinon, le bit "volant" est sorti, on le remet
Saut debut
```
Enfin, pour faire un chenillard sur le port A et le port B (l'ordre d'allumage des LED du port B étant l'inverse de celui du port A), on duppliquera le code en le modifiant afin d'utiliser le port B, un décalage vers la droite et faire commencer le bit "volant" sur le registre de l'autre coté. Il suffit de faire en sorte qu'à la fin de la première partie on arrive à la deuxième partie et qu'on revienne à la première partie à la fin de la deuxième. On peut d'ailleurs utiliser le même registre, étant donné qu'il n'y a pas besoin de transferer d'autres données d'une partie à l'autre.
```pseudo
debutA: r17 ← 0b00000001
Sortir 0x00 sur portB
boucleA: Sortir r17 sur portA
Appeler pause
DecalerGauche r17
Si r17 != 0
Saut boucleA
FinSi
debutB: r17 ← 0b10000000
Sortir 0x00 sur portA
boucleB: Sortir r17 sur portB
Appeler pause
DecalerDroite r17
Si r17 != 0
Saut boucleB
FinSi
Saut debutA
```
Ce qui donne, en assembleur étendu :
!include (TP11.txt lang=asmplus)
De même, le port B de la carte étant utilisée par autre chose que des LED, on pourra contempler le chenillard complet avec la simulation, mais qu'un demi chenillard sur la carte.
## Compteur (TP12)
On nous demande d'afficher ici un compteur allant de 0 à 9 sur un afficheur 7 segments. Sur la carte, le port A est relié à un afficheur 7 segments, avec chaque sortie correspondant à un segment (sans compter le point). Pour afficher un chiffre, il faut afficher allumer certains segments, donc configurer le port A d'une certaine manière. On associera à chaque chiffre sa représentation sur l'afficheur (donc une configuration du port A) dans un tableau que l'on stockera sur la mémoire du programme. À chaque fois que l'on voudra afficher un chiffre, on récupèrera sa configuration associée dans le tableau que l'on enverra sur A.
```pseudo
Pour i allant de 0 à 9
PortA ← afficheur[i]
FinPour
```
La carte ayant plusieurs afficheurs 7 segments, il faut sélectionner celui sur lequel on veut faire l'affichage avec le port C. On veut celui du milieu ici, il faut donc mettre PC6 à 1 (et le reste du port à 0). On a besoin de faire cela qu'une seule fois.
Ce qui donne, en assembleur étendu :
!include (TP12.txt lang=asmplus)
## Afficheur numérique (TP13)
On veut ici afficher sur l'affichage numérique le chiffre que l'on vient de taper sur le clavier. La partie afficheur 7 segments ayant déjà été traitée et ne nécessitant aucune modification, on se concentrera sur la partie clavier. Ce dernier de manière à ce qu'à 8 fils correspondent 16 boutons. Les fils de PC0 à PC3 sont des sorties, où l'on envoie un ou plusieur signal qui "reviennent" sur les entrées PC4 à PC7 en fonction des boutons appuyés.
On peut donc penser à deux manières différentes de tester si un bouton est appuyé. La première étant d'envoyer un signal pour chacune des entrées et de vérifier lesquelles des sorties sont activées. Cela donnerait un algorithme de ce type (on utilise un registre io pour stocker temporairement les données qui viennent d'être lues ou vont être écrites sur un port) :
```pseudo
Pour i allant de 0 à 2
io ← 0x00
io[3-i] ← 1
Sortir io sur portC
Lire portC sur io
Pour j allant de 0 à 2
Si io[7-j] = 1
touche ← i * 3 + j
FinSi
FinPour
Si i=0 et io[6]=1 ; Si on a envoyé un signal sur PC3 et on a reçu un signal sur PC6
touche ← 0
FinSi
FinPour
```
Cette méthode est certainement la plus efficace car n'effectue que 3 envois et 3 lectures du port. Elle est cependant plus calculatoire et plus compliquée à implémenter en assembleur. De plus, il semble difficile de l'étendre pour d'autres boutons, en effet il a été nécessaire d'ajouter une exception pour la touche 0 et il serait nécessaire d'en ajouter pour les autres touches spéciales.
On préfèrera utiliser une autre méthode, vue en TD, qui consiste à stocker dans un tableau la configuration du port C pour chaque bouton. Par exemple, pour le bouton 7, on stockera 0b10000010, ce qui correspond à un signal sur PC1 et PC7 qui consituent les deux extrémités du bouton. On testera alors les boutons un par un en envoyant la configuration stockée dans le tableau sur le port, ce qui aura pour effet d'activer le signal de sortie correspondant (les fils PC7 à PC4 étant configurés en entrée aucun signal ne sera envoyé dessus), puis de relire la configuration du port pour vérifier si le signal d'entrée correspondant au bouton testé a bien été activé. Les fils configurés en sortie ont la particularité de garder en mémoire s'ils envoient un signal ou non, et lorsqu'on les lits il renvoient cet état mémoire, on peut donc comparer ce qu'on lit à l'élément du tableau entier (et non forcément la partie entrée).
```pseudo
Tableau touches 0x41, 0x88, 0x48, 0x28, 0x84, 0x44, 0x24, 0x82, 0x42, 0x22
; 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
Pour test allant de 0 à 9
io ← touches[test]
Sortir io sur portC
Lire portC sur io
Si touches[test] = io alors touche ← test
FinPour
```
Cette méthode est probablement plus lente, mais elle reste suffisament rapide pour que l'utilisateur considère la réponse comme instantanée.
À partir de là, on pourra rajouter de nouvelles touches en leur attribuant un numéro spécial, par exemple 10 pour `*` et 11 pour `#`.
On aura donc ceci comme code final, en assembleur étendu :
!include (TP13.txt lang=asmplus)
Cependant, bien que cette méthode fonctionne sur simulateur, on a rencontré un problème lors du test du carte. En effet, les appuis des boutons n'est pas récupéré par le programme, bien que les boutons fonctionnent quand testés individuellement. On soupçone un problème de synchronisation qui fait que la lecture du port ne renvoie pas les valeurs attendues. En ajoutant une temporisation entre l'écriture et la lecture du port rien n'y fait, en restant appuyé sur le bouton non plus.
<!-- TODO -->
Nous n'avons pas pu tester la version 2, mais il aurait certainement fallu de procéder de la façon suivante : étant donné que les port PCX à PCX sont utilisés à la fois pour la sélection de l'afficheur, et pour les entrées du claviers, il faut donc alterner son mode de fonctionnement. Il faut donc configurer les fils de ce port en entrée lorsque l'on utilise le clavier et en sortie quand on veut afficher. Cependant sans modification supplémentaire, on devrait se rendre compte que le microcontrôleur passe plus de temps sur la partie clavier que sur la partie affichage, et en conséquence l'afficheur 7 segment désiré reste très peu de temps allumé. Il suffirait de rajouter une temporisation dans la partie affichage du programme pour que l'afficheur 7 segment désiré soit activé majoritairement. Cela rajouterait un peu de latence au programme, mais cela devrait rester toujours acceptable pour l'utilisateur.
# Compteur (TP12)
!include (TP12.pc lang=asm)

View File

@ -13,13 +13,16 @@
.org 0x0080
debut:
ldi r16,0x01 ; config direction port A
ldi r16,0x01 ; Configuration de la direction des ports
; PA0 : Sortie (LED) donc 1
; PA1 : Entrée (Bouton) donc 0
; PA7-2 : Indéfférent donc arbitrairement 0
out DDRA,r16
boucle:
in r16,PINA ; Lecture du port A
lsr r16 ; décalage vers la gauche : LED → bouton
; ldi r16,0x01 ; Test pour vérifier la LED
lsr r16 ; Décalage vers la droite : le bit associé au bouton va sur celui associé à la LED
; ldi r16,0x01 ; Test pour vérifier la LED
out PORTA,r16 ; Écriture du port A
jmp boucle ; On recommence

View File

@ -1,23 +0,0 @@
.equ PINA = 0x00 ; définition des adresses des ports
.equ DDRA = 0x01
.equ PORTA = 0x02
.equ RAMEND = 0x21FF
.equ SPH = 0x3E ; initialisation de la pile
.equ SPL = 0x3D
.org 0x000
; Vecteur RESET
jmp debut
.org 0x0080
debut:
ldi r16,0x01 ; config direction port A
out DDRA,r16
boucle:
in r16,PINA ; Lecture du port A
lsr r16 ; décalage vers la gauche : LED → bouton
out PORTA,r16 ; Écriture du port A
jmp boucle ; On recommence

View File

@ -1,44 +0,0 @@
.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 RAMEND = 0x21FF
.equ SPH = 0x3E ; initialisation de la pile
.equ SPL = 0x3D
.org 0x000
; Vecteur RESET
jmp debut
.org 0x0080
debut:
ldi r16,0x00 ; config direction ports
out DDRA,r16
out DDRB,r16
r16 <- 0x01
r17 <- 0x00
boucle:
; On décale vers la gauche port A
lsr r16
; Tant que la chenille est toujours dans le port A, on continue
si r16 > 0 saut boucle
; si elle dépasse, alors on la met sur le début du port B
si r17 = 0 alors r17 <- 0x01
lsr r17
; si elle dépasse, on la remet dans le port A
si r17 = 0 alors r16 <- 0x01
; pause
ldli r24,8
tempo:
subi r22,1
sbci r23,0
sbci r24,0
bbrc tempo
saut boucle

View File

@ -16,10 +16,10 @@
.org 0x0080
debut:
ldi r16,0xFF ; config direction ports
ldi r16,0xFF ; On configure les ports A et B en sortie
out DDRA,r16
out DDRB,r16
r17 <- 0x01 ; 8 places de chenillard
r17 <- 0b00000001 ; 8 places de chenillard
boucleA:
@ -29,29 +29,27 @@ boucleA:
call tempo
; On calcule l'état suivant
lsl r17
si r17 > 0 saut boucleA
out porta,r17
r17 <- 0b10000000
saut boucleB
; Si le bit "volant" est sorti de l'octet, on passe à l'autre partie du programme sur le port B
out porta,r17 ; On éteint le port A
r17 <- 0b10000000 ; On configure le registre pour qu'il commence de l'autre coté
saut boucleB ; (pas nécessaire)
boucleB:
; On affiche l'état courant
out portb,r17
call tempo;
; On calcule l'état suivant
lsr r17
si r17 > 0 saut boucleB
out portb,r17
r17 <- 0x01
r17 <- 0b00000001
saut boucleA
; Programme pour faire une pause
tempo:
; On fait une pause
ldi r24,8
@ -60,4 +58,4 @@ tempoA:
sbci r23,0
sbci r24,0
brcc tempoA
ret
ret

View File

@ -1,51 +0,0 @@
.equ PINA = 0x00 ; définition des adresses des ports
.equ DDRA = 0x01
.equ PORTA = 0x02
.equ PINC = 0x06
.equ DDRC = 0x07
.equ PORTC = 0x08
.equ RAMEND = 0x21FF
.equ SPH = 0x3E ; initialisation de la pile
.equ SPL = 0x3D
.def compteur = r16
.org 0x000
; Vecteur RESET
jmp debut
.org 0x0080
.afficheur:
.DB 0x7E, 0x30, 0x6D, 0x79, 0x33, 0x5B, 0x5F, 0x70, 0x7F, 0x7B
; 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
debut:
ldi r16,0x00 ; config direction ports
out DDRA,r16
out DDRC,r16
r16 <- 0x00
boucle:
; pause
ldli r24,8
tempo:
subi r22,1
sbci r23,0
sbci r24,0
bbrc tempo
jump boucle
; On décale vers la gauche port A
lsr r16
; Tant que la chenille est toujours dans le port A, on continue
si r16 > 0 saut boucle
; si elle dépasse, alors on la met sur le début du port B
si r17 = 0 alors r17 <- 0x01
lsr r17
; si elle dépasse, on la remet dans le port A
si r17 = 0 alors r16 <- 0x01
saut boucle

View File

@ -24,7 +24,7 @@ afficheur:
debut:
ldi r16,0xFF ; config direction ports
ldi r16,0xFF ; On met le port A et le port C en sortie
out DDRA,r16
out DDRC,r16
@ -39,7 +39,7 @@ boucle:
seg <- afficheur@ROM[compteur]
out porta,seg
; On incrémente compteur
; On change de chiffre
inc compteur
si compteur > 9 alors compteur <- 0

View File

@ -62,4 +62,4 @@ tempoA:
sbci r23,0
sbci r24,0
brcc tempoA
ret
ret