Programmation AVR, premiers pas, partie 1

Je programme occasionnellement sur avr (comprendre autre qu’arduino quoi), mais comme c’est très espacé dans le temps, à chaque fois j’ai l’impression de reprendre depuis le début. Du coups, je me suis dit que ça pourrais être bien de faire un petit papier là-dessus, en expliquant un peu les points qui m’ont posé problème à un moment ou un autre. Je ne suis pas un très bon programmeur, donc il y aura peut être des erreurs ou imprécisions, n’hésitez pas à m’en faire part dans les commentaires si c’était le cas :)

On va commencer par un truc simple  : un ATtiny85, une LED, une résistance, et on veut faire clignoter notre led :). Déjà, premier truc à savoir, c’est qu’il y a plein de façons différentes de le faire, alors on va commencer par du « haut niveau », et on ira voir un peu plus loin ensuite.

attiny85

Brochage de l’attiny85

Le schéma nous indique les différents ports E/S. On constate que plusieurs fonctionnalités leur sont attribués, le choix des pins utilisés se fera en fonction de ces fonctionnalités. Dans notre exemple, on peut utiliser n’importe quelle E/S, mais si par exemple on devais gérer une interruption matérielle, elle se ferait obligatoirement sur les broches PB0, PB1 ou PB2 (PCINT0, PCINT1 et PCIN2T).
Pour notre exemple, on va choisir d’utiliser la pin 5 de l’AtTiny85, nommée PB0. Voici la structure de notre programme

#include <avr/io.h>

int main(void)
{
//initialisation des entrées/sorties

  for(;;) //la boucle principale de notre programme
  {
    //on allume la led
    //on attend quelques millisecondes
    //on eteind la led
    //on attend quelques millisecondes
  }
}

Voilà, la structure du programme ne devrait pas poser de problème particulier à quelqu’un qui a déjà vu au moins une fois dans sa vie un programme en C. La suite en revanche peut en dérouter plus d’un, si vous n’êtes pas habitués à ce genre de syntaxes. Petites explications :
Afin de configurer une broche en sortie, il faut affecter la bonne valeur dans le bon registre ;) Le registre configurant la « direction » des données s’appelle DDRB, pour Port B Data Direction Register.

Le registre DDRB

Le registre DDRB

Par défaut, la valeur des bits de ce registre est à 0, ce qui signifie qu’il est accessible en lecture. Pour le passer en écriture, il suffit de mettre à 1 le bit correspondant. Vous pouvez le faire simplement en tapant :

DDRB = 0x01;//configure le port PB0 en sortie

Mais vous pouvez également tomber sur une syntaxe différente qui peut paraître bizarre au premier abord:

DDRB = 1<<PB0;

Celà veut simplement dire que l’on décale le 1 de x vers la gauche, x étant la valeur de PB0 dans mon exemple.
Imaginons que l’on veuille configurer le port PB5 en sortie, il faudrait selon la datasheet que DDRB = 0x00100000. Nous partons donc d’un DDRB qui vaut 0x00000000.On lui fait subir un premier décalage du bit 1 vers la gauche, on a maintenant DDRB = 0x00000001   (équivalent à 1<<0)
On recommence le décalage et on obtiens DDRB=0x00000010 (équivalent à 1<<1)

On recommence le décalage et on obtiens DDRB=0x00100000 (équivalent à 1<<5), on a donc bien réglé notre port PB5 en tant que sortie.

On peut donc maintenant mettre notre programme à jour, on obtiens donc :

#include <avr/io.h>

int main(void)
{
//initialisation des entrées/sorties
DDRB |= 1<<PB0;
  for(;;) //la boucle principale de notre programme
  {
    //on allume la led
    //on attend quelques millisecondes
    //on eteind la led
    //on attend quelques millisecondes
  }
}

Tant qu’on est dans les calculs binaires, il y a d’autres syntaxes que vous serez amené à croiser, par exemple :

GIMSK |= (1 << PCIE);

Il s’agit là de l’utilisation d’un masque, permettant de ne modifier que le bit qui nous intéresse (via l’utilisation d’un OU binaire). Les opérateurs binaires permettent d’améliorer grandement les performances d’un code, vous trouverez de plus amples explications sur ce sujet ici

Une fois que notre pin à été correctement configurée, il suffit de passer le bit correspondant à 1 pour la faire basculer à l’état 1, et inversement. Pour se faire, il faut écrire le bit correspondant à notre pin dans le registre PORTB. Là encore, c’est la datasheet qui nous aide, en nous fournissant le tableau de correspondances :
PORTB

On peut maintenant compléter notre code :

#include <avr/io.h>

