Avant l'interruption :
Après la génération de l'interruption :
Cas pratique :
Nous souhaitons implémenter un programme contrôlant une LED via un bouton. Deux solutions sont possibles :
- Méthode de sondage : Vérifier périodiquement le niveau du bouton. Lorsqu'il est appuyé, activer la LED (détail non développé).
- Méthode d'interruption : Configurer le bouton comme source d'interruption externe. Lorsqu'un appui est détecté, exécuter le service d'interruption (ISR) pour allumer la LED.
I. Initialisation des interruptions
1. Configuration des sources d'interruption
Nous utilisons un bouton comme source d'interruption externe. Les broches GPIO correspondantes doivent être configurées pour déclencher une interruption lors d'un changement de niveau.
Objectif : allumer la LED à l'appui, l'éteindre à la relâche (configuration pour les deux flancs). Alternative : inversion du niveau de la LED à chaque interruption (flanc unique).
D'après le schéma électronique, le bouton est connecté à une résistance de pull-up. En position normale, le niveau est haut. À l'appui, le niveau devient bas. Les broches S2-S5 correspondent à GPF0, GPF2, GPG3, GPG11. Les LED D10-D12 sont connectées à GPF4, GPF5, GPF6.
Nous allons configurer S2, S3, S4 pour contrôler respectivement D10, D11, D12, et S5 pour contrôler les trois LED simultanément.
Code de configuraton GPIO :
// Configuration des broches GPIO pour les interruptions
GPIO_CONFIG &= ~((0x03 << 0) | (0x03 << 4)); // Réinitialiser EINT0 et EINT2
GPIO_CONFIG |= ((0x02 << 0) | (0x02 << 4)); // Activer S2 et S3 comme interruptions
GPIO_CONFIG &= ~((0x03 << 6) | (0x03 << 22)); // Réinitialiser EINT6 et EINT22
GPIO_CONFIG |= ((0x02 << 6) | (0x02 << 22)); // Activer S4 et S5 comme interruptions
// Configuration du type de déclenchement (flancs montant/ descendant)
EXTI_CTRL0 |= (0x07 << 0) | (0x07 << 8); // S2 et S3
EXTI_CTRL1 |= (0x07 << 12); // S4
EXTI_CTRL2 |= (0x07 << 12); // S5
// Activation des canaux d'interruption externes
EXTI_MASK &= ~(0x01 << 11) & ~(0x01 << 19); // Activer les canaux 11 et 19
// Gestion du registre de pendaison des interruptions externes
EXTI_PENDING = 0x00; // Réinitialisation du registre
2. Configuration du contrôleur d'interruption
Le contrôleur d'interruption comporte plusieurs registres clés :
- SRCPND : Indique les sources d'interruption actives
- INTMSK : Masque les interruptions (défaut : toutes masquées)
- INTMOD : Définit le type de mode (FIQ ou IRQ)
- INTPND : Affiche l'interruption en cours avec la plus haute priorité
- INTOFFSET : Indique le numéro de l'interruption active
Code de configuration du contrôleur :
void init_interrupt_controller() {
// Désactiver le masquage des interruptions nécessaires
INT_MASK &= ~(0x01 << 0) & ~(0x01 << 2) & ~(0x01 << 5);
// Le mode par défaut est IRQ, pas besoin de configuration
}
3. Interrupteur global
Le bit 7 du registre CPSR contrôle l'activation globale des interruptions. Il doit être mis à 0 pour permettre les interruptions.
// Désactiver le bit I (masque global des interruptions)
unsigned int cpsr = 0;
asm("mrs %0, cpsr" : "=r"(cpsr));
cpsr &= ~(1 << 7);
asm("msr cpsr, %0" : : "r"(cpsr));
II. Conception du service d'interruption
Une fois l'initialisation terminée, le programme principal (main) sera exécuté. Lorsqu'une interruption se produit, le processeur sautera à l'adresse 0x18 (vecteur IRQ).
Structure du code assembleur :
.text
.global _start
_start:
b reset // Vecteur 0 : réinitialisation
ldr pc, und_addr // Vecteur 4 : exception undefined
ldr pc, swi_addr // Vecteur 8 : interruption SWI
b halt // Vecteur 0x0c : erreur de précharge
b halt // Vecteur 0x10 : erreur de données
b halt // Vecteur 0x14 : réservé
ldr pc, irq_addr // Vecteur 0x18 : interruption IRQ
b halt // Vecteur 0x1c : FIQ
und_addr:
.word do_und
swi_addr:
.word do_swi
irq_addr:
.word do_irq
reset:
// Initialisations diverses
// Désactiver le bit I du CPSR
bic r0, r0, #0x80
msr cpsr, r0
ldr pc, =main
halt:
b halt
Service d'interruption (IRQ) :
do_irq:
ldr sp, =0x33d00000
sub lr, lr, #4
stmdb sp!, {r0-r12, lr}
bl handle_irq
ldmia sp!, {r0-r12, pc}^
Gestion de l'interruption :
void handle_irq(int interrupt_num) {
unsigned int pending = EXTI_PENDING;
unsigned int gpf_data = GPF_DATA;
unsigned int gpg_data = GPG_DATA;
switch(interrupt_num) {
case 0: // Bouton S2 contrôlant D12
if (gpf_data & (1 << 0)) {
GPF_DATA |= (1 << 6); // Éteindre
} else {
GPF_DATA &= ~(1 << 6); // Allumer
}
break;
case 2: // Bouton S3 contrôlant D11
if (gpf_data & (1 << 2)) {
GPF_DATA |= (1 << 5); // Éteindre
} else {
GPF_DATA &= ~(1 << 5); // Allumer
}
break;
case 11: // Bouton S4 contrôlant D10
if (gpg_data & (1 << 3)) {
GPF_DATA |= (1 << 4); // Éteindre
} else {
GPF_DATA &= ~(1 << 4); // Allumer
}
break;
case 19: // Bouton S5 contrôlant toutes les LED
if (gpg_data & (1 << 11)) {
// Éteindre toutes les LED
GPF_DATA |= (1 << 4) | (1 << 5) | (1 << 6);
} else {
// Allumer toutes les LED
GPF_DATA &= ~((1 << 4) | (1 << 5) | (1 << 6));
}
break;
}
EXTI_PENDING = pending; // Réinitialiser le registre
}
Résultat du test :