10 KiB
Compte-rendu TP1 Microprocesseurs
DJERABA Taky PREUD'HOMME Geoffrey (IMA3 TP2)
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.
Un bouton / une LED (TP10)
!include (TP10.asm lang=avrasm)
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 :
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 :
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.
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=avrasmplus)
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.
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=avrasmplus)
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) :
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).
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=avrasmplus)
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.
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 PC7 à PC5 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.