int main(void)
{
//initialisation des entrées/sorties
DDRB |= 1<<PB0;
  for(;;) //la boucle principale de notre programme
  {
    PORTB |= (1<<PB0);//on allume la led
    //on attend quelques millisecondes
    PORTB &= ~(1 << PB0);//on eteind la led
    //on attend quelques millisecondes
  }
}

Voilà, le plus gros est fait, il ne reste plus qu’à gérer les délais. Pour ce premier tutoriel, on ne va pas s’embêter, on utilisera la fonction prévue pour, fournie avec util/delay.h : _delay_ms()
Comme son nom l’indique elle fait attendre le programme pendant un nombre donné de millisecondes. Dans une prochaine version, nous verrons comment utiliser un timer à la place. On dispose maintenant de tout le nécessaire pour terminer notre programme :

#include <avr/io.h>
#include <util/delay.h>

int main(void)
{
//initialisation des entrées/sorties
DDRB |= 1<<PB0;
  for(;;) //la boucle principale de notre programme
  {
    PORTB |= (1<<PB0);//on allume la led
    _delay_ms(500);//on attend quelques millisecondes
    PORTB &= ~(1 << PB0);//on eteind la led
    _delay_ms(500);//on attend quelques millisecondes
  }
}

Voilà, pour utiliser notre programme, il reste encore à le compiler et à l’uploader sur le microcontrolleur. Je ne vais pas détailler cette section, elle est déjà largement documentée sur le net (et facilement compréhensible à mon avis). En revanche, je vais vous laisser un fichier Makefile bien pratique, qui permet de gérer la compilation ET l’upload, ainsi que tout un tas d’autres choses, bref, un vrai couteau suisse. (Le Makefile n’est pas de moi, mais je n’arrive pas à en retrouver la source, désolé)

Mots-clés : , , ,

Posted 3 janvier 2013 by nerick in category General

7 thoughts on “Programmation AVR, premiers pas, partie 1

  1. texane

    Des petites suggestions, si ca aide dans l aspect didactique.

    Tu ferais mieux d utiliser PB0 partout dans le code, 0 apparait
    encore.

    Sinon, utiliser DDRB |= 1 << PB0 est plus general, et permet de
    reprendre le code peu importe l etat precedent de DDRB.

  2. Anatole

    Et pour faire un feu tricolore le code ci-dessous est-il correcte ?

    #include
    #include

    int main(void)
    {
    //initialisation des entrées/sorties
    DDRB |= 1<<PB0;
    DDRB |= 1<<PB1;
    DDRB |= 1<<PB2;
    for(;;) //la boucle principale de notre programme
    {
    PORTB |= (1<<PB0);//on allume la led
    _delay_ms(500);//on attend quelques millisecondes
    PORTB &= ~(1 << PB0);//on eteind la led
    _delay_ms(500);//on attend quelques millisecondes
    PORTB |= (1<<PB1);//on allume la led
    _delay_ms(500);//on attend quelques millisecondes
    PORTB &= ~(1 << PB1);//on eteind la led
    _delay_ms(500);//on attend quelques millisecondes
    PORTB |= (1<<PB2);//on allume la led
    _delay_ms(500);//on attend quelques millisecondes
    PORTB &= ~(1 << PB2);//on eteind la led
    _delay_ms(500);//on attend quelques millisecondes
    }
    }

  3. nerick (Post author)

    hummm ton feu va avoir un drôle de comportement, comme ça ;)
    Disons pour rester vague qu’il va être souvent éteins…. Comme ça ressemble beaucoup a une correction de TP, je ne t’en dis pas plus et te laisse chercher un peu, tu n’es pas bien loin dans tous les cas…

  4. Anatole

    Bonjour,

    Merci, pour la relecture, je n’avais pas anticipé le petit côté TP de mon post.

    Humm, mis à part les includes imcomplets, je ne vois pas d’autres choses.

    Pour les includes, j’ai l’impression que le texte est passé à la trappe avec l’editeur de ton blog. Ci-dessous une ligne de test avec et sans le > et le <.

    / mickey

    Bonne journée

  5. nerick (Post author)

    En fait, c’est un problème fonctionnel, plus que de programmation : tel que tu l’as écrit, tu allume une led 500ms, tu éteins tout pendant 500ms, et tu passe à la led suivante… Alors que sur un feu, tu allume la led suivante sans attendre après l’extinction de la précédente. Donc tu as juste des delay en trop…

  6. Anatole

    Oups, Les doigts sont souvent plus rapide que le cerveau avec le copier-coller et l’activation du cerveau à pasteriori n’est toujours gagnée ;).

Leave a Comment

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

*

Vous pouvez utiliser ces balises et attributs HTML : <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>