S3C2440 en mode bare metal - Gestion des interruptions externes (Partie 4 : IRQ et interruptions externes)

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 :

  1. Méthode de sondage : Vérifier périodiquement le niveau du bouton. Lorsqu'il est appuyé, activer la LED (détail non développé).
  2. 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 :

Étiquettes: microcontrôleur S3C2440 ARM interruptions externes configuration GPIO Système embarqué

Publié le 16 août à 08h21