Bonjour. Nous continuons le cours Comprendre les microcontrôleurs et en particulier, ces exemples que nous voulons vous présenter pour pouvoir expérimenter ce que nous avons appris au début du cours. Aujourd'hui, nous parlerons de programmation de machines d'état, les machines d'état, c'est un vieux souvenir. Vous vous souvenez certainement que lorsque nous avons présenté les systèmes séquentiels, nous avions pris un exemple avec une perceuse qui monte et qui descend. Un exemple très simple, mais qui nous a permis de trouver les méthodes pour résoudre ce type de problème. Nous allons reprendre ce problème, parce que nous ne l'avions pas très bien résolu. La solution avec les circuits logiques était lourde et fastidieuse, et, on ne connaissait encore rien des microcontrôleurs. Nous allons donc essayer de trouver un algorithme pour résoudre ce problème, et ensuite nous allons chercher une autre solution en exploitant particulièrement la variable d'état. Alors, vous vous souvenez certainement du problème auquel nous avions tenté de répondre. Donc une perceuse, avec le moteur qui actionne la mèche, des pièces ici, à percer, et finalement un moteur ici, qui entraîne un axe et qui va faire bouger dans un sens et dans l'autre cette perceuse. Vous vous souvenez qu'on avait placé également des interrupteurs de fin de course en bas, et en haut, et qu'il y avait également un bouton start qui permettait à l'ouvrier de démarrer le cycle. Nous avions donc un système avec trois entrées, start, bas et haut, avec les deux sorties qui commandent le moteur, avance" et recule et ici vous trouvez le graphe d'état avec ces trois états qui décrivent le fonctionnement de cette machine d'état. Je rappelle juste que nous avions trouvé une solution un peu compliquée, avec deux bascules qui mémorisaient l'état, avec un système combinatoire qui, à partir de l'état présent, et des entrées, calculait les nouveaux états, et une horloge qui permettait d'avoir ce calcul en permanence, et nous avions tenté d'écrire un programme, je pense que vous n'aviez pas compris grand chose puisque nous n'avions pas encore appris tout cela. Ce programme, maintenant vous êtes capables de le lire, il a été écrit d'une manière un petit peu brute, ici la lecture des valeurs, ici le calcul du système combinatoire, ici la mémorisation des valeurs, et finalement les sorties. C'est un programme qui en plus, n'est pas bien écrit, donc on va rapidement passer à la suite et essayer de trouver quelque chose de beaucoup plus lisible, de beaucoup plus clair. Les instructions du langage C peuvent être décrites par des organigrammes et souvent lorsqu'on a envie de programmer, on commence par réfléchir à l'organigramme. Donc, comment passer de notre graphe d'état à un organigramme. Alors, voilà ici une solution qui résout le problème. On voit bien qu'on doit mettre les valeurs 0 0 lorsqu'on est à l'arrêt, c'est ce qu'on a fait ici. Et on doit ensuite attendre de franchir cette transition. Pour attendre, il va falloir utiliser une structure de ce type-là, si la condition start n'est pas réalisée, on revient sur le test, si la condition est réalisée, on passe à la suite. On voit bien qu'ici ça correspond à l'état descente avec ses 2 valeurs, de la même manière, on attend qu'on soit arrivé en bas et puis, on arrive à la montée, et on attend qu'on soit arrivé en haut. Il y a bien évidemment une boucle ici qui permet de revenir au début de l'opération et qui permet à l'ouvrier de fabriquer une seconde pièce. Vous remarquez que nous avons quatre boucles dans ce système, oui j'ai bien dit quatre, c'est vrai qu'on voit bien la première, la deuxième, et la troisième, mais il ne faut pas oublier cette quatrième, qui est également une boucle. Dailleurs vous pouvez compter les flèches montantes : 1, 2, 3 et 4, les flèches montantes montrent systématiquement qu'il s'agit d'une boucle. Nous avons un organigramme, nous n'avons pas encore un programme en C, je rappelle qu'en C nous avons un certain nombrre de structures, des structures du type boucle, avec le while et le do while, qui se ressemblent, simplement le test se trouve une fois au début et une fois à la fin, et on a une structure de test qui s'appelle le if. Il est nécessaire de trouver une analogie entre l'organigramme que nous avons écrit et les instructions que nous allons exécuter. Prenons, par exemple, cette boucle. On connaît les while et les do while. Avec les while et les do while, on a le test d'une condition, et on a une instruction. Dans ce cas-là l'instruction n'existe pas. C'est pas grave, il suffira de mettre une instruction vide. Donc, on va effectivement ici, pour cette partie là, écrire quelque chose du type while, mettre la condition start, et ne rien exécuter. C'est vraiment juste, ce que j'ai fait? Non, c'est faux, parce que, ici, la condition start doit ne pas être vérifiée, pour boucler, c'est donc indispensable ici de dire, tant que on n'a pas de start, alors, on doit reprendre, continuer la boucle. La suite du programme est similaire, et il faudra évidemment ne pas oublier de mettre un while 1 pour cette boucle qui elle n'a pas de condition, n'a pas de test, c'est une boucle permanente. Voilà donc un programme. On y voit les trois boucles qui correspondent à l'attente pour les transitions et puis évidemment la boucle principale ici, while 1, boucle permanente. Ce que j'ai écrit ici en rose n'est pas tout à fait du C, dépêchons-nous de corriger ça. Avec ce qu'on vient d'apprendre, on voit qu'on peut écrire d'une manière très semblable. Alors voilà une solution qui maintenant est du C, j'ai remplacé cette écriture qui n'était pas acceptable, par cette syntaxe là, donc il va falloir créer des procédures avance et recule auxquelles on passe des paramètres et également une procédure qui s'appelle start, et la même chose pour bas et haut qui sont les procédures qui vont nous rendre une valeur qui va permettre de connaître l'état du bouton "start" et des interrupteurs de fin de course. Alors, étant donné les choix que j'ai faits pour cette syntaxe, avance", "recule", start, haut et bas, et cetera. eh bien il a fallu auparavant donner un certain nombre de définitions, ici la procédure avance, je lui passe un paramètre du type entier, j'exécute un digitalWrite avec le numéro de la patte correspondant à avant, qui a été défini ici, mais on a décidé d'utiliser la patte P1 souligné 0 la même chose, évidemment pour recule, et puis maintenant pour les entrées j'ai pris l'exemple de start, il s'agit aussi d'une procédure, mais cette fois qui rend une valeur, pour qu'elle puisse être utilisée à l'intérieur de la condition de la boucle, et cette procédure rend, c'est donc le paramètre de sortie, une valeur qui est la lecture de la patte start, la patte start a été définie ici, c'est P1 souligné 3, vous remarquez ici le point d'exclamation qui met en évidence le fait que le start est actif à 0, puisque l'interrupteur est câblé contre la masse, c'est ce qu'on fait généralement lorsqu'on utilise un interrupteur comme entrée d'un microcontrôleur. Voilà ici l'ensemble de ce programme. L'essentiel se trouve ici, on peut se concentrer sur la lecture de ce programme et comprendre comme il fonctionne, tout le reste, ce sont des définitions, et des initialisations, il faut bien évidemment qu'elles aient été faites correctement pour que le programme marche, mais du point de vue de la logique, on peut se concentrer sur cette partie-là du programme principal. Dans le cas que nous venons de voir, c'était relativement facile de faire l'organigramme qui correspondait à notre graphe d'état, mais dans beaucoup de cas, c'est extrêmement compliqué. Imaginez un graphe d'état avec 50 états, avec des transitions dans tous les sens, trouver l'organigramme correspondant, c'est extrêmement compliqué. Et pire que ça, quand vous croyez l'avoir trouvé, êtes-vous vraiment sûr qu'il est correct? On va donc chercher une toute autre méthode, qui va nous permettre de réaliser une machine d'état, je dirais presque, les yeux fermés. Alors, je reprends mon graphe d'état, et cette fois je vais tenter de passer directement à un programme. Alors qu'est-ce que je vais faire? Je vais essayer de faire une boucle, dans laquelle je me pose la question : est-ce que je suis dans l'état arrêt, est-ce que je dans l'état descente, est-ce que je suis dans l'état montée, et, selon que je suis dans l'un ou l'autre de ces états, je vais alors faire ce qui doit être fait au niveau des sorties, et tester ce qui doit être testé au niveau des entrées pour effectuer les transitions. Première chose, je n'ai pas du tout envie d'appeler les états, l'état 0, l'état 1, l'état 2, je préfère utiliser la symbolique qui nous est proposée par le C, avec la fonction enum. Je vous rappelle que une constante a été déclarée, une deuxième constante, une troisième constante, qui s'appellent arrêt, descente, et montée, elles vont prendre des valeurs, probablement la valeur 0, la valeur 1, et la valeur 2, mais ça n'a aucune importance pour celui qui programme, et alors ici, j'ai une variable d'état, cette variable d'état, elle va être initialisée à arrêt, au début, le système doit être arrêté au démarrage. Je trouve ensuite cette boucle infinie, notez qu'elle est totalement différente de la boucle infinie que nous avions tout à l'heure, parce que cette boucle va s'exécuter à très grande vitesse, puisque, il n'y a que quelques if, il n'y a pas d'attente, il n'y a pas de while dans ce programme, dans cette boucle while 1. Alors, je vais ensuite donc regarder si je suis à l'arrêt, il faudra faire un certain nombre de choses, à la descente, il faudra faire un certain nombre de choses, ou encore, à la montée. Entrons maintenant un peu plus dans le détail de ce qui se passe, par exemple, pour l'état descente, je dois, évidemment, m'occuper des sorties qui prennent les valeurs qui sont notées ici, et la deuxième chose qui sera à faire, ce sera de s'occuper de la transition qui quitte la descente. Les autres transitions ne nous concernent pas, mais, cette transition-là, comme elle a sa source dans la descente, elle part de descente, eh bien je dois m'en occuper, et ici il s'agit d'un test, si on est arrivé en bas, alors on va changer d'état et j'assigne état à la nouvelle valeur montée, qui donc est une constante qui a été déclarée par l'enum qui se trouve ici. Voilà maintenant l'ensemble de notre programme, remarquez que j'ai utilisé une autre syntaxe que cette succession de if qui était un petit peu lourde, c'était une excellente occasion d'essayer le switch case, avec ici les trois états possibles, Vous remarquez que je n'ai pas utilisé le default, je sais en effet que la variable état ne prendra jamais d'autres valeurs que arrêt, descente et montée, puisqu'elle est complètement contrôlée par notre programme, elle est initialisée à l'arrêt au départ, elle prend, la valeur descente, la valeur montée, ou la valeur arrêt dans le cours du programme, donc le default n'est pas nécessaire. Par contre, je mets bien en évidence la nécessité d'utiliser les break, sinon, si j'oubliais ce break, alors j'exécuterais toutes ces instructions, même dans le cas où je me trouve à l'arrêt. C'est une curiosité de cette structure switch, et on l'utilise presque toujours avec les break, qui permettent de sortir de la structure switch. Voilà ici le programme complet, qui donc fonctionne avec un Arduino, ici je l'ai programmé pour l'Energia, donc avec le MSP430 étant donné que j'ai fait le choix d'utiliser le main et de faire moi-même le while 1, je dois rajouter cette instruction mystérieuse dont nous avons déjà parlé, et dont, simplement, il faut se souvenir qu'elle est indispensable dans la plupart des programmes. Nous sommes donc parvenus à programmer notre machine d'état, avec une technique qui, cette fois, est beaucoup plus simple. Tout l'effort doit être fait pour dessiner le graphe d'état, par contre, ensuite, partir du graphe d'état pour écrire le programme, c'est tout à fait simple, il faut énumérer les états, il faut faire une structure dans la boucle principale, du type switch qui permet de choisir chacun des états, il faut ensuite actionner les sorties pour chacun des états, et il faut ensuite programmer les transitions pour chacune des transitions qui concernent chaque état.