Bonjour, nous continuons le cours "Comprendre les microcontrôleurs". Nous avions commencé de parler de la gestion du temps, et nous l'avions appliqué à des programmes qui n'utilisaient que les sorties. Maintenant, nous allons aussi nous occuper des entrées, et en particulier, regarder le problème de l'incertitude qui existe sur les entrées, regarder comment on peut scruter les entrées, de s'occuper de détecter des flancs, de considérer le problème des rebonds de contact, de savoir aussi comment compter les événements, et comment mesurer une durée, mesurer un temps. Est-ce que vous vous souvenez du schéma correspondant à ce qui se trouve derrière chaque patte de microcontrôleur, à l'intérieur du circuit? Nous avions vu qu'il y avait fondamentalement une bascule, associée à la sortie, et un passeur, associé à l'entrée. Lorsqu'une sortie est utilisée, ce passeur est utilisé, mais on voit bien qu'il y a fondamentalement quelque chose de différent entre les entrées et les sorties. Pour une entrée, on regarde un court instant la valeur qui se trouve sur la patte, par contre, pour une sortie, on écrit dans une bascule, qui a la particularité, de mémoriser cette valeur, et cette valeur ne va pas changer, donc une nouvelle valeur ne va pas être écrite. Donc, la bascule offre une certitude sur la valeur de sortie, par contre, le passeur d'entrée ne donne la valeur de l'entrée, qu'à l'instant de la lecture, et laisse une incertitude pour tout le reste des instants. Nous allons donc utiliser, pour lire les entrées, un mécanisme qui s'appelle la scrutation. Le terme anglais souvent utilisé est "polling". D'après le dictionnaire, il s'agit d'un examen répété de l'état d'un ou de plusieurs éléments d'un système, pour y détecter un changement éventuel. Donc, on va lire, lire, lire et relire une entrée, en espérant y trouver un événement qui va se produire, un changement en particulier. Evidemment, les contraintes de temps, liées à cette scrutation sont très différentes, selon la nature d'un problème. Ici, vous voyez un tourniquet qui est utilisé, par exemple, dans les métros pour compter les personnes qui passent un couloir donné, par exemple. Ici, vous avez un moteur de moto, qui peut tourner extrêmement rapidement. Posons-nous la question, quel est le temps minimum entre deux scrutations, pour prendre connaissance d'une manière fiable de la rotation de ces deux dispositifs? Je précise que le tourniquet de métro reçoit une impulsion lors du passage d'une personne, une personne qui marche, ou éventuellement, qui court. Le moteur de moto, lui, reçoit 12 impulsions par tour, avec une vitesse maximum de 8000 tours par minute. On va voir que cette réponse est acceptable, évidemment, celle-là aussi, mais que seule celle-là , est acceptable. Essayons de regarder pourquoi. Le piéton va prendre environ une seconde pour faire basculer le tourniquet, peut-être une demi seconde. On imagine alors qu'une scrutation chaque centième de seconde serait largement suffisante. Par contre, le moteur tourne au maximum à 8000 tours par minute, soit un peu plus de 133 tours par seconde, ceci correspond à une période minimale d'environ 7,5 millisecondes. Si on a 12 impulsions, le temps minimum entre deux changements, une demi impulsion en quelques sortes, correspond donc à 312 microsecondes. Donc, dans ce cas-là , une scrutation toutes les millisecondes serait largement insuffisante. On a déjà appris à lire une entrée, donc, apparemment, c'est très facile d'écrire un programme dont le cahier des charges serait : "il faut allumer la LED rouge lorsqu'on presse sur le bouton-poussoir". Voilà toutefois une erreur que les débutants feraient facilement, la boucle principale, si on a pressé sur le bouton-poussoir, alors, on allume la LED, j'ai reproduit exactement le cahier des charges, tel que je l'ai lu. C'est effectivement faux, on comprend bien que ce programme allumerait une fois la lampe, et ne l'éteindrait jamais. On aurait peut-être dû exprimer le cahier des charges en disant, on veut que la lampe rouge s'allume lorsqu'on presse sur le bouton-poussoir, et qu'elle s'éteigne le reste du temps. Et alors le programme doit être complété par un else, si on ne presse pas sur le bouton-poussoir, alors il faut éteindre la lampe. Voilà une variante de ce programme, qui utilise à l'intérieur de la boucle principale, une deuxième boucle, on a donc une variable qui est bloquante, mais qui, pour ce problème-là , va résoudre le problème tout aussi bien. Le fait qu'une entrée soit active est une chose, le fait de détecter le moment où elle s'active, par exemple un flanc montant, en est une autre. Voilà un programme qui va permettre de détecter un flanc montant. Dans la boucle principale, nous allons d'abord attendre, tant que l'entrée est à 0, quand elle n'est plus à 0, elle est à 1, on va pouvoir exécuter l'action souhaitée. Et ensuite, il va falloir attendre le flanc descendant, avec, de nouveau, une boucle, mais avec cette fois la condition inverse. De nouveau, le programme que nous venons d'écrire est bloquant, puisqu'il contient deux boucles while, et il existe une méthode, qui permet de ne pas utiliser de while dans la boucle principale, seul, ici, un if, va être utilisé. L'astuce est la suivante, on va utiliser deux variables, une qui s'appelle valeur, qui va correspondre à la dernière valeur de notre entrée qui a été lue, et une, qui correspond à ancienne valeur. Les étudiants ont souvent un petit peu de peine à comprendre. Alors, il faut commencer par l'initialiser, avec la valeur, mais ensuite, elle prend, à la fin de la boucle, while, de la boucle principale, elle prend la valeur qui était, dans la variable valeur, donc qui a été lue au début de la boucle. Ca signifie que pendant l'exécution de la partie du milieu de la boucle, on dispose, à la fois, de la dernière valeur lue, et de l'avant-dernière valeur lue. Alors regardez maintenant ce diagramme des temps, on voit bien que, on dispose dans valeur, d'une sorte de copie de notre entrée, avec un décalage lié au fait qu'on lit les valeurs successivement, rappelez-vous ce qu'on a dit tout à l'heure, on ne sait pas ce qui se passe entre les lectures, donc ici, il y a un petit décalage entre ce flanc, et celui-là , entre ce flanc et celui-là , et ce signal a quand même à peu près la même allure que celui-là . Et on voit que cette ancienne valeur, c'est exactement la même chose que valeur, mais décalée d'un coup de lecture du signal, toujours ce décalage. Et ce qui est intéressant c'est que en comparant, la valeur avec l'ancienne valeur, il est possible, par exemple ici, de détecter les flancs montants, avec la présence d'un 1 ici, d'un 0 là . Il serait bien entendu aussi possible de détecter, un flanc descendant, à cet endroit-là , ou encore, détecter n'importe quel flanc en disant, il faut que l'ancienne valeur soit différente, de la valeur actuelle. On remarque bien, ici, que c'est différent, que c'est différent, et cetera. De nouveau, cette technique de programmation est très intéressante parce qu'elle permet d'exécuter plusieurs tâches dans notre boucle principale. J'ai écrit ici en rouge ce qu'il fallait rajouter pour gérer une deuxième entrée. Evidemment, les deux variables, la lecture, en début de boucle, la lecture, pardon, la copie, en fin de boucle, et finalement le traitement spécifique pour détecter le flanc sur la deuxième entrée. Le bouton-poussoir est très souvent branché à une entrée d'un microcontrôleur. On pourrait donc penser que, ce que nous venons de voir, fonctionne avec un bouton-poussoir. Et bien, ça n'est pas le cas. En fait, dans un bouton-poussoir, il y a des éléments métalliques, qui bougent, et on va avoir un effet de rebond de contact. Ce phénomène se produit d'ailleurs quand on presse sur le bouton, mais également quand on le lâche. Regardons un peu un diagramme des temps qui met en évidence ces rebonds de contact au moment où on presse, et au moment où on relâche. Il existe une technique très simple, qui permet de s'affranchir de ces rebonds de contact, c'est de ne lire la valeur d'entrée qu'à des intervalles suffisamment longs, pour ne pas risquer d'inclure un rebond de contact. Ici, on voit bien que le rebond de contact était complètement entre deux lectures, il n'y a évidemment pas de problème, on a passé proprement du 0 au 1. Ici, ça semble moins favorable, puisque c'est bien au milieu du rebond qu'on a fait la lecture, et malgré tout, on voit bien qu'on a ici lu un 1, ici, on aurait pu lire un 1 ou un 0, ça va dépendre de peu de choses, ensuite ici, un 0, et dans les deux cas que j'ai cité, on aurait eu successivement des 1, puis des 0, donc les rebonds auraient été supprimés. Le terme anglais utilisé pour la suppression des rebonds de contacts est debouncing. Nous avons ici, le programme qui permet de réaliser cette fonction de suppression des rebonds de contact, avec à l'intérieur de la boucle principale, un delay, qui fait que on ne lit, les entrées, que toutes les 20 millisecondes. Une variante, serait de lire en permanence l'entrée, et lorsque le flanc montant a été détecté, on fait une attente, qui masquerait le rebond. Attention, ça ne va pas fonctionner de cette manière-là . On serait dans une situation où on va lire, lire, lire, on aurait le delay, lire, lire, lire, lire, lire, et là , on récupérerai tous les rebonds de contact. Il est donc nécessaire de, lorsqu'on a de nouveau lu une valeur qui correspond au flanc descendant, d'attendre les 20 millisecondes. Voilà donc le programme, avec détection du flanc montant, application du delay, détection du flanc descendant, avec application du delay. Lorsqu'on fait la scrutation régulière d'une entrée, elle se fait à une certaine fréquence, qu'on appelle fréquence d'échantillonnage. Et le choix de cette fréquence ne doit donc pas se faire au hasard. La théorie du traitement de signal donne des indications, en particulier le fait que, si on a une fréquence maximum sur l'entrée, il faut que la fréquence minimum d'échantillonnage, soit, au moins, égale à 2 fois la fréquence maximale de l'entrée. Là , de nouveau, nous n'allons pas entrer dans ces théories qui dépasseraient le cadre de ce cours. Essayons de programmer maintenant, le comptage d'événements qui se produisent sur une entrée. Voilà encore un programme qui correspond à une erreur de débutant. Si le poussoir est actif, on incrémente le compteur. Ca ne va pas correspondre au nombre de fois qu'on presse sur le bouton, vous pourrez d'ailleurs le voir sur ce diagramme. Lorsque je presse sur le bouton, je vais compter plusieurs fois, ici je suis arrivé à 6, alors que j'ai eu deux impulsions qui se sont produites. Appliquons donc la technique de détection des flancs, que nous connaissons maintenant. De nouveau, l'acquisition de la valeur, la mémorisation de l'ancienne valeur, et dans cette partie, la détection d'un flanc qui elle, va produire, l'incrémentation du compteur. Un autre problème que nous sommes capables de résoudre maintenant, c'est la mesure du temps. Par exemple, mesurer la durée d'une impulsion. J'ai illustré ici, un compteur, qui, chaque fois que la valeur est active, va être incrémenté et qui sera repassé à 0, lorsqu'on aura trouvé le flanc descendant. Si on souhaite avoir un affichage en même temps, de la valeur, c'est quand on a terminé l'impulsion, quand le flanc descendant a été détecté, qu'il faut copier sur l'afficheur et maintenir la valeur, jusqu'à la fin de la prochaine impulsion. On a toujours, donc, la dernière impulsion qui s'est terminée, dont la durée est affichée sur l'afficheur. Voilà le programme correspondant, je rappelle qu'on utilise une boucle, une boucle à durée fixe, il n'y a pas de boucle à l'intérieur de la boucle principale, on trouve seulement des if. On va ici, compter, chaque fois que l'entrée est active, on totalise donc, la durée de l'impulsion, par contre, au flanc descendant, on va, mettre le compteur à 0 en vue du prochain comptage, mais auparavant on va afficher la valeur, qui va rester jusqu'à la fin de la prochaine impulsion. Et de nouveau, pour que cette ligne puisse avoir un sens, c'est-à -dire qu'on ait à la fois, connaissance de la valeur précédente, et de la valeur courante, il faut ce mécanisme de copier la valeur dans l'ancienne valeur à la fin de la boucle, alors que c'est en début de boucle que la valeur est lue. Nous avons donc maintenant intégré les entrées à la gestion du temps. Nous avons étudié l'incertitude des entrées, nous avons regardé comment scruter les entrées, faire du "polling", nous avons appris à détecter un flanc, à supprimer un rebond de contact, à compter des événements, et à mesure du temps. Dans la pratique, les applications que nous risquons de rencontrer, seront certainement plus complexes. Nous pourrons en particulier réaliser des machines d'état, dont nous avons appris tout au début de ce cours, à les décrire avec des graphes d'état. Ce sera le sujet d'un prochain chapitre.