Aller au contenu principal

Laboratoire 2 : Interruptions, Timers

Chapitres utiles du Datasheet ATmega324

  • 1. Configuration des broches
    • Section 1.1 - Page 2
  • 7. Cœur du processeur AVR
    • Section 7.3 - Page 11
    • Section 7.7 - Page 16
  • 12. Interruptions
    • Tableau 12-1 - Page 61
  • 13. Interruptions externes
    • Section 13.1 - Page 67
    • Sections 13.2.4-13.2.9 - Page 69
  • 16. Minuteries/Compteurs 16 bits (Timer/Counter1 et Timer/Counter3) avec PWM
    • Sections 16.1-16.3 - Page 111
    • Section 16.5 - Page 117
    • Section 16.7 - Page 120
    • Sections 16.9.1-16.9.2 - Page 123
    • Section 16.11 - Page 132
attention

Les chapitres référencés proviennent du Datasheet ATmega324, un document disponible sur le bureau des ordinateurs du laboratoire.
L'ordre des chapitres peut varier dans d'autres versions de la fiche technique !


Objectif

Ce laboratoire vise à vous familiariser avec les interruptions matérielles et les timers sur le microcontrôleur ATmega324.
Dans cette séance, nous utiliserons les timers uniquement pour le comptage, sans générer de signaux PWM.
La fonctionnalité PWM sera explorée dans le prochain laboratoire.


1. Interruptions

Une interruption matérielle est un signal synchrone ou asynchrone provenant d'un périphérique, indiquant qu'un événement doit être traité par le processeur.
Lorsqu'une interruption est déclenchée, elle suspend l'exécution normale du programme et déclenche l'exécution d'une Routine de Service d'Interruption (ISR).

Pourquoi utiliser des interruptions ?

Les interruptions éliminent le besoin de boucles d’interrogation (polling) où le processeur vérifie constamment un événement sur un périphérique.
Grâce aux interruptions :

  • Les périphériques peuvent notifier le processeur uniquement en cas de besoin.
  • Le processeur est libre d’exécuter d’autres tâches jusqu’à ce qu’un événement se produise.
  • Le programme s'exécute de manière plus efficace.

Gestion des interruptions

Avant d'exécuter une ISR, le processeur sauvegarde son état actuel :

  • Compteur de programme (PC)
  • Registres d’état
  • Variables affectées par l’exécution de l’ISR

Ces informations sont stockées en mémoire, généralement sur une pile (stack).
Une fois l’ISR terminée, l’état précédent est restauré et le programme principal reprend son exécution à l'endroit où il a été interrompu.


Table de Vecteurs d'Interruption (IVT)

Pour associer une interruption à une fonction ISR spécifique, le processeur utilise une Table de Vecteurs d'Interruption (IVT), représentée dans le schéma ci-dessous.

Chaque interruption est assignée à une adresse mémoire fixe où se trouve la fonction ISR correspondante.
Lorsqu'une interruption se produit, le processeur saute à l’adresse ISR correspondante dans l'IVT.

Priorité des interruptions :

  • Adresses les plus basses → Priorité la plus élevée
  • Les interruptions situées au début de l'IVT sont traitées en premier.

Table de Vecteurs d'Interruption pour l'ATmega324

N° VecteurAdresse ProgrammeSourceDéfinition de l'Interruption
10000RESETRéinitialisation externe, mise sous tension, Reset Brown-out, Reset Watchdog, et Reset JTAG AWR
20002INT0Demande d'interruption externe 0
30004INT1Demande d'interruption externe 1
40006INT2Demande d'interruption externe 2
50008PCINT0Demande d'interruption par changement d’état 0
6000APCINT1Demande d'interruption par changement d’état 1
7000CPCINT2Demande d'interruption par changement d’état 2
8000EPCINT3Demande d'interruption par changement d’état 3
90010WDTInterruption de temporisation Watchdog
100012TIMER2_COMPAComparaison A du Timer/Counter2
110014TIMER2_COMPBComparaison B du Timer/Counter2
120016TIMER2_OVFDépassement du Timer/Counter2
130018TIMER1_CAPTCapture d’événement du Timer/Counter1
14001ATIMER1_COMPAComparaison A du Timer/Counter1
15001CTIMER1_COMPBComparaison B du Timer/Counter1
16001ETIMER1_OVFDépassement du Timer/Counter1
170020TIMER0_COMPAComparaison A du Timer/Counter0
180022TIMER0_COMPBComparaison B du Timer/Counter0
190024TIMER0_OVFDépassement du Timer/Counter0
200026SPI_STCTransfert série SPI terminé
210028USART0_RXRéception USART0 terminée
22002AUSART0_UDRERegistre de données USART0 vide
23002CUSART0_TXTransmission USART0 terminée
24002EANALOG_COMPComparateur analogique
250030ADCConversion ADC terminée
260032EE_READYEEPROM prête
270034TWIInterface série à deux fils (I2C)
280036SPM_READYMémoire programme prête
290038USART1_RXRéception USART1 terminée
30003AUSART1_UDRERegistre de données USART1 vide
31003CUSART1_TXTransmission USART1 terminée
32003ETIMER3_CAPTCapture d’événement du Timer/Counter3
330040TIMER3_COMPAComparaison A du Timer/Counter3
340042TIMER3_COMPBComparaison B du Timer/Counter3
350044TIMER3_OVFDépassement du Timer/Counter3

