Abonnement aux commentaires

S'abonner pour recevoir les commentaires suivants par email

まこと の ブログ

MaKoTo no burogu — Journal de bord…

Aller au contenu | Aller au menu | Aller à la recherche

AVR Stargate Atlantis

Il y a 3 ans (déjà!) je réalisais la maquette de la porte des étoiles d'Atlantis, intégrant des leds bleues pilotées par un circuit électronique de ma conception

Vous pouvez voir le résultat dans la galerie qui lui est réservée.

Le circuit électronique, aussi simple soit-il a été conçu avec les méthodes «old school» que je connaissais, qui paraitront peut-être déjà archaïques aux élèves électroniciens du moment, qui étudient ce genre de truc pour la forme, mais se concentrent désormais sur l'étude et la programmation des microcontrôleurs (µC).
Et comme j'en causais lors de la réalisation du Joystick USB, il serait temps que je tâte un peu du µC… Ce simple jeux de lumière semble être une bonne l'occasion d'apprendre à programmer un µC.



QFTIF ?

(Que Faut-il Faire ?)
sgatlantist.gif Comme sur l'animation, il s'agit d'allumer successivement les 7 chevrons de la porte des étoiles (les chevrons 8 et 9 s'allumant en même temps que le N°7) puis d'attendre quelques secondes, pour tout éteindre et recommencer à l'infini.

Après étude de la documentation du µC ATMEGA (voir en annexes), j'ai choisi d'utiliser le PortD.
Ce port 8 bits, est donc constitué des 8 broches N°2, N°3, N°4, N°5, N°6, N°11, N°12, N°13.
La broche N°2 correspondant au bit de poids faible PD0 qui peut prendre la valeur 0 (le courant ne passe pas) ou 1 (le courant passe), et ainsi de suite jusqu'à la broche N°13 PD7.
En programmation, il sera possible de charger des mots de 8 bits sur le PortD.

Par exemple, si on veut que le courant passe sur les broches N°3, N°4 et N°11, c'est à dire les bits PD1, PD2 et PD5, on va charger le mot « 00100110 » sur le PortD
Écrit autrement ça donne :

PD7 PD6 PD5 PD4 PD3 PD2 PD1 PD0
 0   0   1   0   0   1   1   0

Ainsi, on peut établir ce schéma :

SCHEMA

Et avec notre exemple : 00100110 sur le PortD, on aura allumé les LEDS N°2, N°3 et N°6

On va maintenant pouvoir établir le programme !

Démarche Algorithmique :

  1. Configurer le port D en sortie.
  2. Début de la boucle :
  3. Charger le port D de manière à allumer la LED 1.
  4. Attendre 1 seconde.
  5. Charger le port D de manière à allumer la LED 2, tout en laissant allumé la LED 1.
  6. Attendre 1 seconde.
  7. Charger le port D de manière à allumer la LED 3, tout en laissant allumées les LED 1 et 2.
  8. Attendre 1 seconde.
  9. Charger le port D de manière à allumer la LED 4, tout en laissant allumées les LED 1, 2 et 3.
  10. Attendre 1 seconde.
  11. Charger le port D de manière à allumer la LED 5, tout en laissant allumées les LED 1, 2,3 et 4.
  12. Attendre 1 seconde.
  13. Charger le port D de manière à allumer la LED 6, tout en laissant allumées les LED 1, 2,3,4 et 5.
  14. Attendre 1 seconde.
  15. Charger le port D de manière à allumer les LED 7, 8, et 9, tout en laissant allumées les LED 1, 2,3,4,5 et 6.
  16. Attendre 8 secondes.
  17. Charger le port D de manière à éteindre toutes des LEDs.
  18. Attendre 3 secondes.
  19. Fin de la boucle, recommencer…


Programme en C :

Après avoir rassemblé mes notions de langage C et potassé des tutos, voici ce que j'ai écrit : (mon premier logiciel libre )

/* AVR Stargate Atlantis */

#include <avr/io.h>		// appelle la bibliothèque des entrée/sorties
#include <util/delay.h>		// appelle la bibliothèque pour définir _delay_ms() 

