CR TP1 rédigé
This commit is contained in:
parent
23da83c351
commit
d3dce08dcc
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -1,4 +1,7 @@
|
|||
*.tmp
|
||||
*.pdf
|
||||
*.html
|
||||
*.hex
|
||||
*.lst
|
||||
*.err
|
||||
!template.html
|
||||
|
|
10
TP1/Makefile
10
TP1/Makefile
|
@ -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))
|
||||
|
|
153
TP1/TP1.md
153
TP1/TP1.md
|
@ -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)
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
23
TP1/TP10.pc
23
TP1/TP10.pc
|
@ -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
|
44
TP1/TP11.pc
44
TP1/TP11.pc
|
@ -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
|
22
TP1/TP11.txt
22
TP1/TP11.txt
|
@ -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
|
||||
|
|
51
TP1/TP12.pc
51
TP1/TP12.pc
|
@ -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
|
|
@ -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
|
||||
|
||||
|
|
|
@ -62,4 +62,4 @@ tempoA:
|
|||
sbci r23,0
|
||||
sbci r24,0
|
||||
brcc tempoA
|
||||
ret
|
||||
ret
|
||||
|
|
Reference in a new issue