Published on

Différence entre le polling et l'interruption en embarqué

 10 mins
Authors
  • avatar
    Name
    Léo Delpon
    Twitter

Prérequis:

  • Avoir des connaissances dans le domaine du firmware
  • Savoir ce qu'est le C ainsi que la notion d'une adresse

Le principe du Polling

Lorsque l’on configure un pin en mode polling, le CPU va vérifier périodiquement et manuellement si il y a des requêtes I/O de disponible, si il n’y en a pas il va continuer à exécuter les instructions de la stack et si il y en a, il va gérer en priorité la requête I/O. Quand je parle de requête, c’est surtout le fait de savoir si de la donnée est disponible ou si le pin est capable d’accépter de la donnée.

Dans les premiers systèmes informatiques personnels, c'est exactement comme cela qu'un programme se comportait ; lorsqu'il voulait lire une touche du clavier, il interrogeait le port d'état du clavier jusqu'à ce qu'une touche soit disponible. Ces ordinateurs ne pouvaient pas effectuer d'autres opérations en attendant le clavier.

Exemple

On configure dans un premier temps les pins du microcontroleur

Untitled

Avec la fonction HAL_UART_Receive nous pouvons, recevoir de la données depuis un pin spécifique qui aura été paramétré pour être en UART_RX

uint8_t rx_data[10] // creation d'un buffer de 10 octets

while (1) {
	// Fonction permettant de recevoir 4 octets de données
	HAL_UART_Receive(&uart2, rx_data, 4, 100);
}

Si vous exécutez ce code, vous ne posséderez surement pas la totalité des données que vous souhaitez envoyer. En effet, le polling n’a été exécuté que pour écouter pendant 100 ms

Le principe des interruptions

Vous connaissez le principe du polling , à présent, nous allons nous attaquer à quelque chose d’un peu plus complexe : les interruptions . Avant de vous expliquer concrètement ce que c’est, il semble pertinent de vous montrer une exemple :

Le polling est également utilisé dans la programmation réseau pour la réception et l'envoi de données sur un socket. Dans ce cas, une boucle de polling est utilisée pour vérifier régulièrement la disponibilité des données à lire ou pour s'assurer que les données sont correctement envoyées. Cependant, l'utilisation du polling peut entraîner une surutilisation du processeur, ce qui peut être évité en utilisant des interruptions ou des mécanismes de blocage non bloquant.

Imaginez que vous soyez en train de faire cuire une pizza au four qui sera prête dans 10 mintues. Vous avez deux choix:

  • Vous avez une excellente notion du temps et vous pensez gérer ça sans minuteur, vous allez regarder une série Netflix. Sauf que vous êtes de nature stressée et vous allez vérifier si votre pizza est prête à un interval régulier car vous n’avez pas de minuteur.
  • Votre four est équipé d'une minuterie, vous réglez cette dernière sur dix minutes et vous allez regarder votre épisode de “How I met your mother” pendant la cuisson de votre pizza et le décompte du temps de la minuterie du four.

Dans le premier cas, vous occupez 100 % de vos ressources sur une tâche qui est de compter à haute voix. Vous êtes donc dans l’incapacité de faire autre chose sous peine de perdre potentiellement le compte !

En revanche, dans le second cas, vous pouvez effectuer d'autres tâches pendant que l'opération de cuisson se déroule. Le four s'arrête automatiquement après les dix minutes et sonne pour vous informer que le processus est terminé. Cela a été rendu possible grâce à l'utilisation d'une minuterie et d'une sonnette intégrées au four.

Ces concepts, tels que la possibilité pour un cuisinier affamé de faire autre chose que de surveiller sa pizza en train de cuire, ainsi que l'utilisation de minuteries et de sonnettes, peuvent également être appliqués aux microcontrôleurs.

Pour un microcontrôleur, une interruption est un événement qui entraîne plusieurs actions :

  • la mise en pause du programme en cours d'exécution,
  • la sauvegarde du contexte d'exécution du programme,
  • le traitement d'actions spécifiques associées à l'interruption, appelée la routine de service de l'interruption (ISR),
  • la restauration du contexte qui était en vigueur avant l'interruption, une fois que le traitement de cette dernière a été terminé,
  • et enfin la reprise de l'exécution du programme qui avait été interrompu.

Les noyaux ARM prennent également en charge les lignes d'interruption qui sont "externes" au noyau lui-même. Ces lignes d'interruption sont généralement acheminées vers des périphériques spécifiques au fournisseur sur le MCU, tels que les moteurs d'accès direct à la mémoire (DMA) ou les broches d'entrée/sortie à usage général (GPIO). Toutes ces interruptions sont configurées via un périphérique appelé Nested Vectored Interrupt Controller (NVIC). Les interruptions sont des événements asynchrones par rapport à l'horloge du microcontrôleur et peuvent être provoquées par des éléments matériels, tels que des timers, des boutons-poussoirs, ou des périphériques capables de recevoir des données, ou par le microcontrôleur lui-même en cas d'erreur ou de détection d'une exception.

