Club robotique de Sophia-Antipolis

Accueil > POBOTpedia > Programmation > Apprendre à coder > Les micro-contrôleurs > Trucs et astuces > Vecteurs d’interruption pour AVR

Vecteurs d’interruption pour AVR

programmation en C avec avr-gcc

dimanche 7 mars 2010, par Julien H.

Le principe d’interruption est rapidement nécessaire pour gérer des comportements non-linéaires. En effet concevoir un programme de robot (ou tout autre système électronique) avec seulement des séquences d’instructions et des boucles arrive rapidement à ses limites.

Il n’est pas non plus toujours nécessaire de recourir à un système d’opérations temps réel (même si nous avons traité ce sujet : RTOS sur AVR). On va donc présenter ici comment écrire un programme "réactif" qui gère des "événements". Dans le langage informatique, on parle d’interruptions car le programme principal (ou "main" en anglais/langage C) est interrompu de son exécution linéaire, pour aller faire quelque chose de spécial puis revenir suivre son cours normal.

Bien sûr il faut préciser au microcontrôleur qu’on souhaite utiliser ces interruptions, car ce n’est pas obligatoire.

Il y a des interruptions pour beaucoup d’événements donc un coup d’oeil sur le tableau fourni avec la documentation d’avr-libc vous donnera une meilleure idée : http://www.nongnu.org/avr-libc/user...

Matériel nécessaire

Pour que le résultat soit clairement visible, nous allons utiliser une interruption externe : on peut alors intervenir manuellement sur le code en modifiant la tension d’une patte du µC (i.e. changer son état logique). Nous en avions déjà parlé pour Arduino (Interruptions externes) mais sans entrer dans les détails du fonctionnement par registres).

 un microcontrôleur Atmel AVR et son programmateur (j’ai pris un ATtiny13 et une carte EasyAVR mise à disposition par le club à ses adhérents)
 deux leds connectées aux sorties numériques avec la résistance qui va bien
 un bouton connecté à une entrée particulière (voir la suite)

Préparation

Il faut tout d’abord s’assurer du fonctionnement normal du microcontrôleur (sans interruption) en faisant clignoter la led dans une boucle infinie de la fonction main, par exemple :


for( ; ;)

PORTB |= _BV(0) ;
_delay_ms(500) ;
PORTB &= (_BV(0)) ;
_delay_ms(250) ;

Cela permet aussi de s’assurer que la programmation s’effectue correctement.

Principe

Le principe des interruptions est de renseigner le microcontrôleur des événements dont on souhaite être informé, et de lui indiquer une fonction particulière pour chacun de ces événements.

Les informations de configuration se donnent via des modifications de registres (parfois appelés "masques" dans des cas précis). La fonction qui traite l’événement est spécifique à chaque interruption, et la bibliothèque "avr-libc" fournit des macros pour simplifier leur usage, à condition de bien savoir ce qu’on souhaite.

Même si tous les AVR utilisent le même principe, chacun est différent pour les registres, les valeurs et les codes des interruptions. Il faut donc vous munir de la documentation technique (datasheet) de votre microcontrôleur.

Ainsi pour le micro ATtiny13 que je souhaite utiliser pour une interruption externe, j’ai le choix :

 utiliser l’interruption externe "INT0" spécifique, offrant la possibilité de filtrer les événements (niveau bas, fronts)
 utiliser l’interruption externe "PCINT0..5" générique, qui gère tout changements de valeur sans filtre

Et bien la façon de les paramétrer n’est pas la même :

 dans les deux cas il faut mettre à jour le masque général des interruptions : GIMSK
 dans le cas de INT0 le paramétrage se fait via le registre MCUCR, sur deux bits pour tous les filtres
 dans le cas de PCINT0 le paramétrage se fait via le masque PCMSK, avec un seul bit (mais 1 par patte)

