Les interruptions ne sont pas seulement un élément de base de notre vie quotidienne. Ils sont également essentiels pour faire fonctionner les systèmes informatiques aussi bien qu’eux, car ils permettent à un système de répondre immédiatement à un événement. Alors que sur les ordinateurs de bureau, ces interruptions sont moins importantes qu’à l’époque où nous devions encore définir manuellement l’IRQ pour un nouveau matériel à l’aide de commutateurs à bascule sur une carte ISA, les IRQ ainsi que les transferts DMA (accès direct à la mémoire) sont toujours ce qui fait un système semblent zippy à un utilisateur s’il est utilisé correctement.

Sur les systèmes à microcontrôleur comme le STM32, les interruptions sont encore plus importantes, car c’est ce qui permet à un microcontrôleur de répondre en temps réel dur à un événement (externe). Surtout dans quelque chose comme un processus industriel ou dans une voiture moderne, il existe de nombreux événements qui ne peuvent tout simplement pas être traités chaque fois que le processeur se met à interroger un registre. Au-delà de cela, les interruptions ainsi que les gestionnaires d’interruptions constituent un moyen pratique de répondre aux événements externes et internes.

Dans cet article, nous examinerons ce qu’il faut pour configurer des gestionnaires d’interruption sur les entrées GPIO, en utilisant un exemple pratique impliquant un codeur incrémental rotatif.

Assemblage requis

Schéma du cœur Cortex-M4 dans la famille de microcontrôleurs STM32F4. (ST PM0214, section 1.3).

Les interruptions sur les microcontrôleurs STM32 existent en deux versions: internes et externes. Les deux types d’interruptions utilisent le même périphérique central dans le cœur Cortex-M: le Contrôleur d’interruption à vecteur imbriqué, ou NVIC. En fonction du cœur Cortex-M exact, ce périphérique peut prendre en charge des centaines d’interruptions, avec plusieurs niveaux de priorité.

Ces interruptions ne sont cependant pas toutes librement attribuables. Si nous regardons le manuel de référence pour les MCU STM32F4xx (en particulier RM0090, section 12), nous pouvons voir que pour les lignes d’interruption NVIC, nous obtenons une réduction de 82 à 91 canaux d’interruption masquables sur un total de 250 pour le noyau NVIC. périphérique dans le Cortex-M4.

Ces canaux d’interruption ont tous un but spécifique, tel que défini dans le tableau vectoriel (par exemple RM0090, tableau 62), qui compte plus de 90 entrées. Certaines de ces interruptions sont réservées aux événements du processeur, de la mémoire ou du bus de données (par exemple les pannes), tandis que celles qui sont généralement les plus intéressantes pour un développeur sont celles liées aux périphériques non-core. À peu près n’importe quel périphérique – qu’il s’agisse d’une minuterie, d’un canal USART, d’un canal DMA, d’un SPI ou d’un bus I2C – a au moins une interruption qui lui est associée.

Schéma fonctionnel du périphérique EXTI (RM0090, 12.2.5).

Il en est de même pour le périphérique EXTI (EXTernal Interrupt / Event Controller). Sur les familles STM32F1, F4 et F7 STM32, le périphérique EXTI est associé à 7 interruptions et 3 sur le F0 (STM32F04x et autres). Pour le premier groupe, ceux-ci sont décrits comme suit:

  • Ligne EXTI 0
  • EXTI ligne 1
  • EXTI ligne 2
  • EXTI ligne 3
  • EXTI ligne 4
  • EXTI ligne 5 à 9
  • EXTI lignes 10 à 15

Comme on peut le voir, nous obtenons 16 lignes sur le périphérique EXTI qui peuvent être utilisées avec des broches GPIO, mais certaines de ces lignes sont regroupées, ce qui nécessite un peu plus de travail dans le gestionnaire d’interruption pour déterminer quelle ligne a été déclenchée si cela est souhaitable. Les lignes elles-mêmes sont connectées à l’aide de multiplexeurs aux broches GPIO comme dans le schéma suivant:

Mappage des périphériques STM32F4 EXTI vers GPIO. (RM0090, 12.2.5)

Cela signifie que sur les familles F1 à F7, les broches GPIO 0 à 4 reçoivent une interruption dédiée qu’elles partagent avec d’autres périphériques GPIO. Les 11 broches restantes sur chaque périphérique GPIO sont regroupées dans les deux interruptions restantes. Sur la famille STMF0xx, les lignes 0 & 1, ainsi que 2 & 3 et 4 à 15 sont regroupées en un total de trois interruptions.

Les lignes EXTI restantes sont connectées à des périphériques tels que RTC, Ethernet et USB pour des fonctionnalités telles que les événements de réveil et d’alarme.

Temps de démonstration: encodeurs incrémentiels et interruptions

Codeur incrémental rotatif mécanique monté sur un PCB.

Le fonctionnement des codeurs incrémentaux rotatifs mécaniques est qu’ils créent alternativement un contact entre la broche d’entrée unique et les broches de sortie A et B. Le résultat est une sortie pulsée à partir de laquelle on peut déduire le sens de rotation et la vitesse. Ils sont couramment utilisés dans les panneaux de contrôle, où deux broches supplémentaires fournissent une fonctionnalité de bouton-poussoir.

Cependant, pour détecter correctement ces impulsions, notre code qui s’exécute dans le MCU doit être conscient de chaque impulsion. Les impulsions manquées entraîneront des effets visibles pour l’utilisateur, tels qu’une réponse lente dans le système, ou même un changement de direction qui ne sera pas détecté immédiatement.