D'après le tableau ci-dessus, en plus des interruptions des composants internes (timers, interfaces série, convertisseurs analogique-numérique), il existe également des interruptions des périphériques externes :
INT0-INT2
PCINT0-PCINT3

La différence entre ces deux types d'interruptions externes réside dans leurs capacités et leur niveau de granularité.

Interruptions INTn

  • Les signaux des interruptions INTn proviennent de :
    • Port D (broches 2, 3)
    • Port B (broche 2)
  • Elles peuvent déclencher une interruption :
    • Sur un front montant ou un front descendant
    • Ou sur un niveau bas (0), selon la configuration.

Interruptions PCINTn

  • Les interruptions PCINTn se déclenchent sur les deux fronts (d'où le nom Pin Change INTerrupt).
  • 8 broches sont multiplexées sur une seule interruption.
  • Bien que les signaux PCINT puissent être activés individuellement, la broche exacte ayant déclenché l'interruption doit être déterminée en vérifiant le registre PINn.
  • Les changements simultanés de plusieurs broches ne peuvent pas être distingués.
attention

Pour les interruptions par changement d’état, ne confondez pas le vecteur d'interruption (ex. : PCINT0) avec l'interruption réelle (ex. : PCINT0 (PA0)).
Le mapping entre les vecteurs et les interruptions se trouve dans la Section 30, Page 555 du datasheet.

Broches d'Interruption Externe sur l'ATmega324

Broches d'Interruption Externe sur l'ATmega324

astuce

Lorsqu'une interruption se produit, en plus de sauvegarder l'état, le processeur désactive les interruptions.
À la fin de la routine d'interruption, elles sont réactivées.
Les interruptions peuvent également être réactivées manuellement à l'intérieur du gestionnaire (ex. : si nous sommes dans un gestionnaire recevant une trame via une interface série et souhaitons activer une interruption de timer).


1.1. Utilisation des Interruptions

Pour activer une interruption, suivez ces étapes :

1️⃣ Activer le mécanisme d'interruption globale

La gestion des interruptions doit être activée explicitement (bit I - Global Interrupt Enable dans le registre SREG).
Pour activer et désactiver ce bit, utilisez les fonctions suivantes :

// Activer les interruptions
sei();

// Désactiver les interruptions
cli();

2️⃣ Configurer le périphérique qui générera les interruptions

Par exemple, INT0, INT1 et INT2 sont configurés via le registre EICRA
(External Interrupt Control Register A, section 13.2.1, page 67 du datasheet).

Pour les interruptions par changement d’état (PCINTn), utilisez le registre PCICR.

Registre EICRA

Registre EICRA

Modes de Déclenchement des Interruptions

Modes EICRA

Exemples de Configuration

// Interruption externe : Configurer INT0 pour se déclencher sur tout changement logique
EICRA |= (1 << ISC00);

// Interruption par changement d'état : Activer l’interruption sur changement d'état, configurer PCIE1 pour analyser PCMSK1
PCICR |= (1 << PCIE1);
astuce

Une interruption externe peut être configurée pour se déclencher sur :

  • Un front montant
  • Un front descendant
  • Tout changement d’état

📌 Voir Table 13-1 dans le datasheet pour plus de détails.

📌 Les interruptions par changement d’état se déclenchent sur n'importe quel changement de niveau, il faut donc vérifier explicitement la valeur du signal dans le gestionnaire d’interruption.


3️⃣ Implémenter la Routine de Service d'Interruption (ISR)

Les gestionnaires d'interruptions doivent être mappés à des adresses mémoire fixes dans la table de vecteurs d’interruption.
Utilisez la macro ISR() pour les définir, en passant le type d'interruption approprié.

Exemple : Gestion des interruptions INT0 et PCINT1

ISR(INT0_vect) {
// Code pour l'interruption externe
}

ISR(PCINT1_vect) {
// Code pour l'interruption par changement d’état

if ((PINB & (1 << PB1)) == 0) {
// Interruption déclenchée par la broche PB1
}
}

Activation des Interruptions et Test du Programme

Pour activer les interruptions externes, configurez le registre de masque des interruptions externes (EIMSK) :

  • Les bits INT2:0 contrôlent l'activation des interruptions externes (INT0-INT2).

Pour les interruptions par changement d’état, configurez le registre correspondant (par ex., PCMSK1 pour les interruptions sur des broches spécifiques).

// Activer l'interruption externe INT0
EIMSK |= (1 << INT0);

// Activer l'interruption par changement d’état PCINT9 (PB1)
PCMSK1 |= (1 << PCINT9);

// Activer les interruptions globales
sei();

📌 Registres supplémentaires pour la gestion des interruptions

Pour une description complète de ces registres, consultez le datasheet
(Interruptions, Interruptions Externes).

Registre d'État (SREG)

  • Stocke les indicateurs d'état définis par l'ALU.
  • Contient le bit I, qui active/désactive les interruptions globales.
  • N'est pas sauvegardé lorsqu'une interruption se produit.
  • Décrit dans le chapitre Cœur du processeur AVR.

📌 Registre SREG Registre SREG


Registre de Contrôle MCU (MCUCR)

  • Bit IVSEL : Définit l'emplacement de la table des vecteurs d'interruption
    (0 = Début de la mémoire Flash, 1 = Section Boot Loader).
  • Bit IVCE : Permet d'écrire dans le bit IVSEL.

📌 Registre MCUCR Registre MCUCR


Registre de Masque des Interruptions Externes (EIMSK)

  • Les bits INT2:0 contrôlent l'activation des interruptions externes.
  • Si INT2:0 ET le bit I dans SREG sont à 1,
    les interruptions externes sont activées sur la broche correspondante.

1.2 Utilisation des Interruptions dans avr-gcc

La bibliothèque avr-libc fournit l'interface <avr/interrupt.h>
pour définir des routines de service d'interruption (ISR).

Chaque microcontrôleur possède une table de vecteurs spécifique,
déclarée dans le fichier IO header correspondant
(pour ATMega324, voir iom324.h).

Vecteurs d'Interruption Courants

InterruptionVecteurDescription
TIMER1_COMPATIMER1_COMPA_vectComparaison sur Timer 1, Seuil A
TIMER1_COMPBTIMER1_COMPB_vectComparaison sur Timer 1, Seuil B
TIMER1_OVFTIMER1_OVF_vectDépassement du Timer 1
PCINT1PCINT1_vectInterruption par changement d'état sur Port B
.........

Définition d'une ISR

Utilisez la macro ISR() pour créer un gestionnaire d'interruption :

#include <avr/interrupt.h>

ISR(INT0_vect) {
// Code de gestion de l'interruption
}

Règles de Gestion des Interruptions

Pas de valeur de retour : Le processeur reprend l'exécution là où il s'était arrêté
Minimiser le temps d'exécution : L'ISR bloque l'exécution du programme principal
Manipuler les variables partagées avec précaution :

  • Utiliser volatile pour les variables partagées, afin d'éviter l'optimisation du compilateur.
  • Faire attention aux conditions de concurrence lors de la modification de variables multi-octets (16/32 bits).

📌 Les interruptions peuvent être déclarées avec des options spécifiques

#include <avr/interrupt.h>

ISR(vector, flag) {
// Code de gestion de l'interruption
}

La macro ISR() :

  • Définit le gestionnaire pour un périphérique spécifique.
  • Sauvegarde le registre SREG avant l'exécution.
  • Appelle l'instruction reti à la fin.

Options ISR

OptionDescription
ISR_BLOCKComportement par défaut : Les interruptions globales sont désactivées dans l'ISR.
ISR_NOBLOCKPermet les interruptions imbriquées (utile pour la gestion de priorités).
ISR_NAKEDSupprime le prologue/épilogue (pas de sauvegarde SREG, pas d'appel reti).
ISR_ALIASOFRend une ISR alias d'une autre ISR.

Exemple : Alias d'Interruptions

#include <avr/interrupt.h>

ISR(INT0_vect) {
// Gestionnaire d'interruption pour INT0
}

ISR(INT1_vect, ISR_ALIASOF(INT0_vect)) {
// INT1 utilise le même gestionnaire que INT0
}

Fonctions Utilitaires Supplémentaires

FonctionDescription
sei()Active les interruptions globales (I bit dans SREG = 1).
cli()Désactive les interruptions globales (I bit dans SREG = 0).
reti()Retourne depuis une ISR, réactivant les interruptions.

attention

⚠ Une interruption active sans ISR provoquera un Reset système !
Si une interruption activée n'a pas de gestionnaire, le système sera réinitialisé.

2. Minuterie (Timer)

2.1. Fonctionnement d'un Timer

Un Timer/Compteur mesure des intervalles de temps fixes et peut déclencher des interruptions lorsque l'intervalle mesuré expire.
Une fois initialisé, un timer fonctionne indépendamment du CPU, éliminant ainsi le besoin de boucles d’attente.

Composants de Base d’un Timer sur l’ATmega324

  1. Registre Compteur (TCNT) - Mesure les intervalles de temps et s’incrémente à une fréquence définie.
  2. Prédiviseur (Prescaler) - Divise la fréquence d’horloge en fonction des besoins de l’application.
  3. Registre de Comparaison (OCRn) - Stocke une valeur seuil. Si TCNT atteint cette valeur, une interruption est générée.

📌 Schéma Fonctionnel du Timer (ATmega324)
Schéma Fonctionnel du Timer

L’ATmega324 dispose de trois timers :

  • Deux timers 8 bits (Timer0 & Timer2)
  • Un timer 16 bits (Timer1)

Les timers peuvent aussi fonctionner en mode PWM pour générer des signaux de commande de tension variable, qui seront étudiés lors du prochain laboratoire.


2.2. Modes de Fonctionnement du Timer

Les timers peuvent fonctionner en différents modes, chacun définissant :

  • La plage de comptage.
  • Le comportement du comptage (incrémentation uniquement ou incrémentation/décrémentation).
  • Quand le compteur se réinitialise.

📌 Modes utilisés dans ce laboratoire :

ModeDescriptionComportement du CompteurPropriétés
NormalDémarre à 0 Compte jusqu’à 0xFFFFMode NormalFréquence fixe basée sur l’horloge & le prédiviseur
CTC (Clear Timer on Compare)Démarre à 0 Compte jusqu’à OCRnA Se réinitialise à la valeur seuilMode CTCFréquence variable basée sur l’horloge, le prédiviseur & la valeur de comparaison

Termes Clés :

  • BOTTOM : Valeur minimale de comptage (0).
  • TOP : Valeur maximale de comptage.
  • MAX : Valeur la plus haute possible (255 pour les timers 8 bits, 65535 pour les timers 16 bits).
    • TOP = MAX en Mode Normal.

2.3. Registres du Timer

TimerRegistresDescription
Timer0 (8 bits)TCNT0Registre du compteur (valeur courante du compteur)
TCCR0A, TCCR0BRegistres de contrôle (configuration du timer)
OCR0A, OCR0BRegistres de comparaison (définition des seuils d’interruption)
TIMSK0, TIFR0Registres pour activer/désactiver les interruptions & indicateurs d’état
Timer1 (16 bits)TCNT1H/LRegistre compteur 16 bits
TCCR1A, TCCR1B, TCCR1CRegistres de contrôle
OCR1AH/L, OCR1BH/LRegistres de comparaison 16 bits
TIMSK1, TIFR1Registres d’interruption & indicateurs d’état
ICR1H/LRegistre de Capture d’Entrée - stocke la valeur du compteur sur un événement externe
Timer2 (8 bits)Même registres que Timer0Timer2 prend en charge une horloge externe via TOSC1 & TOSC2
ASSR, GTCCRRegistres pour le fonctionnement asynchrone
astuce

Les interruptions doivent être activées globalement (bit I dans SREG doit être activé).
Utilisez sei(); pour activer les interruptions globales.


2.4. Configuration du Timer

Définition du Mode du Timer

Pour configurer un mode de timer :

  1. Définissez les bits WGM dans le registre TCCRnA correspondant.
  2. Définissez le seuil de comparaison.

Exemple : Configurer Timer0 en mode CTC, comptant jusqu'à 5

TCCR0A |= (1 << WGM01); // Mode CTC activé
OCR0A = 5; // Définition du seuil de comparaison

Configuration du Prédiviseur

Les valeurs du prédiviseur sont définies à l’aide des bits CSx dans TCCRnB.

Exemple : Définir le prédiviseur du Timer2 à 256

TCCR2B |= (1 << CS22) | (1 << CS21); // Prédiviseur de 256

Activation des Interruptions du Timer

Pour déclencher une interruption lors d’une comparaison, activez le bit correspondant dans TIMSKx.

Exemple : Activer l’interruption de Comparaison A pour Timer1

#include <avr/interrupt.h>

ISR(TIMER1_COMPA_vect) {
// Gestionnaire d'interruption pour la comparaison A du Timer1
}

void init_timer1() {
TIMSK1 |= (1 << OCIE1A); // Activer l’interruption Comparaison A du Timer1
}

int main() {
sei(); // Activer les interruptions globales
init_timer1(); // Initialiser Timer1
while (1) {
// Boucle principale
}
}

2.5. Gestion des Registres 16 bits

Certains registres (TCNT1, OCR1A/B, ICR1) sont 16 bits mais accessibles 8 bits à la fois.

  • Écrire le byte de poids fort en premier garantit des mises à jour atomiques.

Exemple : Écriture dans OCR1A (registre 16 bits)

OCR1AH = (seuil >> 8); // Écrire le byte de poids fort d’abord
OCR1AL = seuil; // Puis écrire le byte de poids faible
astuce

Cela est automatiquement géré par le compilateur en C lorsque les registres sans suffixe H ou L sont utilisés.


2.6. Calculateur de Timer

Pour configurer un timer :

  • Choisissez un prédiviseur.
  • Définissez une limite de comptage.
  • Calculez les valeurs en fonction de la fréquence désirée et de l’horloge système.

Formule pour la fréquence d’interruption (f_int) :

f_int = f_clk / (prédiviseur * (tc + 1))

Calcul de la valeur du Timer (tc) :

tc = f_clk / (prédiviseur * f_int) - 1

📌 Exemple : Générer une interruption à 1Hz (Horloge = 12MHz)

PrédiviseurLimite de Compteur (tc)Timer1 (16 bits)Timer0,2 (8 bits)Remarques
111,999,999❌ Impossible (Dépassement 16 bits)❌ Impossible (Dépassement 8 bits)Dépassement
81,499,999❌ Impossible❌ ImpossibleDépassement
64187,499❌ Impossible❌ ImpossibleDépassement
25646,874✅ Valide (Utiliser Timer1)❌ ImpossibleRequiert Timer1
102411,717❌ Impossible❌ ImpossibleValeur non exacte
astuce

Timer2 prend en charge des prédiviseurs de 32 et 128 (Voir ATmega324 Datasheet, Section 17.11.2, Page 160).


Outils de Calcul en Ligne

Si vous avez besoin de valeurs précises des registres, utilisez ces outils :

Tâche 1.1 : Implémentation de millis()

  • Implémentez une fonction similaire à millis() d'Arduino, qui retourne le temps écoulé depuis le démarrage ou le dernier reset du microcontrôleur (µC).
  • Configurez USART0 avec les mêmes paramètres que dans le laboratoire précédent.
  • Envoyez un message choisi au PC toutes les 1 seconde.

📌 Astuces :

  • La fréquence d'horloge du µC est de 12MHz.
  • Utilisez les formules de la section précédente pour calculer le prédiviseur et la valeur du registre de comparaison pour le timer.
  • Timer2 offre plus d’options de prédiviseurs, ce qui en fait un bon choix.

Tâche 1.2 : Anti-rebond des boutons

  • Une méthode de filtrage (debounce) est nécessaire pour détecter correctement les appuis courts sur un bouton.
  • Comment peut-on utiliser millis() pour éliminer les fausses lectures ?
  • Écrivez une fonction en pseudocode illustrant votre solution.

Tâche 2 : Réimplémentation de l'Exercice 3 du Laboratoire 0 en utilisant les Timers et Interruptions

  • Utilisez des timers et interruptions pour détecter les appuis sur les boutons et contrôler le clignotement des LEDs.

📌 Rappel (Lab 0, Exercice 3) :

  • BTN1 : Change la couleur de la LED RGB (Rouge → Vert → Bleu → Rouge).
  • BTN2 : Active/Désactive le clignotement de la LED.

Tâche 3 : Contrôle d’un Buzzer avec des Boutons

  • Utilisez les boutons PD6 et PB2 pour contrôler un buzzer.
  • Un bouton sélectionne la fréquence parmi trois valeurs (ex. 100Hz, 200Hz, 300Hz).
  • Le deuxième bouton doit être maintenu enfoncé pour que le buzzer émette un son.
  • Utilisez la LED RGB pour indiquer la fréquence sélectionnée.

Tâche 4 (BONUS) : Correction d’Erreur du Timer

  • La fonction millis() accumule une erreur au fil du temps en raison des imprécisions de division de la fréquence d’horloge.
  • Calcul :
    • Après combien de cycles du timer l'erreur dépasse-t-elle 1ms ?
  • Proposez une méthode pour réduire cette erreur.
  • Implémentez la méthode de correction proposée.