Bonjour, nous continuons le cours "comprendre les microcontrôleurs". Et maintenant nous allons parler des entrées-sorties et peut-être ce sera enfin l'occasion de les comprendre. Je vais commencer par vous faire un petit rappel de la manière de travailler avec les entrées-sorties avec le système Arduino. Ensuite nous essayerons de regarder en détail comment les entrées-sorties sont réalisées à l'intérieur du microcontrôleur. Nous regarderons aussi comment s'organisent les registres qui permettent d'utiliser les entrées-sorties. Et finalement, je donnerai quelques exemples. Jusqu'à maintenant, pour les entrées-sorties, nous avons utilisé la solution proposée par Arduino, ou par le système Wiring dont Arduino est issu. L'idée était de donner un numéro arbitraire aux pattes d'entrées-sorties. J'insiste sur le mot arbitraire dans le sens qu'il n'y a pas une logique liée aux microcontrôleurs. C'est plutôt une logique liée à la carte qui a été utilisée pour donner ces numéros de pattes d'entrée-sortie. Dans l'environnement logiciel, on a des procédures spécifiques qui permettent d'utiliser les entrées-sorties. Je mentionne les principales ici : pinMode qui permet par exemple ici de dire l'entrée numéro 3, je veux que ce soit une sortie, ensuite je peux l'utiliser en écrivant, en donnant toujours ce numéro, en donnant le niveau. De la même manière, l'entrée numéro 4 je veux que ce soit une entrée et je peux tester cette entrée numéro 4. Les avantages de ce système sont : une très grande simplicité, c'est aussi la possibilité d'avoir une certaine compatibilité entre des environnement à priori totallement différents. Certains pensent même qu'il s'agit de l'esperanto des microcontrôleurs. Par contre, ici, nous avons envie de comprendre ce qui se cache derrière ces pattes d'entrée-sortie. Dans tous les microcontrôleurs, les pattes d'entrée-sortie sont groupées. Elles sont groupées, assez souvent, par mots de 8 bits. Parfois par mots de 16 ou 32 bits. Et, c'est ce qu'on appelle des ports. Les ports d'entrée-sortie. Ne soyez pas effrayés de ces schémas, c'est ce que le fabricant nous donne. On voit ici que ce fabricant-là a décidé d'appeler port, port D, port B. Curieusement, il n'y a pas le port A dans ce cas-là . D'autres ont utilisé une autre terminologie. Ici, P1, avec des petits numéros. Voilà encore une autre manière d'exprimer les choses. On remarquera qu'il s'agit ici d'un processeur qui fournit des ports avec 16 bits, donc de 0 à 15. Notez aussi ici le terme GPIO, qui est extrêmement souvent utilisé. Ça signifie "general purpose input output". Donc c'est des pattes d'entrée-sortie à usage général. Donc les ports ont des noms qui sont donnés par le fabricant. Par exemple, pour les PIC ou pour les AVR, c'est les noms PortA, Port B et cetera qui sont utilisés. Dans le cas du MSP430, il y a une autre terminologie qui a été choisie par le fabricant. Les concepts sont les mêmes, les noms sont un petit peu différents. Chaque patte individuelle à l'intérieur du port à aussi un nom, qui est évidemment dérivé du nom du port. Pour les AVR, c'est par exemple, PA0, PA1, PA2 et cetera. Pour le MSP430, pour éviter de l'appeler P1,0 qui aurait pu être interprété comme P10, ils l'ont appelé P1.0, P1.1 et cetera. À l'intérieur du microcontrôleur, on trouve des registres spécialisés qui permettent de manipuler ces ports. ils ont souvent une certaine euh, similitude les uns avec les autres mais ils ne portent pas forcément les mêmes noms. Si je prends les trois registres fondamentaux dont on parlera tout à l'heure, sur les AVR, ils s'appellent PortA, PinA, DDRA, alors que sur les PIC, ils s'appelent PortA et également PortA pour l'entrée et TrisA, avec d'ailleurs une inversion bit à bit par rapport au DDRA, mais les concepts sont les mêmes. Pour les MSP430, ils vont s'appeler P1OUT, P1IN, P1DIR, d'une manière qui est très systématique et logique. Nous allons essayer maintenant d'y voir clair dans ces registres. Je rappelle une chose fondamentale, une patte d'entrée-sortie doit précisément pouvoir être à certains moments une sortie et pour une sortie on doit pouvoir décider la valeur qu'on met dans la sortie. Et cette valeur doit être mémorisée ensuite jusqu'à ce qu'une nouvelle valeur soit écrite. Il faut donc une bascule, il faut donc un registre pour mémoriser la valeur qui doit être sortie. Pour une entrée, la problématique est un petit peu différente, on doit pouvoir saisir, photographier, en quelque sorte, la valeur à un instant donné. Il faut donc ce qu'on appelle un passeur, "tri-state driver", de telle manière que la valeur qui est à l'extérieur, donc sur la patte, puisse entrer dans le microcontrôleur et être lue par le processeur. Finalement, pour qu'une même patte puisse être utilisée tantôt comme une entrée, tantôt comme une sortie, il faudra un deuxième passeur qui permettra de valider, ou de ne pas valider, la valeur de sortie. Voilà deux schémas internes d'une patte d'entrée-sortie d'un microcontrôleur. C'est des schémas qui sont un petit peu difficile à comprendre. Celui-ci ici est tout à fait détaillé, tout à fait exact et parfaitement bien dessiné mais il est tellement complet qu'on n'y comprends pas grand chose. Celui-là est simplifié et dessiné d'une manière un petit peu incompréhensible puisqu'on a à la fois des portes et des transistors, et que d'autre part, on ne comprend pas bien pouquoi, ici, ils ont mis une porte NAND et ici une porte NOR, il faut analyser la fonctionnalité souhaitée pour comprendre la raison de ces portes. Voici un schéma interne, je dirais même un schéma interne simplifié d'une patte d'entrée-sortie d'un microcontrôleur. Essayons de le comprendre. Voici ici la patte d'entrée-sortie. Je rappelle qu'une patte doit être tantôt une entrée, tantôt une sortie. Lorsqu'il s'agit d'une entrée, l'information prendra ce chemin-là , passera à travers ce passeur pour être distribuée sur le bus interne du microprocesseur, qui se trouve dans le microcontrôleur. Et c'est lorsqu'on va lire ce passeur que l'information sera prise en considération. Lorsqu'il s'agit d'une sortie, il faudra activer ce passeur. Il est dessiné d'une manière un petit peu plus épaisse que l'autre parce que c'est un passeur qui doit être capable de tirer d'avantage de courant puisque sa sortie est directement exposée à la patte du microcontrôleur. Je profite de dire que la plupart des microcontrôleurs sont capables, actuellement, de fournir approximativement 20 milliAmpères dans les deux sens comme courant de sortie. Donc les transistors associés à ce passeur doivent être de surface suffisante. Alors ce passeur, d'une part il doit être commandé pour savoir s'il est en mode haute impédance ou s'il est en mode actif. Et naturellement, la valeur doit être mise à l'entrée pour, le cas échéant, être transmise à la sortie. On a donc deux bascules. La première correspond à une bascule de direction, ici, je l'ai appelée du nom P1DIR. Lorsque j'écris, dans cette bascule, la valeur va être donnée au passeur. Si j'écris un 1 ici, le passeur sera en sortie, ma patte sera en sortie. Lorsque j'écris un 0, le passeur devient inactif, et alors, l'usage normal, c'est donc de reprendre ce chemin pour que les données puissent entrer dans le microcontrôleur. Lorsque j'ai un 1 ici, ce passeur étant actif, je peux passer une valeur sur la sortie, qui pourra être un 1 ou un 0, selon ce qu'on a écrit dans cette position, qui a pris ici le nom P1OUT. En fait, ce schéma n'est pas tout à fait complet. J'ai ajouté ici le fait qu'il est possible de relire la valeur qu'on transmet à la sortie, qu'on est capable de relire la valeur de direction qui choisit le mode de fonctionnement du passeur, ce qui facilite sérieusement la programmation. Essayons maintenant de regarder un petit peu une représentation logicielle. On a parlé de cette bascule, de ces bascules, donc de ce registre P1OUT. On a parlé de P1DIR, également un ensemble de bascules qui forment un registre, par exemple de 8 bits. Généralement, on va chercher à écrire des valeurs dans les bits de P1OUT et de P1DIR. Alors qu'on va plutôt chercher à lire des données dans P1IN, qu'on cherche donc à lire les valeurs qui se présentent sur les entrées. Comme je l'ai dit tout à l'heure, il est aussi possible de relire le registre P1OUT, de relire le registre P1DIR pour des raisons de commodité de programmation. Si j'essaie maintenant de prendre un bit particulier, par exemple ici le bit numéro 6, et de regarder qu'est-ce que m'offrent comme combinaisons les deux valeurs qui se trouvent ici. J'aurais pû évidemment prendre n'importe quel autre bit que le bit numéro 6, c'est juste un exemple. Le "Dir" peut prendre la valeur 0 ou la valeur 1. Le "Out" peut prendre les valeurs 0 ou les valeurs 1. Et je vais, ici, écrire le rôle des pattes dans ces différentes combinaisons. Alors, on se souvient que le registre "Dir", P1DIR par exemple, permet de dire si c'est une entrée, est-ce qu'il est à 0, ou au contraire si c'est une sortie lorsqu'il est à 1. Par contre, le registre P1OUT n'a pas véritablement de signification lorsque "Dir" est à 0 puisque le passeur de sortie est inactif. Par contre, lorsque "Dir" est à 1, alors le "Out" va être transmis à l'extérieur : on aura une sortie à 0 lorsque le bit correspondant de "Out" est à 0. La même chose pour la valeur 1. Il est essentiel de se souvenir de ce tableau parce que on va, en fait, tout le temps l'utiliser lorsqu'on va choisir le rôle de patte d'entrée-sortie et leur affecter des valeurs. C'est la raison pour laquelle je l'ai dessiné une deuxième fois ici. On est donc prêt maintenant à utiliser ces registres en C. On va utiliser l'assignation. Je vais placer dans le registre P1DIR, dans le registre P1OUT, une valeur, que j'ai ici bêtement écrite en binaire. Qu'est-ce que je veux dire par le fait que j'ai mis ici des 1 à ces endroits-là et des 0 aux autres endroits? C'est que je veux que cette patte-là soit une sortie et que cette patte-là soit une sortie. Maintenant, avec cette instruction, pour ces deux pattes qui sont en sortie, je mets la valeur 1 pour la patte de puissance 6, je mets la valeur 0 pour la patte de puissance 0. À noter que ces autres valeurs n'ont pas de signification puisque quelle que soit la valeur de "Out", lorsque le "Dir" correspondant est à 0, la patte est une entrée et à ce moment-là , P1OUT n'a plus de rôle à jouer. Donc les deux choses importantes que je dis dans cette ligne-là , c'est seulement que ce bit est à 0, c'est une sortie à 0, que ce bit est à 1, c'est une sortie qui est à 1. Qu'est-ce qu'il va se passer maintenant pour une entrée? On cherche à tester une entrée, on va donc utiliser P1IN. Et ici, faute de savoir comment m'y prendre, j'ai maladroitement écrit "if" P1IN est égal à cette valeur. En fait, je souhaitais savoir s'il y avait un 1 à cet endroit-là . Mais cette instruction est visiblement fausse parce que dans ce cas-là , non seulement j'exige un 1, ici, dans le bit de puissance 0, 1, 2, 3, mais j'exige aussi et à priori ce n'est pas ce que je voulais, un 0 ici, un 0 là , un 0 là et cetera. Visiblement, cette manière de travailler ne convient pas. Il nous manque deux choses : il nous manque l'indépendance des bits et en plus, je dirais, il nous manque une bonne lisibilité. Ce sera le sujet du prochain chapitre, nous apprendrons à écrire proprement ces manipulations de bits à l'intérieur de registres en C. Nous avons donc regardé aujourd'hui le schéma interne d'une patte d'un microcontrôleur, et nous avons appris à reconnaître ces registres : le registre de direction, le registre de sortie et le registre d'entrée, qui permet de lire la valeur.