int main(void)
{
	DDRD = 0xFF;			// PORTD en sortie
	while (1)			// Début de la boucle
    {
		PORTD = 0b00000001;	// allumer la LED de PD0
		_delay_ms (1000);	// attendre 1 seconde
		PORTD = 0b00000011;	// allumer les LED de PD0+PD1
		_delay_ms (1000);	// attendre 1 seconde
		PORTD = 0b00000111;	// allumer les LED de PD0+PD1+PD2
		_delay_ms (1000);	// attendre 1 seconde
		PORTD = 0b00001111;	// allumer les LED de PD0+PD1+PD2+PD3
		_delay_ms (1000);	// attendre 1 seconde
		PORTD = 0b00011111;	// allumer les LED de PD0+PD1+PD2+PD3+PD4
		_delay_ms (1000);	// attendre 1 seconde
		PORTD = 0b00111111;	// allumer les LED de PD0+PD1+PD2+PD3+PD4+PD5
		_delay_ms (1000);	// attendre 1 seconde
		PORTD = 0b01111111;	// allumer les LED de PD0+PD1+PD2+PD3+PD4+PD5+PD6
		_delay_ms (8000);	// attendre 8 secondes
		PORTD = 0b00000000;	// éteindre toutes les LEDs
		_delay_ms (3000);	// attendre 3 secondes
	}	
	return 0;			// fin du programme
}

Il suffit d'écrire ça dans un fichier texte qu'on nome Stargate.c.

Explications de texte supplémentaire :

  • On charge le fichier d'en tête util/delay.h qui contient des routines qui forcent le processeur à attendre simplement en laissant passer les cycles machine sans rien faire d'utile.
  • La fonction delayms laisse s'écouler le temps spécifié en millisecondes en comptant les cycles d'horloge du processeur.


Le «Makefile» :

Le programme Stargate.c se compile avec le Makefile (fournit en annexes), contenu dans le même répertoire.
Avant de lancer la compilation, il faudra modifier quelques directives de celui-ci pour l'adapter à la situation.

MCU = atmega8, indique le type de µC.
F_CPU = 1000000, pour la fréquence du processeur.
TARGET = Stargate, Le nom du fichier de sortie (sans l'extension), doit être identique au nom du fichier .c.

Ainsi ce fichier tout fait pourra servir à d'autre projet !

Compilation :

On a besoin du logiciel gcc-avr installable par la commande :

sudo apt-get install gcc-avr avr-libc binutils-avr

Puis il suffit de se placer dans le répertoire contenant le fichier .c et le makefile et de lancer la commande :

make

Ce qui crée le fichier Stargate.hex.

Les Bits fusibles :

Si le µC est neuf, on ne va pas toucher à la configuration des bits fusibles.
Sinon, il faudra lui remettre les valeurs par défaut, incluant notamment la prise en compte de l'oscillateur interne @1Mhz :

Sont désactivés (=1 attention, logique inverse) : CKSEL0, SUT1, BODEN, BODLEVEL, BOOTRST, ESAVE, CKOPT, WDTON, RSTDISBL
Sont activés (=0 attention, logique inverse): CKSEL1, CKSEL2, CKSEL3, SUT0, BOOTSZ0, BOOTSZ1,SPIEN

En binaire, les LOW Byte Fuses sont notés ainsi :

BODLEVEL|BODEN|SUT1|SUT0|CKSEL3|CKSEL2|CKSEL1|CKSEL0
    1   |  1  |  1 |  0 |   0  |   0  |   0  |   1

Les HIGH Byte Fuses :

RSTDISBL|WDTON|SPIEN|CKOPT|ESAVE|BOOTSZ1|BOOTSZ0|BOOTRST
    1   |  1  |  0  |  1  |  1  |   0   |   0   |   1

Pour programmer ça avec avrdude, on a besoin de ces valeurs en hexadécimal, chose très facile, sinon voici des explications de la conversion binare/hexa en vidéo ici
Ce qui donne E1 pour les LOW Byte Fuses et D9 pour les HIGH Byte Fuses.

On peut aussi utiliser une calculatrice pour AVR comme celle-ci http://www.engbedded.com/fusecalc/

Réalisation du montage sur plaquette d'essai et Programmation du µC :

Si besoin donc, les bits fusibles doivent recevoir les valeurs 0xe1 en LOW Byte et 0xd9 en HIGH Byte.
On fait cela avec la commande suivante :

sudo avrdude -p atmega8 -P /dev/parport0 -c stk200 -Uhfuse:w:0xd9 :m -Ulfuse:w:0xe1:m


Pour programmer le fichier HEX dans la mémoire flash : (plus d'info ici)

sudo avrdude -p atmega8 -P /dev/parport0 -c stk200 -Uflash:w:/chemin vers le fichier/Stargate.hex

Dés la fin le la programmation, l'animation commence :


Réalisation du typon pour le circuit imprimé grâce à kicad :

J'ai routé le circuit pour m'amuser car je n'ai pas l'intention de le réaliser, mais je vous laisse le projet kicad [1] et la vue 3D.
Stargate-Serigr_Cmp.png Stargate-Dessous.png
AVR-Stargate3D03.png
AVR-Stargate3D01.png AVR-Stargate3D04.png

Note

[1] en annexe

Commentaires

1. Le mardi, 7 juin 2011, 21:07 par JJL

Si tu veux travailler un peu ton C, je te propose ça

byte c = 0;
while (1)
{
   while (c < 0x7F)
   {
      c = (c << 1) | 1;
      PORTD = c;
      delay (1000);
   }
   delay (7000);
   PORTD = c = 0;
   delay (3000);
}

Si je ne me suis pas gouré, tu devrais avoir le même résultat mais en moins de lignes ;)

2. Le mardi, 7 juin 2011, 22:45 par MaKoTo

Merci, j'essaierais dés que possible !
Mais ça fait quoi ligne par ligne ?
Je ne connais pas ces fonctions et sans commentaires ni algorithme, je suis pris au dépourvu ^^;

3. Le mercredi, 8 juin 2011, 01:45 par JJL

Voici quelques explications complémentaires.

// on déclare une variable c d'1 octet (8 bits)
// byte c'est quand on programme un arduino, désolé
uint8 c = 0;
while (1)
{
  // tant que c est inférieur à 0x7F
  // 7F en hexa, c'est 0111 1111 en binaire
  while (c < 0x7F)
  {
     // la ça devient amusant.
     // << est un opérateur de décalage à gauche
     // c << 1 décale à gauche c de 1 bit
     // par exemple si c vaut 0b00000001
     // (c<<1) vaudra 0b00000010
     // | fait un ou binaire (bit à bit)
     // soit: 0|0 == 0, 0|1 == 1, 1|1 == 1
     // donc dans notre exemple (c<<1)|1 vaut 0b00000011
     c = (c << 1) | 1;
     // donc à chaque passage dans la boucle,
     // on "pousse" la valeur de c à gauche avec un 1
     // si tu as du mal à voir comment ça marche, déroule la boucle à la main
     PORTD = c;
     // la j'ai eu la flemme d'écrire _delay_ms :)
     delay (1000);
  }
  delay (7000);
  // affectation en ligne, équivalent à c=0; PORTD=c;
  PORTD = c = 0;
  delay (3000);
}

Quand on commence à jouer en c sur des µC, il devient rapidement utile de bien maitriser les opérations sur les bits. Ça demande une phase d'apprentissage mais après c'est un vrai bonheur :)

