Bonjour, nous continuons le cours : comprendre les microcontrôleurs. Nous avons déjà parlé de programmation, nous avons déjà parlé du langage C Mais aujourd'hui j'aimerais revenir un petit peu plus en détails sur certains points concernant ce langage C. Nous donnerons quelques éléments de son histoire, c'est intéressant. Nous donnerons quelques explications sur les variables et surtout sur les assignations. Nous parlerons plus en détails des structures de contrôle qu'il est évidemment essentiel de bien comprendre pouvoir faire même des programmes simples. Et finalement, je dirai quelques mots sur les procédures ainsi que le programme principal. C'est en 1972 que le langage C a été créé. Donc vous imaginez que c'est extrêmement ancien. À cette époque-là , on utilisait des minis ordinateurs qui étaient grands comme une armoire et il n'y en avait pas beaucoup. Donc la situation était très différente de la situation actuelle. Ça a été développé dans les laboratoires Bell de la grande société américaine AT&T Société de télécommunication. Donc par Dennis Ritchie. En fait c'est le résultat de l'évolution de langages plus anciens, en particulier le langage B. Donc c'est peut être l'explication de son nom, il s'appelle C ou alors C comme compilateur. Étant donné la puissance des ordinateurs de l'époque, c'est finalement un langage qui est proche de l'assembleur, qui est relativement simple. Et surtout qui a une écriture extrêmement concise. Et finalement, c'est cette simplicité et cette concision qui fait du langage C un très bon candidat comme langage de programmation pour les microcontrôleurs. C'est beaucoup plus tard que ce langage C a été standardisé sous forme d'une norme ANSI en 1989 et également en 1999. Le langage C a été l'ancêtre de beaucoup d'autres langages de programmation. Beaucoup de langages modernes s'inspirent de la syntaxe du C. Je citerais très simplement comme exemple le langage JAVA. Le C ++ est un autre langage que le langage C. Bien qu'il a énormément de points communs, mais c'est une très grande extension du C. Je signale que la communauté libre autour de Linux a un projet qui s'appelle GCC, donc GNU compiler collection, qui dispose de, d'un grand nombre de compilateurs pour un grand nombre de processeurs, et ce GCC est la base de beaucoup de compilateurs qui sont utilisés pour les microcontrôleurs, et étant donné que c'est un produit libre, open source, il est évidemment facile de l'obtenir. Je vais commencer par dire quelques mots sur les variables. Les variables c'est un concept extrêmement important. Les variables c'est des cellules dans lesquelles on va pouvoir mettre un contenu. Physiquement, ces cellules sont en mémoire vive, hm. Chaque mémoire à un nom. Ici j'ai mis le nom X. Elle a également un type c'est extrêmement important de savoir le type alors je ne vais pas parler aujourd'hui de type. Je vous dis simplement que int signifie intenger c'est-à -dire un nombre entier, mais on parlera plus en détails des types plus tard. Une variable va contenir une certaine valeur, et on va pouvoir lui assigner une nouvelle valeur. Donc, il faut bien comprendre cette ligne là comme étant la variable x va prendre le contenu qui est dans ce cas là , la valeur 12. On peut évidement mettre des valeurs sous forme de constante. On peut également, comme on l'a fait ici, hm, utiliser une autre variable. On peut même utiliser la même variable pour recalculer. Alors vous remarquez que cette ligne est une absurdité du point de vue mathématique, mais il faut bien comprendre X va prendre une nouvelle valeur. qui est la soustraction de la valeur 2 à l'ancienne valeur de X. Pour effectuer ces assignations, ces calculs, on va utiliser des opérations arithmétiques. Lorsqu'on utilise des nombres entiers, comme je l'ai fait ici, on dispose de cinq opérations arithmétiques de base, l'addition, la soustraction, la multiplication, la division entière et le reste de la division, c'est-à -dire la valeur modulo. Parlons maintenant des structures de contrôle qui sont évidement essentielle à bien maîtriser, même pour écrire un programme relativement simple. Alors on en a déjà entendu parler. J'aimerais mettre en évidence le fait qu'il y a deux catégories fondamentalement différentes Des structures qui sont liés à des tests et des structures qui sont des boucles. J'ai représenté ici des organigrammes et vous pouvez remarquer que pour un test, j'ai pris ici l'exemple du if, toutes les flèches descendent. Par contre dans les cas des boucles, j'ai pris deux exemples dont on parlera tout à l'heure, Un certain nombre de flèches, comme par exemple celle-là , remonte. C'est également le cas ici. Fondamentalement dans cette structure du if on va commencer là , on va passer et on va terminer ici, alors que dans la structure de boucle on va pouvoir revenir en arrière. Alors regardons ça un petit peu plus en détails. Regardons en détail la structure if, qui est donc le test de base en langage C. Le principe est on va tester une condition et en fonction de la réponse qui peut être oui ou qui peut être non, c'est mis en évidence par le petit rond, on va effectuer l'instruction ou au contraire on ne va pas effectuer l'instruction. Assez souvent on va utiliser une instruction qui peut être en fait, plusieurs instructions et on utilise donc accolade ouverte, accolade fermée. Alors y a deux manières de le présenter graphiquement soit on aligne les deux accolades, ouverte et fermée, soit au contraire on met l'accolade fermée en regard de la ligne où se trouve l'accolade ouverte. Alors, il faut que vous puissiez avoir l'habitude de lire ces deux manières de présenter les choses. Cette manière est peut être plus lisible. Cette manière a évidemment l'avantage de prendre moins de lignes, et parfois c'est extrêmement intéressant d'avoir moins de lignes. Nos écrans sont toujours limités en taille et plus on arrive à voir de lignes, plus notre cerveau est capable d'appréhender un nombre important de choses différentes. Donc la structure if est un test et ensuite le programme se poursuit, il faut bien comprendre ça. Alors, cette instruction, elle peut s'exécuter soit zéro fois soit une fois. Il n'y a pas d'autre possibilité. On a une variante du if qui est un test plus complet, non seulement comme avec le if, on va exécuter l'instruction, la première instruction si le test est vrai, mais on a également une autre instruction qui elle, va s'exécuter que si le test est faux. Et finalement, là aussi le programme continue L'écriture on la comprend bien. If, une condition. qui a été mise entre accolade, else, une autre condition. également mise entre accolade, donc avec le if else, la condition fausse donne aussi lieu à une instruction. On a exactement une instruction qui s'exécute dans tous les cas. soit la première, soit la seconde. Un petit exemple, essayons de regarder ce petit morceau de programme. On a un premier if avec une condition. Ici des instructions qui s'exécutent lorsque cette condition est vraie. Et ici un else, suivi d'une instruction, cette instruction if qui commence ici. Et qui va se terminer là . Alors je vais le représenter sous forme d'un organigramme, peut-être on arrivera mieux à comprendre. On voit bien qu'on a un premier if qui est marqué en noir avec la condition vraie qui donne lieu à une instruction. Et puis le else entre dans une nouvelle instruction if qui également a son test, sa condition vraie, sa condition fausse. Et finalement le tout se retrouve à cet endroit-là . C'est une structure de choix multiples qui est assez fréquemment utilisée, qui est assez pratique, vous verrez qu'il y en a une autre qui lui ressemble, qui n'est pas identique, on l'étudiera plus tard, qui s'appelle le switch case. Parlons maintenant des boucles. Dans une boucle il y a fondamentalement ce retour en arrière le fait que ça peut durer longtemps. On va pouvoir rester dans cette boucle, et ce mouvement rotatif que je suis en train de faire maintenant. C'est ce qui explique ce mot, boucle. Ici un test, suivi d'une instruction, si le test est faux, alors on termine la boucle. Voilà comme ça c'est écrit de nouveau avec la syntaxe où on aligne les parenthèses, la syntaxe où on aligne la parenthèse fermée à la ligne de la parenthèse ouverte. J'insiste sur le fait que la structure while est une boucle et qu'elle peut durer indéfiniment. Il faut donc toujours être extrêmement prudent. Parfois c'est ce qu'on recherche, parfois ça n'est pas ce qu'on recherche. Et l'instruction qui se trouve ici, elle peut ne pas s'exécuter, zéro fois. Elle peut s'exécuter une fois ou plusieurs fois. Selon, bien entendu, la condition. Est-ce que vous voyez la différence entre cette boucle, cette nouvelle boucle et la précédente? On a simplement une inversion de l'instruction et du test. Dans le while, le test était au début, dans le do while, le test est à la fin. Alors la syntaxe do suivi d'une instruction qui, généralement, est notée entre accolades, avec de nouveau les deux manières d'écrire. L'instruction qui va s'exécuter. Et pour terminer, la condition suivi d'un point-virgule. J'insiste là -dessus, c'est important. Donc la structure do while est une boucle. De nouveau, elle peut durer indéfiniment. J'insiste là -dessus c'est important. Cette fois elle peut ne s'exécuter qu'une fois ou plusieurs fois. Elle va forcément s'exécuter au moins une fois. Et c'est important de s'en souvenir. Prenons un exemple tiré de nouveau de la mécanique. On a un moteur qui entraîne une roue sans fin, qui permet de faire bouger un chariot. On va décider par exemple ici que la chariot avance dans cette direction et recule dans cette direction. Le programme que nous avons écrit, vous y voyez une boucle sans fin puis vous avez deux "if" qui permettent de faire avancer et reculer le moteur. Alors, je vous laisse quelques instants pour réfléchir à ce programme et vous posez la question : quel mouvement du chariot est effectué par cette partie de programme? Alors la réponse effectivement, c'est que le chariot fait un mouvement de va-et-vient permanent. Le permanent est lié au fait qu'on a ce "while (1)" qui est une boucle sans fin. Et effectivement, lorsqu'on termine la course à droite, on va noter ici l'interrupteur de fin de course droite, et bien on va se mettre en mode "avance". Alors que lorsqu'on est arrivé dans l'interrupteur de fin de course gauche, on va reculer et on aura effectivement les mouvements va-et-vient qui vont s'effectuer. Nous avons donc déjà vu la boucle "while" et la boucle "do while". Il existe une troisième manière d'écrire les, une boucle, c'est la structure qui s'appelle "for". Alors, ça n'est rien de fondamentalement nouveau, c'est simplement une astuce d'écriture qui permet des présentations relativement compactes et souvent assez agréables. Si vous êtes d'accord, je vais commencer par un exemple : ici, "for" on lui passe trois objets séparés par des points-virgules. Ces paramètres en quelques sortes du, de la boucle "for" sont les suivants : tout d'abord, l'initialisation. C'est une instruction qui s'exécute une fois au début de la boucle. Une seule fois et au début de la boucle. La deuxième, le deuxième paramètre, c'est une condition. C'est la condition pour que la boucle continue. Si cette condition est fausse, on va sortir de la boucle. Et finalement, le troisième paramètre, on l'appelle assez souvent l'incrémentation. En fait, c'est simplement une instruction qui s'exécute à chaque itération de la boucle, à chaque reprise au début de la boucle. Donc lorsqu'on a terminé ces instructions, si la condition est vraie, on va revenir au début et ce troisième paramètre s'exécute au moment de cette itération. Dans le cas particulier de cet exemple, on comprend très bien "i" va aller de 0 jusqu'à 10 moins 1, jusqu'à 9 et il va prendre les valeurs successives 0, 1, 2, 3, 4, et cetera. Un autre exemple, cette fois plus complet : "for i égal 0, i inférieur ou égalà 5 ; i ++)" On utilise donc de nouveau cette boucle "for" pour faire du comptage en quelque sorte. Et dans ce cas-là , on allume une LED, on attend une demi-seconde, on éteint la LED et on attend de nouveau 500 millisecondes. Combien de fois la LED va-t-elle s'allumer par ce programme? Alors la LED va effectivement s'allumer six foix. Parce que, on est bien d'accord que chaque fois qu'on exécute ces instructions, on va allumer puis éteindre la LED. Et on va l'exécuter pour la valeur 0, pour la valeur 1, pour la valeur 2, pour la valeur 3, la valeur 4 et pour la valeur 5 puisque lorsque "i" est égal à 5, on est encore inférieur ou égal à 5. La condition est encore vérifiée et la boucle peut s'exécuter une dernière fois. Donc, la réponse, 6 fois. Avec la boucle "while", le test s'effectue au début de la boucle. Avec le "do while", il s'exécute à la fin. Et il y a des cas où on serait peut-être interessé à le faire au milieu. Alors il existe une instruction très générale qui s'appelle "break" et qui permet de terminer l'exécution de la boucle englobante. Donc la première boucle dans laquelle ce "break" est contenu. Cela peut être une boucle "while", "do while" ou une boucle "for". On verra même que cela peut être aussi le cas dans la structure "switch" qu'on verra plus tard. Voilà un exemple qui illustre cette boucle généralisée. On a une série d'instructions au début de la boucle, on a une série d'instructions à la fin de la boucle, et la condition est testée au milieu de la boucle avec un "if" suivi d'un "break". Je vais maintenant parler de la dernière structure de contrôle dont nous aurons besoin. Elle est plus compliquée que les autres. Elle est même, à certains égards, un peu bizarre. Voilà ce qu'elle fait : On a un test sur une variable. On va donner donc euh, une expression qui donne comme résultat des valeurs, des valeurs numériques, 0, 1, 2, 3, des valeurs euh, discrètes. Et on va pouvoir tester les différents cas. Ici, j'ai testé le cas 1, le cas 2, le cas 3 mais je peux évidemment aussi tester le cas 1000 si je le souhaite. Et l'ordre n'a pas d'importance, c'est des tests séparés. Il s'agit donc bien d'un branchement conditionnel. En fonction de la valeur de la variable qui a été donnée au départ, et bien, on va aller dans l'instruction 1, l'instruction 2 ou l'instruction 3, et cetera. Le problème c'est que, très curieusement, après avoir exécuté l'instruction 1, on exécute l'instruction 2 et on exécute l'instruction 3. Ce qui n'est absolument pas ce qu'on veut la plupart du temps. Il se trouve que le C fonctionne de cette manière-là . Notez aussi qu'il est possible d'apprendre un cas qui est le cas par défaut. C'est-à -dire tous les cas qui ne sont pas pris en compte par les valeurs qui ont été données avec le "case". Ici, ça permet de faire une autre instruction pour tous les autres cas. Mais comme je vous le disais tout à l'heure, cette structure ne correspond pas aux besoins réels. La plupart du temps, pour le cas, le premier cas qu'on a pris, on souhaite exécuter cette instruction puis terminer. Et ne pas exécuter cette deuxième ou cette troisième instruction. Alors, il est nécessaire dans ce cas-là d'utiliser la commande "break". Certains estiment que cette manière de présenter le switch, c'est la manière standard. Bon, alors dans ce cas-là il faut ne pas oublier les "breaks". Ici, celui-là n'est pas nécessaire puisque c'est le dernier. Dans certains cas, on peut avoir des astuces particulières : si par exemple, on voulait utiliser euh, le même traitement pour le cas 1 et le cas 2, et bien on pourrait effectivement supprimer cette instruction, supprimer ce "break" et on aurait, pour le cas 1 et le cas 2, la même instruction 2 qui s'exécuterait, suivie de la sortie par le "break". Je terminerai ce chapitre en parlant de procédures. Ce serait trop long d'en parler en détail maintenant. On n'a pas besoin de savoir beaucoup de choses. Je donnerai juste les points essentiels. Une procédure a un nom. Une procédure peut recevoir des paramètres d'entrée. Un ou plusieurs. Une procédure peut rendre des paramètres en sortie. Ici, on n'en rend pas alors que dans ce cas-là , on rend un paramètre de sortie. Donc, une procédure c'est un certain nombre d'instructions qu'on a regroupées et auxquelles on a attribué un nom. Et donc la possibilité de passer des paramètres en entrée et en sortie. Voilà un exemple très concret. On va faire ici un clignotement. Ce clignotement, il va durer un certain nombre de fois avec une structure "for". Et chaque fois, on allume, on attend, on éteint, on attend. Un autre exemple : ici, on donne en entrée un nombre et on souhaite obtenir le carré du nombre. Ce carré est calculé ici. Le mot réservé "return" permet de rendre en sortie une valeur. Ici, une valeur de type nombre entier. Il se trouve que, en C, le programme principal est tout simplement une procédure particulière qui a un nom réservé qui est le nom "main". Alors on a les deux manières de le noter : parfois on voit "void main", parfois on voit "int main". Disons que sur un microcontrôleur, ce serait plus logique de prendre le "void main" pour la raison suivante : dans un microcontrôleur, le programme ne se termine jamais. Par contre, sur un ordinateur, l'habitude est de pouvoir euh, obtenir, lorsqu'un programme se termine, un code d'erreur, d'où ce "int". Alors on peut utiliser les deux, certains compilateurs sont chatouilleux et aiment bien voir ce "int", c'est la raison pour laquelle on va souvent l'utiliser mais les deux solutions sont possibles. À noter que dans cet exemple ici, j'ai écrit un programme principal, le programme "main" qui, lui, ne fait que appeler deux procédures : la procédure "setup" et, à l'intérieur d'une boucle "while", la procédure "loop". Et vous avez reconnu que c'est le système de l'Arduino. Lorsque vous n'écrivez pas le "main" dans un programme Arduino, mais que vous écrivez "setup" d'une part et "loop" d'autre part, le programme va, de lui-même, rajouter en quelque sorte cette structure pour le programme principal qui existe bel et bien évidemment, même dans une application Arduino. Évidemment, nous n'avons pas parlé de l'ensemble du langage C. C'est quand même un petit peu plus compliqué que ça, mais on a un certain nombre de, d'éléments importants : les variables et les assignations, les structures de contrôle que nous avons euh, vues d'une manière exhaustive et une petite approche des procédures dont on a souvent besoin. Avec ce qu'on a à disposition, on va pouvoir déjà faire beaucoup de programmes pour des microcontrôleurs. C'est un sous-ensemble du langage C qui est déjà suffisant pour travailler.