La fonction de gestion de l’interruption n’a pas un prototype classique, c’est-à-dire "type de retour" "nom de la fonction" "type des paramètres". Au lieu de celà, avr-libc propose une macro appelée "ISR" (nouveau nom, avant c’était SIGNAL) qui doit prendre en paramètre obligatoire le nom de l’interruption, selon une liste spécifique à la bibliothèque, comme "INT0_vect" ou "PCINT0_vect". D’autres paramètres sont possibles (voir la doc citée au début de l’article).

Code source


/**
* Programme de démonstration d’utilisation d’interruptions avec AVR-GCC
*
* - utilisation d’un ATtiny13
* - utilisation d’une interruption externe
*
* Ce programme fait clignoter en permanence la LED reliée à la pin PB0 du MCU
* et change l’état de celle qui est reliée à la pin PB2 lorsqu’on appuie sur
* le poussoir connecté sur la pin PB1 (qui correspond au signal INT0).
*/

#include "avr/io.h"
#include "avr/interrupt.h"
#include "util/delay.h"

/** Déclaration des fonctions **/

static void avr_init(void) ;

/** Gestion de l’interruption **/

/*
* Cette interruption est déclenchée dès que l’état de la pin PB1 passe
* de 1 à 0, ce qui se produit lorsque le poussoir est appuyé.
* Ce comportement est défini par les initialisations effectuées en début de
* programme (voir les commentaires plus loin)
*/

ISR(INT0_vect)

// on change l’état de la LED en testant son état actuel et en
// le modifiant en conséquence
if (bit_is_set(PORTB,2))
PORTB &= (_BV(2)) ;
else
PORTB |= _BV(2) ;

/* à noter que sauf erreur ça peut se faire plus simplement
avec un XOR comme ceci :
PORTB ^= _BV(2) ;
*/

/**
*
* MAIN
*
*/
int main(void)

// configuration du MCU
avr_init() ;

// Cette boucle sans fin fait clignoter la LED PB0 en permanence
for( ; ;)

PORTB |= _BV(0) ;
_delay_ms(500) ;
PORTB &= (_BV(0)) ;
_delay_ms(250) ;

// ce return n’est pas vraiment utile, puisqu’on n’y arrivera jamais
return(0) ;

static void avr_init(void)

// on inhibe les interruptions le temps de mettre en place la
// configuration (pas obligatoire, mais sage précaution pour
// éviter les départs en vrille lorsque la mise en place d’une
// configuration est susceptible de déclencher une interruption
// alors qu’on n’y est pas encore préparé)
cli() ;

// configuration des entrées-sorties

DDRB |= _BV(0) ; // pin PB0 en sortie
DDRB |= _BV(2) ; // pin PB2 en sortie

DDRB &= (_BV(1)) ; // pin PB1 en entrée

// configuration de l’interruption utilisée

// Activer l’interruption externe "INT0"
GIMSK |= _BV(INT0) ;

// Définir un déclenchement sur front descendant
// - lire la datasheet
// - trouver le tableau qui dit que ISC00 = 0 et ISC01 = 1 pour le trigger falling-edge

MCUCR |= _BV(ISC01) ;

// on autorise les interruptions à partir de maintanant
sei() ;

return ;

Voici le projet permettant de reproduire ce test (avec INT0) :

Programme de test des ISR
pour WinAVR ou AVR-Gcc

Test

Au passage, voici le look du nouveau programmateur des EasyAVR :

Programmation de l’ATtiny13

Et le test est concluant : la led 2 change de niveau quand on relâche le bouton 1 (car front descendant ou "falling-edge").

Un message, un commentaire ?

modération a priori

Attention, votre message n’apparaîtra qu’après avoir été relu et approuvé.

Qui êtes-vous ?

Pour afficher votre trombine avec votre message, enregistrez-la d’abord sur gravatar.com (gratuit et indolore) et n’oubliez pas d’indiquer votre adresse e-mail ici.

Ajoutez votre commentaire ici

Ce champ accepte les raccourcis SPIP {{gras}} {italique} -*liste [texte->url] <quote> <code> et le code HTML <q> <del> <ins>. Pour créer des paragraphes, laissez simplement des lignes vides.