4. Le mercredi, 8 juin 2011, 21:06 par MaKoTo

Merci pour ces explications très claires !
Effectivement, en faisant appel à des fonctions logiques, cette solution est bien plus simple.

5. Le vendredi, 10 juin 2011, 20:51 par Vince

Je ne suis pas électronicien, mais je le trouve très bizarre ce montage : à quoi te servent tes 7 résistances ?

Si tu as du 0V en sortie de ton µC, alors pas de tension aux bornes de la LED, pas d'allumage, et la résistance dissipe tout le le 5V de l'alim. Si au contraire tu as 5V en sortie de l'µC, pas de tension aux bornes de la résistance, et tout le courant en sortie de l'µC passe direct dans la LED sans résistance.

C'est normal ? Pourquoi ne pas utiliser la sortie de ton µC avec une résistance et une LED montées en série ?

6. Le samedi, 11 juin 2011, 12:02 par MaKoTo

Bien vu !
Une grossière erreur dont je cherche encore la raison, tellement la/ta bonne solution est évidente, et que je l'avais déjà mise en œuvre sur l'ancien montage, celui à base de registres à décalage…
Ça fait plaisir de voir qu'au moins une personne ait pris la peine de contrôler le schéma ^^
C'est corrigé…
Merci !

7. Le lundi, 13 juin 2011, 23:40 par Toons

Hello,

Petite explication sur les resistances. La resistance est en serie uniquement dans le cas ou il n'y a qu'une led a alimenter. En effet le courant max que peux sortir une sortie du uC est de 20mA. en mettant la resistance entre Vcc et le port utilisé permet de tirer le courant directement de l'alim et non du uC donc permet d'alimenter les 3 leds. donc le schema definitif est serie lorsqu'il n'y a que 1 led et resistance entre vcc et le port sur la sortie ou il y 3 leds.

@++

Ajouter un commentaire

Les commentaires peuvent être formatés en utilisant une syntaxe wiki simplifiée.

Fil des commentaires de ce billet