Le numéro d'exception pour les interruptions externes commence à 16. Le manuel de référence ARMv7-M contient un bon graphique qui affiche les mappages des numéros d'exception :

Untitled

Passons à la partie technique !

Dans une carte STM32, chaque interruption est associée avec un vecteur d’interruption qui est codé sur 4 octets.

Chaque vecteur d’interruption contient l’adresse de la routine de service d’interruption. Cette adresse indique l’emplacement du code qui doit être traitée lorsque l’interruption intervient.

Si la mémoire n’a pas été déplacée, le début de la mémoire du STM32 se présente comme ceci :

// Adresse        //Contenu de la mémoire             //Signification pour le STM32
0x0000 0000       Réservé                             Emplacement de la mémoire reservée
0x0000 0004       Exception 0                         Reset
0x0000 0008       Exception 1                         NMI
0x0000 000C       Exception 2                         HardFault (erreur materielle ou division par 0)
...
0x0000 0040       INT0                                Window Watchdog interrupt (surveillance hardware pour générer une interruption en cas de timout)
0x0000 0044       INT1                                PVD sur EXTI Line detection interrupt (Surveillance de la tension d'alimentation)
0x0000 0048       INT2                                Modification de la branche TAMPER
...
0x0000 0058       INT6                                EXTI0
...
0x0000 0130       INT60                               DMA2 (permet de transférer des données entre périphériques sans passer par le processeur)

L’ensemble de ces vecteurs sont contenus dans une table qui est relocalisable, ce qui signifie que cette table peut être déplacée dans la mémoire d’un programme STM32. Cette table est stockée dans la mémoire flash pour que cette dernière soit présente au démarrage de l’appareil et ne doit pas être modifiée pendant l’exécution du programme.

La modification de l'emplacement de la table de vecteurs d'interruption via le registre SCB_VTOR (System Control Block Vector Table Offset Register) permet de redéfinir l'emplacement de cette table en mémoire, ce qui peut être utile dans certaines situations.

Par exemple, si une carte dispose de plusieurs périphériques intégrés qui ont leur propre table de vecteurs d'interruption, il peut être nécessaire de modifier l'emplacement de la table de vecteurs d'interruption pour qu'elle pointe vers la table de vecteurs d'interruption du périphérique utilisé à un moment donné. De même, si une application utilise un bootloader qui redéfinit la table de vecteurs d'interruption, il peut être nécessaire de modifier l'emplacement de la table de vecteurs d'interruption pour qu'elle pointe vers la table de vecteurs d'interruption du bootloader.

En modifiant l'emplacement de la table de vecteurs d'interruption, on peut donc permettre à l'application de gérer de manière plus flexible les interruptions en fonction du contexte et des périphériques utilisés.

La priorité dans les interruptions

Les interruptions ont des priorités qui sont réglables, sauf pour les 4 premières exceptions, dont le niveau de priorité est fixe.La priorité est un nombre qui est lié à chaque interruption.Plus ce nombre est faible, plus l'interruption correspondante est prioritaire.

Une interruption peut survenir alors qu'une autre interruption est déjà en cours de traitement.Si la nouvelle interruption est plus prioritaire que celle qui est exécutée, alors cette dernière sera mise en pause et laissera la nouvelle interruption se dérouler, jusqu'à ce qu'elle soit terminée. L'interruption précédente reprendra ensuite.

Si la nouvelle interruption n'est pas prioritaire, l'interruption en cours de traitement continuera d'être exécutée sans aucune modification de son déroulement.

Les niveaux de priorité des exceptions pouvant être modifiés peuvent l'être grâce aux registres nommés « SCB_SHPR1 », « SCB_SHPR2 » et « SCB_SHPR3 ».

Les niveaux de priorité des interruptions peuvent l'être grâce aux registres nommés « NVIC_IPR0 » à « NVIC_IPR20 ».

Polling vs Interruption

Il existe deux “styles” de programmation réactive à des événements:

  • En polling, le code consulte régulièrement un état (registre) et agit de façon spécifique en fonction de l’évenement.
    • le temps de réaction va dépendre de la fréquence de la consultation (en général cela correspond à la fréquence du processeur)
    • chaque consultation va prendre du temps processeur, on appelle ça l’attente active
    • C’est une méthode très lente et très énergivore pour le processeur
  • En interruption, on laisse la consultation se faire par le hardware qui va donc nous “prévenir” en cas d’évenement.
    • le temps de réaction est très court (dépend du hardware)
    • le hardware va être le contrôleur d’interruption
    • il n’y a pas de consultation régulière par le processeur. On appelle cela de la programmation réactive

Et voilà ! Vous savez désormais ce qu'est la différence entre le polling et l'interruption 🙂.