Pour cet exemple, nous utiliserons un encodeur rotatif standard, connectant sa broche d’entrée à la masse et connectant les broches A et B aux entrées GPIO. Cela peut être n’importe quelle combinaison de broches GPIO sur n’importe quel port, tant que nous gardons à l’esprit que nous ne chevauchons pas les numéros de broches: si nous utilisons, par exemple, PB0 pour le signal A, nous ne pouvons pas utiliser PA0 ou PC0 pour le signal B. On peut cependant utiliser PB1, PB2, etc.

Configuration des interruptions externes

Les étapes impliquées dans la configuration d’une interruption externe sur une broche GPIO peuvent être résumées comme suit:

  • Permettre SYSCFG (sauf sur F1).
  • Permettre EXTI dans RCC (sauf sur F1).
  • Ensemble EXTI_IMR enregistrez-vous pour la broche pour activer la ligne comme une interruption.
  • Ensemble EXTI_FTSR & EXTI_RTSR registres pour la broche pour le déclenchement sur front descendant et / ou montant.
  • Ensemble NVIC priorité à l’interruption.
  • Activer l’interruption dans le NVIC S’inscrire.

Par exemple, un MCU de la famille STM32F4, nous activerions d’abord le périphérique SYSCFG (contrôleur de configuration système).

RCC->APB2ENR |= (1 << RCC_APB2ENR_SYSCFGCOMPEN_Pos);

Le SYSCFG périphérique gère les lignes d’interruption externes vers le GPIO périphériques, c’est-à-dire le mappage entre un GPIO périphérique et le EXTI ligne. Disons que si nous voulons utiliser PB0 et PB4 comme broches d’entrée pour les signaux A et B de notre encodeur, nous devrons définir les lignes en question sur les valeurs appropriées. GPIO périphérique. Pour le port B, cela se ferait dans SYSCFG_EXTICR1 et SYSCFG_EXTICR2, car chaque registre 32 bits couvre un total de quatre EXTI lignes:

Registre SYSCFG_EXTICR1 pour les microcontrôleurs STM32F4. (RM0090, 9.2.3)

Bien que quelque peu déroutant à première vue, la configuration de ces registres est relativement simple. Par exemple pour PB0:

SYSCFG->EXTICR[0] |= (((uint32_t) 1) << 4);

Comme la section de chaque ligne dans le registre est de quatre bits, nous décalons vers la gauche la valeur de port appropriée pour atteindre la position requise. Pour PB4, nous faisons la même chose, mais dans le deuxième registre, et sans décalage à gauche, car ce registre commence par la ligne 4.

À ce stade, nous sommes presque prêts à configurer les registres EXTI et NVIC. Tout d’abord, nous devons activer le périphérique GPIO que nous avons l’intention d’utiliser et définir les broches en mode d’entrée en configuration pull-up, comme ici pour PB0:

RCC->AHB1ENR |= (1 << RCC_AHBENR_GPIOBEN_Pos); 
GPIOB->MODER &= ~(0x3);
GPIOB->PUPDR &= ~(0x3);
GPIOB-&>PUPDR |= (0x1);

Disons que nous voulons définir PB0 pour qu’il se déclenche sur un front descendant, nous devons d’abord activer la ligne 0, puis configurer les registres de déclenchement:

pin = 0;
EXTI->IMR |= (1 << pin); 
EXTI->RTSR &= ~(1 << pin); 
EXTI->FTSR |= (1 << pin);

Tous ces registres sont assez simples, chaque ligne ayant son propre bit.

Une fois cela terminé, nous devons simplement activer les interruptions maintenant et nous assurer que nos gestionnaires d’interruptions sont en place. D’abord le NVIC, qui se fait le plus facilement via les fonctions CMSIS standard, comme ici pour PB0, avec le niveau de priorité d’interruption 0 (le plus élevé):

NVIC_SetPriority(EXTI0_IRQn, 0);
NVIC_EnableIRQ(EXTI0_IRQn);

Les gestionnaires d’interruption (ISR) doivent correspondre à la signature de la fonction telle que définie dans la table vectorielle chargée dans la RAM au démarrage. Lorsque vous utilisez les en-têtes de périphérique ST standard, ceux-ci ont la signature suivante:

void EXTI0_IRQHandler(void) {
// ...
}

Lorsque vous utilisez C ++, sachez que les ISR doivent absolument avoir un symbole de fonction de style C (c’est-à-dire pas de changement de nom). Soit envelopper l’intégralité de l’ISR dans un extern "C" {} bloquer ou transmettre les déclarations des ISR pour contourner ce problème.

Emballer

Avec tout cela implémenté et l’encodeur câblé aux bonnes broches, nous devrions voir que les deux gestionnaires d’interruption que nous avons implémentés sont déclenchés chaque fois que nous faisons tourner l’encodeur. Une grande partie du code de cet article était basée sur l’exemple «Eventful» du projet Nodate. Cet exemple utilise les API implémentées dans la classe Interrupts de ce framework.

Bien qu’à première vue quelque peu intimidant, utiliser des interruptions et même les configurer manuellement comme décrit dans cet article ne devrait pas sembler trop intimidant une fois que l’on a un aperçu de base des composants, de leur fonction et de ce sur quoi définir les registres individuels.

L’utilisation des périphériques NVIC et EXTI pour détecter les entrées externes n’est bien entendu qu’un exemple d’interruptions sur la plate-forme STM32. Comme évoqué précédemment, ils servent une myriade d’autres objectifs, même en dehors du noyau Cortex-M. Ils peuvent être utilisés pour réveiller le MCU d’une condition de veille, ou pour qu’un périphérique de minuterie déclenche périodiquement une interruption afin qu’une fonction spécifique puisse être exécutée avec un déterminisme élevé plutôt qu’en vérifiant une variable dans une boucle ou similaire.

J’espère que cet article a fourni une vue d’ensemble et une base solide pour de nouvelles aventures avec les interruptions STM32.