Bonjour, on a vu le concept d'interruption avec les timers. On va en reparler en détail aujourd'hui, montrer la différence entre scrutation, polling et puis l'interruption, parler des vecteurs et des routines d'interruptions qui sont appelés par le processeur et qui utilisent la pile. Je reviens 15 ans en arrière quand le microcontrôleur 16F877 a été annoncé. C'était le plus performant de l'époque. Son mécanisme d'interruption était d'avoir une interruption unique, autorisée, comme on l'avait vu, par un bit d'interruption général et devant cette possibilité d'interruption, il y a plusieurs flags, le timer, une entrée externe et je ne me souviens plus de ce que faisait celle-là. Et, le 877, ça c'est ce qu'on avait dans la génération précédente, le 84. Le 877 rajoutait toute une fonctionnalité avec des timers et, chaque fois, vous avez un timer supplémentaire avec son bit d'autorisation. Du point de vue interne, l'architecture était très simple, vous aviez un compteur d'adresse, vous aviez la mémoire programme avec une position de démarrage pour le programme principal et une position de démarrage pour les interruptions à l'adresse 4. Donc, au début du programme, il fallait mettre une instruction de saut dans la partie d'exécution et puis la routine d'interruption pouvait s'exécuter à partir de 4. Quand il y avait interruption ou quand il y avait une routine qui était appelée, et bien, la pile offrait la possibilité de quelques accès à imbriquer. Bien, l'amélioration, c'est de dire : c'est quand même embêtant quand on a une interruption, de balayer tous ces flags, c'est du polling, comme on dit en anglais, pour savoir lequel a créé l'interruption. Bien sur on, avec les enable, on a activé seulement les fl, les quelques interruptions qu'on va utiliser dans l'application mais le perfectionnement évident pour la prochaine génération, c'était de dire : si on s'intéresse à cette interruption, ce serait agréable qu'elle aille directement appeler un vecteur, si elle est autorisée naturellement, appeler un vecteur dans le processeur, partir directement à une adresse différente. Et, là, le terme de vecteur est utilisé mais c'est une adresse de saut et comme on le voit dans cet exemple pour un processeur un petit peu plus récent mais c'est en fait un AVR plus simple que celui des cartes Arduino, vous avez un certain nombre de vecteurs qui sont préparés en mémoire : vous avez le reset, vous avez le timer overflow, qu'on a déjà vu, et euh, cette liste se prolonge d'année en année en fonction de la complexité des processeurs. Alors, il y a différentes catégories d'interruptions. Vous pouvez avoir une pin extérieure qui demande une interruption, c'est ce qu'on appelle l'external interrupt. Vous avez le pin change, j'en reparle tout à l'heure, vous avez le timer, qu'on connait, vous avez le convertisseur A/D, on en a parlé mais je n'ai pas dit qu'il pouvait déclencher une interruption. En général, effectivement, avec la routine analogRead, on attend que ça soit terminé, c'est la fonction analogRead qui attend sur le flag pour savoir quand la conversion est terminée. Alors évidemment qu'on pourrait faire autre chose pendant ce temps et avoir une interruption au moment où le flag s'active. Il y a en général un canal analogique qui mesure la température à l'intérieur du processeur. Donc ce qu'on pourrait programmer là, c'est que au dessus d'une certaine température, et bien il y a une interruption et que vous déclenchez une action euh, de clignotement spécial par exemple. Vous avez naturellement des interruptions associées aux possibilités de communication, on en reparlera. Vous avez le Watchdog. Qu'est-ce que c'est? Et bien, le chien va hurler si vous ne vous occupez pas de lui donner à manger en temps voulu. En d'autres termes, vous avez un compteur qui décroît pour arriver à 0 et quand il arrive à 0, ça crée cette interruption euh, watchdog. Et ce que vous devez faire dans le programme, c'est remettre dans ce registre watchdog une valeur suffisamment grande pour avoir le temps de la réactiver. Alors, ça ça peut être utilie, par exemple, dans une, dans une station météo où tout à coup une perturbation due à un éclair, par exemple, peut faire dérailler le processeur, il y a peu de chances qu'il retombe dans le programme principal, qui réactive ce watchdog toutes les minutes, par exemple, vous vérifiez que le fonctionnement est correct. Si la fonction n'est plus correcte, ben, au bout d'une minute, il va y avoir une action de réinitialisation déclanchée par cette routine watchdog. Vous avez des exceptions, ça devient un petit peu plus compliqué mais il y en a une qui est facile à comprendre. Dans une division, vous n'avez pas le droit de diviser par 0, si vous faites cette erreur de programme, et bien, un processeur moderne va appeler une routine d'exception qui va vous sortir des calculs arithmétiques dans lesquels vous n'avez aucun espoir d'obtenir une réponse valable. Il peut y avoir des détections de mauvais fonctionnement interne. Vous avez enfin le "non maskable interrupt" et ben, ça veut simplement dire qu'il n'a pas la porte et qui permet de désactiver. Donc c'est souvent, enfin dans les anciens processeurs, associé à une pin qui permettait de récupérer le processeur et n'existe plus sur les processeurs récents que j'ai vus. Et vous avez comme dernier recours, le reset, qui évidemment n'est pas vraiment un, un vecteur d'interruption, c'est le démarrage du processeur qui se fait en position 0 pour certains processeurs, éventuellement ça peut être au sommet de la mémoire. Je mentionne encore ce sleep, qui en fait n'est pas une interruption, c'est une possibilité de programmation qu'on reverra, qui en fait, endort le processeur. Endormir, ben ça veut dire qu'on va désactiver le plus possible de ses fonctions, garder une horloge qui ne fait que de surveiller les interruptions et, dès qu'il y a une interruption, et bien le processeur va se réveiller pour servir cette interruption. Donc c'est un moyen d'économiser beaucoup d'énergie puisqu'au lieu que le processeur tourne dans une boucle à la vitesse maximum et gaspille la même énergie que d'habitude, et bien on l'endort et il se réveille dès qu'il y a une sollicitation interne ou externe. Si je regarde maintenant un petit peu l'organisation en mémoire de ce processeur. Donc on a vu qu'il y a toute une série de vecteurs qui vont se trouver au début de la mémoire flash. Ensuite, ben, vous aurez le programme, vous aurez les fonctions. Vous remarquez que la fin de la mémoire est une partie réservée, dans le fond, pour le programme de démarrage hein, c'est un programme relativement compliqué qui surveille USB et qui va vous permettre, via USB, de venir remplir ce programme. Alors, c'est une zone protégée puisque, naturellement, il ne faut pas que vous puissiez, en déclarant votre programme, venir intervenir dans cette zone. Bien, du côté de la mémoire vive, donc là on a vu que c'était un, une largeur de 16 bits, toutes les instructions sont 16 bits. Dans les processeurs 8 bits, et bien, on prends deux mots consécutifs. Là, c'est un processeur 8 bits, parce que la mémoire de donnée est 8 bits, l'unité arithmétique est 8 bits. Et vous trouvez des registres de travail dans lesquels euh, le calcul va se faire, tous les, tous les transferts importants vont se faire. Quand on a besoin de plus d'informations, et bien on va chercher dans la mémoire vive et c'est plus lent, les transferts perdent du temps donc on s'efforce de gérer, c'est le travail du compilateur, de gérer au mieux ces registres pour que l'exécution soit rapide et de préparer au moment de la compilation pour que l'on mette la bonne information dans mémoire vive ou dans les registres. Vous avez, ici, les registres d'entrées-sorties, le timer, les bords, et cetera, 64 positions sont réservées pour ça. Et puis en plus de ça, on en a parlé, vous avez cette mémoire, E carré PROM, jusqu'à l'adresse un FF. Donc, 100 c'est 256. Vous avez, ici, 512, position mémoire des PROM. Alors, revenons, pour préciser, ces interrupt externes. Donc, vous avez, dans le cas des processeurs qu'on trouve sur Arduino, deux pins qui peuvent déclencher une interruption. Et alors, vous avez le vecteur int zéro qui va être appelé, si vous avez activé les deux pins, int un, si vous avez activé l'autre des deux pins. Et maintenant, évidemment, alors on va laisser le choix à l'utilisateur de dire, est-ce que c'est un flanc montant, un flanc descendant, un changement qui va activer l'interruption? Alors, cette information, vous la trouvez dans un registre, et là, j'ai copié quelques petites parties de la documentation du fabricant. Et on vous dit, voilà, ces deux bits vous permettent d'avoir le choix suivant. Si c'est la configuration zéro zéro, et bien, c'est un low level, c'est un, un état zéro, vous avez peut-être de la peine à lire. Si c'est zéro un, c'est un changement, any logical change. Si c'est un zéro, c'est un flanc descendant. Si c'est zéro, euh, un, c'est un flanc montant. Donc, vous choisissez quand votre interruption va être déclenchée en disant, ben, dans ce registre EICR, et bien, on va activer, par exemple, falling edge en prenant ce bit-là qui va se trouver activé dans ce mode de configuration. Maintenant, en plus de ça, il faut savoir que le flag d'interruption sera dans ce registre, à cette position. En fait, le vecteur, d'interruption va nous faciliter ça, mais ce qui est très important, c'est d'activer cette interruption avec un interrupt enable. Donc ça, dans cet interrupt enable mask, on va activer le int un. Et ça, ça a été fait, c'est à faire dans le setup. Ensuite, une fois qu'on a activé toutes, préparé toutes les interruptions, on va pouvoir faire le set interrupt enable SEI, et le programme, dorénavant, sera sensible à ces interruptions. Alors, au niveau des fonctions qu'il faut définir à ceci, à cet interrupt, et bien, le compilateur, en association avec le processeur, vous permet d'écrire ISR, interrupt service routine, et le nom int un vect va être reconnu pour aller exécuter ce qui doit être exécuté lorsque cette interruption est activée. Alors là, j'ai décidé tout simplement qu'on fait clignoter une led. Donc, c'est tout le contenu de cette procédure d'interruption. Initialiser les registres, demander au processeur de partir à la bonne adresse, dans le bon vecteur d'interruption, et faire ce que vous avez à faire avec quelques petites règles que l'on verra, si on essaye de faire quelque chose d'un petit peu plus compliqué. Alors, Arduino vous dit, oh la la, c'est trop compliqué pour vous. On va vous fournir un attachInterrupt. On va fournir une librairie attachInterrupt, et une fois que vous avez défini le pin mode, vous écrivez attachInterrupt, vous écrivez le numéro de la pin, mais dans certains cas, c'est le numéro zéro un de ces interruptions. Vous donnez le nom de la procédure que vous devez exécuter, et vous dites avec des mots-clés que vous trouvez à tel endroit, si c'est low, si c'est change, et cetera. Et puis ensuite, dans votre programme, dans la partie des fonctions, il faudra définir la fonction faire quelque chose, qui ici, était simplement, clignoter, et voilà comment ça s'inscrit dans l'esprit Arduino où la réponse à l'interruption sera naturellement beaucoup plus lente, puisque chaque fois, ce sont les procédures qui testent et qui déclarent. Donc, à vous de choisir. Et parlons maintenant des pin change. Alors, le pin change agit sur plusieurs bits à la fois. Il agit, en fait, sur un port pour savoir s'il y a eu changement. L'application type, c'est un clavier balayé où en fait, étant donné que vous avez beaucoup trop de touches, vous pouvez pas les relier directement au processeur. On fait un réseau où on va sélectionner une rangée de touches, et les touches qui sont pressées, donc, on va mettre un zéro dans le cas particulier, puisqu'on a des pôles amp. Les touches qui sont pressées vont passer à zéro. Alors, chacune de ces touches va être, lues. Là, j'en ai dessiné qu'une seule. Mais ça veut dire que je me suis intéressé à cette, à ce, cette pin, ici. Et puis, pour dire que je m'intéresse à cette pin, je dois agir sur un masque qui va activer, ici, la porte ET pour que la logique qui suit soit attentive à ce qui se passe sur cette pin, donc ça, c'est toujours ce principe général qu'on trouve avec les autorisations. Alors, qu'est-ce qui se passe maintenant? Vous n'avez pas appuyé sur cette touche. Donc, vous avez un un. Ce un passe dans un registre à décalage. Vous vous souvenez, ils continuent dans le registre à décalage, puisqu'ils sont nettement connectés à la suite de l'un iii. là vous avez, une porte OU exclusif, un et un, ça veut dire zéro. Maintenant, vous appuyez sur la touche, là, qu'est-ce qui se passe? Vous allez voir un zéro, ici, d'abord, qui va passer par la porte, puisque vous avez autorisé. Ce zéro va être copié dans la première bascule, et attention, là, maintenant, il y a toujours un un, ici. J'ai un zéro, là. Donc, la porte OU exclusif va passer à un. Donc, le flag PCF pin change et i flag est activé. S'il a été autorisé avec un interrupt enable, si les interrupts sont actives, vous êtes partis dans la routine qui va vous dire que vous avez activé sur cette touche. Et étant donné qu'on vous offre que la possibilité de faire un pin change, et bien, vous aurez également une interruption quand c'est relâché, et on ne reparlera pas des claviers balayés, mais, si vous les étudiez, et bien, vous apprendrez que ça peut être prudent de mettre des diodes à chaque intersection pour éviter qu'il y ait ce qu'on appelle les touches fantômes, des touches qui apparaîssent, alors qu'on les a pas sollicitées. Alors voilà, on a pu faire une petite révision de système logique, ici, et c'est multiplié sur, et bien, dans les, dans les processeurs Arduino, vous avez, sauf erreur, une vingtaine de fois, cette logique, et vous pouvez distribuer les actions de votre, clavier sur les pins qui vous arrangent, mais ensuite, je ne dis pas que c'est trivial de trouver la routine qui fonctionne bien. Bon, je reviens à mon timer. On avait vu qu'il pouvait créer une interruption. Donc, on a initialisé les RJ. Je vais pas vous répéter. Le but, maintenant, c'est de voir un petit peu plus en détail, une routine qui me semble très, très utile pour beaucoup, beaucoup d'applications. Alors, vous avez initialisé vos timers avec son horloge, avec son masque, et vous avez lancé les interruptions. Donc, au moment où vous aurez toutes les 100 microsecondes, une interruption qui vous amène, ici, dans timer 2 overflow, le flag est automatiquement remis à zéro. C'est la procédure générale, avec, euh, avec AVR. Vous réinitialisez votre timer pour avoir une nouvelle auto, interruption dans 100 microsecondes. Et maintenant, vous êtes partis pour faire ce que vous voulez, et l'exemple que j'explique un petit peu plus en détail, parce qu'il comprend, il correspond mieux à, à quelque chose que je fais fréquemment. Toutes les 100 microsecondes, vous avez envie de gérer un encodeur, encodeur de rotation, on en parlera prochainement. Toutes les 100 microsecondes, il faut gérer des moteurs pas à pas, parce que les transitions, pour les pas, doivent être assez précises. On parle aussi des moteurs pas à pas, prochainement. Et maintenant, on n'a plus besoin de faire les choses aussi fréquemment. Donc, on met en route, on surveille un compteur par 40, qui toutes les 40 fois recommence, et ce qui veut dire qu'on va continuer, on va exécuter, toutes les quatre millisecondes, euh, cette fonction qui est du Pfm, euh, ça c'est mon petit préféré, pour remplacer le PVM. On en reparlera aussi en détail. Et maintenant, j'aimerais encore faire quelque chose toutes les secondes, et dois rafraîchir la mémoire à propos du, de watchdog. Vous lancez une opération, vous attendez une touche d'un clavier. Et bien, si cette touche ne vient pas, il faut arrêter d'attendre à un moment donné. Donc, ça, c'est qu'on appelle le, le concept du, du time out, du forclos, devrait-on dire en français. Et là, je réalise une fonction time out qui pourrait être programmée en fonction des besoins. Alors, toutes les secondes, ça, c'est avec ce petit compteur qui va compter 250 fois quatre millisecondes. Je décompte un time out, et si ce compteur arrive à zéro, je dois faire quelque chose. Et puis, ce quelque chose, ce sera une procédure que je vais définir, qui s'appelle alarme time out. Et maintenant, je dois faire quelque chose, puisqu'ici, j'ai, je vais sans arrêt passer à travers. Donc, il faudrait éviter que, que le compteur time out reste à zéro, si ce n'est pas, moi qui ai décidé. Donc, c'est dans le programme principal qu'on va définir une valeur supérieure à zéro. Et maintenant qu'on est arrivé à zéro, il faut rester tranquille. Donc là, une solution, c'est de réincrémenter le timer, comme ça, on sera différent de zéro. Donc, ce qu'on a vu aujourd'hui, c'est cette différence entre polling et interruption. C'est la puissance de ces vecteurs d'interruption qui oblige à comprendre un petit peu, la structure du processeur, si on veut travailler directement avec cette mise en route d'interruption qui est très efficace. Et je vois qu'on n'a pas beaucoup parlé de la pile qui, en fait, est simplement un petit peu plus grande que dans le cas des appels de procédures fonctions usuelles, puisqu'il faut sauver un peu plus de l'état du processeur.