Dans le domaine des systèmes programmables, on trouve des ports d'entrées‑sorties numériques (en anglais, digital input output port) principalement sur :

  • les cartes de développement à microcontrôleur (Arduino, ESP8266, ESP32, Teensy, etc.) ;
  • les nano‑ordinateurs monocartes (Raspberry Pi, BeagleBone, etc.) ;
  • les ordinateurs équipés d'un port parallèle (technologie obsolète).

La technologie matérielle des ports d'entrées‑sorties numériques est plus complexe qu'on pourrait naïvement le penser. Elle dépend du type de machine utilisée.

Il en va de même pour la syntaxe de codage des opérations d'entrées‑sorties. Néanmoins, les notions fondamentales sont souvent les mêmes :

  • on procède en logique tout‑ou‑rien avec deux niveaux de tension discrétisés BAS et HAUT ; la tension sur une broche du port d'entrées‑sorties peut donc être représentée par une variable booléenne ;
  • les broches du port peuvent être employées comme entrée ou sortie, mais pas les deux en même temps ;
  • les opérations de base sur une broche sont la lecture et l'écriture de son niveau logique de tension.

Ce chapitre présente exclusivement la technologie et la syntaxe de codage Arduino, dont les fonctions spécifiques de haut niveau (pinMode, digitalWrite, etc.) ont apporté d'énormes progrès en termes de lisibilité du code et de simplicité de mise en œuvre. Elle constitue aujourd'hui une référence en programmation des cartes à microcontrôleur.

Pour poursuivre l'apprentissage de la programmation procédurale dans le contexte des cartes Arduino et compatibles, l'emploi de ces fonctions d'entrées‑sorties de haut niveau doit être maîtrisé. Dans cet objectif, on abordera dans l'ordre :

  • des rappels d'électronique sur les notions de niveau logique de tension et de potentiel de référence ainsi que les spécificités matérielles et logicielles de la technologie Arduino ‑ Atmel ;
  • la configuration des broches d'entrées‑sorties d'une carte Arduino via la fonction pinMode ;
  • la gestion du niveau logique d'une broche d'entrées‑sortie via la fonction d'écriture digitalWrite et la fonction de lecture digitalRead, avec en particulier la détection d'un front montant ou descendant.

Le code source de ces fonctions Arduino est consultable dans le fichier wiring_digital.c G mais il est complexe et difficile à comprendre, même pour un étudiant motivé. En effet, il fait appel à plusieurs macro‑définitions codées dans le fichier d'en‑tête Arduino.h G – ces dernières faisant appel à d'autres macro‑définitions codées dans des fichiers d'en‑tête de la bibliothèque libc spécifique au microcontrôleur utilisé (par exemple le fichier avr/pgmspace.h G pour les microcontrôleurs Atmel à cœur AVR – cf. chap. C1‑III ).

Rappels d'électronique

Dans cette section, il n'est pas question d'exposer des connaissances approfondies, mais simplement des rappels – voire des rudiments pour les étudiants qui n'auraient pas étudié l'électronique avant d'aborder la programmation. Pour tout complément, on pourra consulter ce chapitre d'introduction à l'électrocinétique  de Science de l'ingénieur.

Potentiels de référence

Sur une carte électronique, le niveau de tension ou potentiel d'une broche donnée est sa différence de potentiel avec la broche GND (de l'anglais ground, c'est‑à‑dire « Terre », en fait la masse) de l'alimentation de la carte. Cette dernière est considérée comme ayant en permanence le potentiel bas 0 V.

Par ailleurs, il existe au moins un autre potentiel remarquable, dit potentiel haut dont la stabilité est assurée par un régulateur de tension. La valeur de ce potentiel dépend de la technologie des semi‑conducteurs employée :

  • En technologie dite TTL W (transistor transistor logic), il vaut 5 V  (± 10%).
  • On emploie le sigle VCC pour repérer ce potentiel.
  • En technologie dite CMOS W (complementary metal oxide semi‑conductor), il peut aller de 3 à 18 V selon les applications (ce qui constitue un avantage indéniable de cette technologie).
  • On emploie le sigle VDD pour repérer ce potentiel.

Très souvent, la technologie CMOS est implémentée avec le potentiel haut fixé à 5 V, ce qui apporte une compatibilité avec les composants TTL.

Par « abus de langage », il arrive qu'on emploie le sigle VCC pour désigner le potentiel haut de façon générique, c'est‑à‑dire quelle que soit la technologie des circuits.

Notion de niveau logique de tension

Sur une carte électronique, la tension d'une broche peut être interprétée en logique tout‑ou‑rien (TOR) par rapport aux potentiels de référence de la technologie employée (TTL ou CMOS), moyennant des intervalles d'interprétation très larges. On parle alors de niveau logique de tension.

Pour éviter les confusions avec les potentiels de référence, on peut noter en majuscules ces deux niveaux logiques de tension, respectivement BAS – en anglais, LOW – et HAUT – en anglais, HIGH –.

Ainsi, en technologie TTL W :

  • le niveau logique de tension HAUT est spécifié comme étant compris entre la limite inférieure d'interprétation 2 V et le potentiel haut VCC = 5 V ;
  • le niveau logique de tension BAS est spécifié comme étant compris entre le potentiel bas 0 V et la limite supérieure d'interprétation 0,8 V.

L'intervalle compris entre les deux limites d'interprétation 0,8 et 2 V est considéré comme non fiable en termes de comportement des circuits. La technologie doit garantir qu'aucune broche d'un circuit ne présente durablement une valeur de tension dans cet intervalle.

Technologie Arduino – Atmel

Schéma‑bloc d'une broche d'entrée‑sortie

Le schéma‑bloc ci‑dessous illustre l'implémentation matérielle d'une broche Pxn – c'est‑à‑dire la broche nº n du port d'entrée‑sortie numérique x (B, C…) du microcontrôleur Atmel ATmega328p, lequel équipe notamment les cartes Arduino Uno et Nano (cf. chap. C1‑III ).

Ce schéma consiste principalement en trois registres 1-bit implémentés par des bascules D W :

  1. le registre DDxn qui mémorise le mode configuré de la broche en entrée (bit à 0) ou en sortie (bit à 1) ;
  2. le registre PORTxn qui mémorise la valeur (c'est‑à‑dire le niveau logique 0 ou 1) d'écriture en sortie ; ce registre dispose également d'un circuit de lecture ;
  3. le registre DDxn qui mémorise la valeur (c'est‑à‑dire le niveau logique 0 ou 1) de lecture en entrée ;
    • un trigger de Schmitt assure la discrétisation en niveaux logiques du signal de tension présent sur la broche ;
    • le registre est synchronisé avec l'horloge du microcontrôleur (sa valeur est figée entre deux tops d'horloge).

De plus, ce schéma comprend un résistor de pull‑up W d'environ 20 kΩ pour référencer le niveau de tension de la broche au potentiel de référence haut. Elle est activée ou déactivée par le signal PUD.

Liaisons des broches d'une carte au microcontrôleur

Sur une carte Arduino, les connecteurs électriques du port d'entrées‑sorties numériques (des broches de type « Dupont » mâles ou femelles, au pas d'écartement standard de 2,54 mm – cf. photo ci‑contre) sont directement reliés aux broches des ports d'entrées‑sorties correspondantes du microcontrôleur.

Ainsi, conformément à son schéma électronique, on a les correspondances ci‑dessous pour la carte Arduino Uno R3 A, qui est dotée d'un microcontrôleur Atmel ATmega328P :

Carte Microcontrôleur
port numérique
broches 0 à 7
port D
broches PD0 à PD7
port numérique
broches 8 à 13
port B
broches PB0 à PB5
port analogique
broches A0 à A5
port C
broches PC0 à PC5

Dans un premier temps, il n'est pas nécessaire de connaître ce genre de correspondance pour programmer une carte Arduino : quel que soit le modèle, les numéros de broches de la carte sont pris en charge par les fonctions Arduino de lecture et d'écriture sur les ports de la carte (cf. infra ) ainsi que des pseudo‑constantes qui ciblent directement les différentes broches des ports du microcontrôleur.

Pour plus de détails, on pourra consulter le schéma électronique de la carte disponible au lien suivant A.

  1. En règle générale, les ports d'un microcontrôleur sont très polyvalents. En particulier :
    • le port C du microcontrôleur Atmel ATmega328P est « nominalement » dévolu aux entrées analogiques  A0 à A5 ;
    • mais il fait aussi fonction de port numérique, avec les numéros de broches 14 à 19 (numéros qui ne sont pas inscrits sur la carte).
    Cela qui porte à 20 le nombre total d'entrées‑sorties booléennes qu'une carte Arduino Uno R3 peut gérer (cf. infra ).
    NB : en codage, les identificateurs A0 à A5 sont définis comme des pseudo‑constantes dans le fichier d'en‑tête pin_arduino.h spécifique pour la plupart des cartes à cœur AVR G.
  2. Ces aspects matériels sont plus complexes pour les cartes à modules ESP car, pour chaque broche, on a deux liaisons :
    • l'une entre la broche de la carte et celle du module ;
    • l'autre entre la broche du module et celle du microcontrôleur.
    Plus de détails sont donnés infra pour les cartes à module ESP8266  et celles à module ESP32 .

Niveaux logiques de tension d'entrée‑sortie du microcontrôleur

Dans les microcontrôleurs employés par les cartes Arduino, les circuits d'entrées‑sorties sont réalisés en technologie CMOS mais avec les potentiels de référence suivants A :

  • 0 ‑ 5 V sur microcontrôleurs à cœur AVR (cas des cartes les plus usuelles : Uno, Mega, Nano…), avec les intervalles d'interprétation :
    • de 0 à 1,5 V pour le niveau logique BAS,
    • de 3 à 5 V pour le niveau logique HAUT ;
    ces niveaux étant compatibles avec la technologie TTL.
  • 0 ‑ 3,3 V sur microcontrôleurs à cœur ARM (cartes Due, Zero…), avec les intervalles d'interprétation :
    • de 0 à 1 V pour le niveau logique BAS,
    • de 2 à 3,3 V pour le niveau logique HAUT.
    La faiblesse de la valeur du potentiel de référence VDD sur ces carte limite encore davantage les possibilités de mettre en œuvre des sorties pilotant directement des composants, notamment les relais. Il faut souvent recourir à des transistors d'amplification.

Sur les cartes Arduino, le potentiel de référence HAUT employé sur le port numérique d'entrées‑sorties ne peut pas être modifié, même si la carte dispose par ailleurs d'une broche d'alimentation avec un autre potentiel de référence. En effet :

  • il y a bien une broche 3.3V sur les cartes Uno, Nano et Mega mais fondamentalement, elles opèrent avec le potentiel HAUT VDD = 5 V ;
  • il y a bien une broche 5V sur les cartes Due et Zero mais fondamentalement, elles opèrent avec le potentiel HAUT VDD = 3,3 V ;

Pseudo‑constantes logiques LOW et HIGH

Pour rendre les programmes simples à coder et plus lisibles, le fichier Arduino.h définit deux pseudo‑constantes spécifiques LOW et HIGH A qui ont respectivement les correspondances avec les valeurs booléennes 0 et 1 et les constantes logiques false et true du C++, ce que récapitule le tableau ci‑dessous.

Niveau de
tension
pseudo‑constante
Arduino
Valeur
booléenne
Constante
logique
BAS LOW 0 false
HAUT HIGH 1 true

Dans la pratique, il est alors courant de faire l'amalgame entre une pseudo‑constante et sa valeur booléenne correspondante.

Configuration des modes d'entrée‑sortie d'une broche

Modes et particularités des broches numériques d'une carte Arduino

Les broches du port numérique d'une carte Arduino sont polyvalentes : elles peuvent aussi bien être employées en entrée ou en sortie.

En revanche, ces deux modes sont exclusifs l'un de l'autre : une broche ne peut pas simultanément jouer le rôle d'entrée et de sortie.

Par défaut, toute broche du port numérique est configurée en entrée, avec un potentiel électrique flottant, c'est‑à‑dire non référencé par un résistor interne.

Par ailleurs, certaines broches ont des usages particuliers dont il importe de prendre connaissance dès à présent. À titre d'exemple, sur une carte Arduino Uno R3, c'est notamment le cas des broches détaillées ci‑dessous.

  • Les broches 0 et 1 sont respectivement les connecteurs RX et TX de la liaison série de la carte. Elles sont donc affectées par le protocole de communication UART (cf. chap. C3‑X ), donc durant la phase de téléversement du programme, ou l'utilisation du moniteur série ; elles ne doivent alors être reliées à aucun dispositif (résistor, condensateur, etc.) qui pourrait perturber les signaux transmis.
  • Mais en dehors de ces contextes, leur exploitation comme entrée‑sortie booléenne reste possible.
  • Les broches 3 5 6 9 10 11 sont modulables en sortie (PWM, pulse width modulation – cf. chap. C3‑VII ), ce qui est indiqué par le symbole  inscrit avant leur numéro sur la carte (cf. la figure ci‑dessus). La technique de modulation permet de faire varier la valeur moyenne du signal tension sur une broche dans l'intervalle 0 ‑ 5 V.
  • La broche 13 est raccordée à la led intégrée  – cf. le symbole L sur la figure ci‑dessus et le chap. C1‑III . De ce fait, elle ne peut jouer le rôle d'entrée qu'avec une restriction : pour référencer son potentiel, il faut impérativement employer un résistor externe, et non pas un résistor de pull‑up interne à la carte (cf. supra ).

Lorsqu’on souhaite utiliser la led intégrée de la carte, il est préférable dans le code de désigner son numéro par la pseudo‑constante LED_BUILTIN – cette dernière étant définie dans le fichier pin_arduino.h.

En effet, ce codage sera portable sur une autre carte Arduino, car la pseudo‑constante Arduino LED_BUILTIN est définie spécifiquement pour s'adapter au brochage de chaque carte (Uno, Nano, Mega, Due…).

En revanche, la pseudo‑constante LED_BUILTIN n'est pas définie de la même manière pour toutes les cartes tierces compatibles avec le framework Arduino (ESP8266 , ESP32 , etc.).

Schéma de brochage d'une carte de développement

Plus généralement, on peut découvrir toutes les fonctionnalités des broches d'une carte électronique de développement en récupérant dans la documentation technique son schéma de brochage – en anglais, pinout diagram.

Cas de la carte Arduino Uno R3

Un schéma de brochage de la carte Arduino Uno, restitué partiellement ci‑dessous, est disponible sur le site du constructeur A.

À chaque broche sont associés un ou plusieurs numéros avec un code de couleurs pour distinguer sa ou ses différentes fonctionnalités. À titre d'exemple, on peut faire les observations suivantes :

  • Les broches 18 et 19 du port numérique de la carte sont en fait respectivement reliées aux broches PC4 et PC5 du port C du microcontrôleur Atmel ATMega382P.
  • Attention ! Sur la figure, les numéros des broches des ports numériques sont précédées du préfixe « D » pour digital, mais ce dernier ne doit pas être codé dans les programmes.
  • De plus, ces broches constituent respectivement celles dites SDA (Serial data) et SCL (Serial clock) du bus SPI (cf. chap. C4‑VII ) embarqué dans le microcontrôleur.
  • Enfin, ces broches sont des duplications de celles situées en bas sur la rangée de gauche, et ce faisant, elles correspondent aussi respectivement aux broches A4 et A5 du port analogique de la carte.

Pour s'en convaincre, on peut simuler sur Tinkercad le montage représenté sur la figure ci‑contre de test de la broche 18 (numéro non inscrit sur la carte) employée comme sortie booléenne pour alimenter une led.

Le programme associé consiste simplement à coder la valeur 18 (ou encore A4) comme premier argument dans l'appel des fonctions pinMode et digitalWrite (que l'on va détailler plus loin dans le chapitre).

void setup()
{
  pinMode(18, OUTPUT);
  digitalWrite(18, HIGH);
}
  
void loop() {}

On peut alors constater que la led s'allume comme prévu.

Cas des autres cartes Arduino

L'exploitation des entrées‑sorties numériques sur les autres modèles de cartes Arduino ne présente pas de différences significatives avec ce qui vient d'être exposé pour une carte Uno R3 ; cf. par exemple ci‑dessous le diagramme de brochage d'une carte Arduino Mega A.

  • L'étiquetage des broches est le même ; faute de place, il est juste moins visible et moins détaillé sur les cartes miniatures comme la Nano.
  • Dans tous les cas, il suffit de se rapporter au diagramme de brochage du modèle de carte, qu'on trouve facilement sur le site du constructeur.
  • En cas de doute sur l'existence de tel ou tel identificateur, on peut se rapporter au fichier d'en‑tête pin_Arduino.h spécifique à tel ou tel modèle de carte, disponible sur GitHub ; par exemple, celui pour une Mega G.
  • Toutefois, attention ! Pour une carte Due, le fichier pin_Arduino.h est vide mais renvoie à un autre nommé variant.h G.

Cas d'une carte à module ESP8266

Un schéma de brochage d'une carte de développement de type NodeMCU à module ESP8266‑12E est donné ci‑dessous et à ce lien .

Toutefois, l'exploitation de ce schéma est nettement plus complexe que celui d'une carte Arduino, d'autant plus que sur la carte elle‑même, faute de place, les inscriptions sont très limitées et la numérotation peut prêter à confusion. Néanmoins, on peut faire les observations suivantes, vérifiées pour le modèle SBC‑NodeMCU de l'assembleur Joy‑It (cf. chap. C1‑III ).

  • Globalement, le module ESP‑12 embarqué sur la carte (ici, de type ESP‑12E) possède 17 broches numériques numérotées de 0 à 16. Ces numéros GPIO  – pour general‑purpose input output – ne sont pas inscrits sur la carte mais peuvent être directement utilisés comme argument des fonctions Arduino pinMode, digitalWrite, etc.
  • Le module comporte aussi 1 broche analogique ADC qui n'est pas polyvalente.
    Toutes ces broches sont reliées à celles de la carte, soit directement, soit via divers circuits élémentaires.
  • Sur la rangée de droite de la carte, les broches du port numériques font l'objet d'une numérotation spéciale avec le préfixe D (pour digital). Dans le code d'un programme, cette numérotation peut être utilisée pour identifier les broches comme arguments des fonctions Arduino grâce à la définition de constantes déclarées dans le fichier d'en‑tête pin_arduino.h spécifique aux cartes à module ESP‑12 (cf. la capture d'écran ci‑contre et le lien suivant G). Les valeurs de ces constantes correspondent aux numéros GPIO indiqués sur les schémas ci‑dessus.
  • Sur cette rangées, la plupart des broches ont de multiples fonctionnalités qui ne sont pas utilisables simultanément. Cela restreint leur possibilités d'emploi en tant qu'entrées‑sorties numériques.

    En particulier, les broches TX et RX, correspondant respectivement aux numéros 1 et 3 GPIO sont dévolues à la liaison série de la carte avec le poste de travail. Comme sur une carte Arduino (cf. supra ), elles ne doivent être reliées à aucun dispositif durant le téléversement du programme et l'exploitation du moniteur série.

    En définitive, seules les broches D1 D2 D5 D6 D7 sont réellement polyvalentes. Pour plus de détails, on pourra se reporter au tableau dressé à ce lien .

  • La carte possède deux led intégrées qui sont toutes les deux de couleur bleu et câblées en logique négative (elles sont allumées lorsque leur broche associée est au niveau logique LOW) :
    • L'une est embarquée sur le module ESP‑12 (à droite de l'antenne Wi‑Fi) et associée à la broche D4 ; elle peut aussi être identifiée par la pseudo‑constante LED_BUILTIN.
    • L'autre est embarquée sur la carte (à gauche du circuit CP1202) et associée à la broche D0 ; elle peut aussi être identifiée par la pseudo‑constante LED_BUILTIN_AUX.
  • La carte possède également deux boutons‑poussoirs situés de part et d'autre du connecteur USB (cf. la figure ci‑dessus).
    • À gauche, le bouton RST (pour reset) qui permet, comme sur une carte Arduino, de réinitialiser l'exécution du programme utilisateur (cf. chap. C1‑III ). Il est relié à la broche RST juste à côté sur la rangée de gauche.
    • À droite, le bouton FLASH est prévu pour divers usages spécifiques de téléversement.
      • Il permet de forcer le téléversement du programme utilisateur durant la phase de connexion avec le poste de travail (cf. ce lien , section Troubleshooting).
      • Il permet aussi, durant la phase de démarrage de la carte, de passer en mode téléversement du bootloader W.
      Ce bouton est relié à la broche D3 du port numérique. Il est donc possible d'utiliser ce bouton comme dispositif d'entrée dans un programme.
    Ces deux boutons sont l'un et l'autre câblés en logique négative – autrement dit, leur broche associée est par défaut au niveau logique HIGH et un appui sur le bouton provoque un front descendant du signal de tension sur la broche.
  • La rangée de gauche comporte diverses broches dont seulement 2 peuvent être utilisées comme entrées‑sorties numériques :
    • Tout en haut se trouve la seule broche d'entrée analogique A0 ; elle ne peut pas être utilisée comme broche numérique.
    • Juste en dessous se trouvent deux broches RSV – pour « reserved ». En principe, elles ne sont reliées à aucun dispositif et n'existent que pour des questions de dimensions (form factor). Toutefois, c'est la broche RSV voisine de A0 est reliée à la broche ADC du module ESP‑12 ; elle permet de réaliser un pont diviseur de tension pour étendre l'échelle de tension analogique à environ 3,1 V que l'on peut appliquer sur la broche A0 de la carte.
  • On trouve ensuite deux broches SD2 et SD3 (pour serial data) qui sont reliées respectivement aux broches 9 et 10 du module module ESP‑12. Elles peuvent être utilisées comme entrées‑sorties numériques.
  • Ensuite, les quatre broches CLK SD0 CMD SD1 sont reliées respectivement aux broches 6 7 11 8 du module ESP‑12. Mais elles ne peuvent pas être utilisées comme entrées‑sorties numériques car elles sont dévolues au bus de communication SPI qui relie le microcontrôleur ESP8266 à la puce de mémoire flash embarquée dans le module ESP‑12.
  • Remarque : il faut ne pas confondre ces quatre broches avec celles sur l'autre rangée, D5 à D8 qui permettent de mettre en œuvre un bus SPI pour connecter la carte à toutes sortes de dispositifs (capteurs, écran, etc.)
  • Enfin, les broches GND 3V3 VIN etc. sont dédiées à l'alimentation des montages et/ou de la carte, ainsi qu'à la réinitialisation du programme. Elles sont équivalentes à celles que l'on trouve sur une carte Arduino.

Cas d'une carte à module ESP32

Un schéma de brochage d'une carte de développement de type NodeMCU à module ESP32 (ici, de type ESP‑32‑WROOM) est donné ci‑dessous et à ce lien .

Comme pour une carte à module ESP8266, l'exploitation de ce schéma est nettement plus complexe que celui d'une carte Arduino. Et pour ne rien simplifier, il existe aussi des différences non négligeables avec ce que l'on vient d'exposer pour les cartes à module ESP8266. Les observations qui suivent sont vérifiées pour le modèle NodeMCU‑ESP32 de l'assembleur Joy‑It (cf. chap. C1‑III ).

  • Globalement, le module ESP32 (de type ESP‑32‑WROOM) embarqué sur la carte possède 32 broches numériques numérotées de 0 à 39, avec quelques numéros absents (20, 24, 28 à 31, 37 et 38). C'est la numérotation GPIO  – general‑purpose input output qui, comme pour un module ESP8266, peut être directement utilisée pour coder les arguments des fonctions Arduino pinMode, digitalWrite, etc.
  • Pour la plupart, ces broches sont très polyvalentes : 16 d'entre elles peuvent être utilisées comme entrées analogiques. C'est pourquoi il n'est pas pertinent de faire une distinction entre « port numérique » et « port analogique » sur une carte à module ESP32 ; ici, il est vraiment préférable de parler de GPIO (general purpose input output).

    Toutefois, du fait que la carte comporte seulement 30 broches en tout (en comptant celles d'alimentation), on a 7 broches GPIO qui ne sont pas reliées à celles de la carte :
    • la broche 0 ;
    • les broches 6 à 11, sachant que de toute façon, elles ne pourraient pas être utilisées comme entrées‑sorties numériques ou analogiques car elles sont dévolues au bus de communication SPI qui relie le microcontrôleur ESP32 à la puce de mémoire flash embarquée dans le module ESP‑32‑WROOM.
  • Sur la carte, à l'exception de quelques broches particulières comme celles type « TX » ou « RX », les broches GPIO sont toutes libellées avec un préfixe D suivi de leur numéro. Et attention ! Contrairement à une carte à module ESP8266 :
    • il n'y a pas de numérotation spéciale, c'est bien le numéro GPIO de chaque broche qui est inscrit sur la carte ;
    • dans les programmes, il faut ne pas coder le préfixe D pour identifier une broche comme argument de fonction Arduino ; en effet, le fichier d'en‑tête pins_arduino.h G ne définit aucune constante ou pseudo‑constante préfixée par D.
    Parmi ces broches, seulement 18 sont réellement polyvalentes. En particulier, il faut tenir compte du fait que :
    • les broches TX0 et RX0 (respectivement 1 et 3) sont dévolues à la liaison série avec le poste de travail ;
    • les broches D34 35 VP VN (respectivement 34 35 36 39), peuvent être utilisées seulement comme entrées.
    Pour plus de détails, on pourra consulter le tableau au lien suivant .
  • La carte possède deux led intégrées, une rouge et une bleue (cf. la figure ci‑contre).
    • La led rouge (à gauche) –  qu'on pourrait libeller « ON » – signale la mise sous tension de la carte. Elle n'est reliée à aucune broche du microcontrôleur et ne peut donc pas être utilisée dans un programme.
    • La led bleue (à droite) est câblée en logique positive à la broche D2 et peut donc être utilisée en programmation. Toutefois, elle n'est référencée par aucune constante ou pseudo‑constante dans un fichier de bibliothèque (l'identificateur LED_BUILTIN n'est pas défini pour carte à module ESP32).
  • La carte possède également deux boutons‑poussoirs situés de part et d'autre du connecteur USB (cf. la figure ci‑dessus).
    • À gauche, le bouton EN (pour enable) permet de réinitialiser l'exécution du programme utilisateur – autrement dit, la fonctionnalité reset, comme sur une carte Arduino ou à module ESP8266. Il est relié à la broche EN tout en haut de la rangée de gauche.
    • À droite, le bouton BOOT est prévu pour divers usages spécifiques de téléversement.
      • Il permet de forcer le téléversement du programme utilisateur durant la phase de connexion avec le poste de travail Y.
      • Il permet aussi, durant la phase de démarrage de la carte, de passer en mode téléversement du bootloader W.
      Ce bouton est relié à la broche GPIO 0 du module ESP32, mais comme cette broche n'est pas déployée sur la carte, ce bouton ne peut pas être utilisé comme dispositif d'entrée dans un programme.
    Comme sur une carte à module ESP8266, ces deux boutons sont l'un et l'autre câblés en logique négative – autrement dit, leur broche associée est par défaut au niveau logique HIGH et un appui sur le bouton provoque un front descendant du signal de tension sur la broche.

Configuration du mode d'une broche

En règle générale, avant tout emploi d'une broche du port numérique d'une carte Arduino ou compatible, il est vivement recommandé de configurer son mode d'entrée‑sortie.

En effet, un oubli peut être la source de dysfonctionnements insidieux du programme, typiquement :

  • l'éclat lumineux anormalement faible d'une led ;
  • le fonctionnement erratique d'un bouton‑poussoir…

Et même si la broche est utilisée comme entrée non référencée qui est le mode par défaut, on gagne en lisibilité du programme en codant explicitement cette configuration.

Pour configurer le mode d'une broche, on code une instruction appelant une fonction spécifique Arduino de la forme :
pinMode(pin, mode);  A

  • l'expression à valeurs entières positives pin déterminant le numéro de la broche de la carte ;
  • l'expression à valeurs entières positives mode déterminant la configuration – entrée ou sortie – choisie via l'une des trois pseudo‑constantes INPUT OUTPUT ou INPUT_PULLUP définies dans le fichier Arduino.h  (elles valent respectivement 0, 1 et 2).

Ces deux arguments sont attendus de type uint8_t (cf. chap. C3‑II ). De plus, la fonction pinMode est de type void : elle ne retourne aucune valeur.

Voyons maintenant les valeurs à coder pour l'argument mode.

  1. La pseudo‑constante OUTPUT configure la broche en sortie. Cette dernière peut alors être commandée en tension en tout‑ou‑rien (TOR).
  2. Un câblage typique de led reliée à la broche 7 est schématisé ci‑contre :

    • lorsque la broche sera mise au potentiel bas 0 V (commandé par l'écriture sur la broche de la pseudo‑constante LOW), la led sera éteinte ;
    • lorsque la broche sera mise au potentiel haut VDD (commandé par l'écriture sur la broche de la pseudo‑constante HIGH), la led sera allumée.
    De plus, si elle en possède la faculté, la broche peut aussi être commandée en modulation PWM (cf. chap. C3‑VII ), par exemple pour faire varier la luminosité d'une led .
  3. La pseudo‑constante INPUT configure la broche en entrée – et c'est le mode de configuration par défaut. Si la broche n'est reliée à aucun dispositif, elle est le siège d'un potentiel quasi‑nul mais fluctuant au gré des variations du champ électromagnétique ambiant. Pour remédier à ces fluctuation, il est nécessaire de référencer la broche par un résistor externe relié :
    • au potentiel bas 0 V – et on parle alors d'un résistor de pull‑down W ;
    • au potentiel haut VDD – et on parle alors d'un résistor de pull‑up (cf. infra).
    Avec un résistor de pull‑down, le bouton opère en logique positive (poussoir au repos ⇒ niveau logique LOW, poussoir en appui ⇒ niveau logique HIGH).

    Un câblage typique de bouton‑poussoir en logique positive relié à la broche 8 est schématisé ci‑contre :

    • Tant que le bouton restera au repos, la broche sera au potentiel 0 V (c'est‑à‑dire, le niveau logique LOW).
    • Lors d'un appui sur le bouton, la broche sera au potentiel VDD (c'est‑à‑dire, le niveau logique HIGH).
  4. La pseudo‑constante INPUT_PULLUP configure la broche en entrée en la référençant au potentiel haut VDD  de la carte via un interne de pull‑up (cf. supra ). Le bouton opère alors en logique négative (repos ⇒ HIGH, appui ⇒ LOW). Ce mode de fonctionnement présente l'intérêt de pouvoir distinguer les deux cas de figure où le bouton est :
    • au repos (l'entrée est à VDD) ;
    • non alimenté (l'entrée est à 0 V).

    Un câblage typique de bouton‑poussoir en logique négative avec recours à un résistor de pull‑up interne relié à la broche 8 est schématisé ci‑contre :

    • tant que le bouton sera au repos, la broche restera au potentiel VDD (c'est‑à‑dire, le niveau logique HIGH) ;
    • lors d'un appui sur le bouton, la broche passera au potentiel 0 V (c'est‑à‑dire, le niveau logique LOW).

  1. Le référencement de potentiel de la broche commandée d'un bouton est indispensable. En son absence, la broche se comporte comme une antenne qui capte le champ électromagnétique ambiant et prend un niveau logique aléatoire, provoquant des comportements indéterminés (cf. TP P1‑3 ).
  2. L'application Tinkercad simule ces phénomènes.
  3. Lorsqu'on recourt à un résistor de pullup interne pour référencer une broche, il est nécessaire de temporiser d'au moins 5 ms l'exécution du programme après l'instruction de configuration de la broche pour éviter des phénomènes transitoires indésirables. Typiquement, on utilisera la fonction delay (cf. chap. C2‑IX ) comme par exemple ci‑dessous :
  4.   pinMode(BUTTON_PIN, INPUT_PULLUP);   
      delay(5); //let the internal resistor to be effectively activated 
    
  5. Avec des composant d'entrée tout‑ou‑rien (bouton, capteur, etc.), on recommande de raccorder entre les deux contacts électriques un condensateur anti‑rebond afin de lisser les phénomènes indésirables de rebond électrique et de surtension qui se produisent lors des commutations (cf. TP P1‑3 ).
  6. Néanmoins, le comportement d'un tel condensateur ne semble pas bien simulé dans l'environnement Tinkercad ; il est donc préférable de s'en passer dans cet environnement.
  7. En règle générale, la configuration des broches d'entrées‑sorties est codée une fois pour toute dans la fonction setup, du moins tant que les composants externes branchés sur le port numérique de la carte ne sont pas susceptibles de changer durant l'exécution du programme.
  8. Néanmoins, rien n’interdit, après une première instruction de configuration, de coder des modifications du mode d'une broche plus loin dans le code – dans la fonction setup ou même dans la fonction loop.

Cas d'une carte à module ESP32

Les cartes à module ESP32 sont plus polyvalentes que les cartes Arduino en termes de modes d'entrées‑sorties des broches GPIO. La fonction pinMode accepte plus de valeurs pour l'argument mode. En particulier :

  • Une entrée peut être configurée avec un résistor de pull‑down interne (cf. supra ), en utilisant la pseudo‑constante INPUT_PULLDOWN.
  • Une sortie peut être configurée en drain ouvert W (en anglais, open drain), en utilisant la pseudo‑constante OUTPUT_OPEN_DRAIN. Cela permet de piloter un circuit dont la tension de fonctionnement est différente de celle de la carte.

Ces pseudo‑constantes sont définies dans le fichier d'en‑tête esp32-hal-gpio.h G (lignes nº 44 à 55). Leurs valeurs forment des masques pour agir par des opérations logiques bit à bit (cf. chap. C3‑III ) sur un mot binaire qui mémorise la configuration d'une broche.

Cas d'une carte à module ESP8266

En termes de modes d'entrées‑sorties, une carte à module ESP8266 est un peu moins polyvalente qu'une carte à module ESP32. Son framework est moins complexe ; les pseudo‑constantes de mode d'entrées‑sorties sont définies dans le fichier d'en‑tête Arduino.h G (lignes nº 49 à 61). Il présente quelques particularités, notamment les suivantes :

  • Un résistor de pull‑down interne ne peut être activé que sur la broche D0 (GPIO 16). Pour cela, il faut utiliser la pseudo‑constante INPUT_PULLDOWN_16 pour l'argument mode de la fonction pinMode.
  • La broche D0 peut aussi être configurée pour recevoir un signal de réveil (pour quitter un état de veille) en définissant son niveau de tension au repos HIGH ou LOW respectivement via les pseudo‑constantes WAKEUP_PULLUP et WAKEUP_PULLDOWN .

Gestion du niveau logique d'une broche

Écriture du niveau logique d'une sortie

L'écriture (ou l'établissement) du niveau logique du signal de tension sur une broche préalablement configurée en sortie se code par une instruction d'appel de la fonction Arduino digitalWrite qui possède deux arguments. La forme générale de cette instruction est :
digitalWrite(pin, level);  A
où :

  • l'argument pin détermine le numéro de la broche manipulée ;
  • l'argument level détermine le niveau logique à écrire sur la broche.

Ces deux arguments sont attendus de type uint8_t (cf. chap. C3‑II ). De plus, la fonction digitalWrite est de type void : elle ne retourne aucune valeur.

En règle générale, lorsqu'on configure une broche en sortie, une bonne pratique de sécurité consiste à lui conférer d'emblée le niveau logique LOW, c'est‑à‑dire une absence de tension avec la broche GND.

Typiquement, si cette sortie commande une led, cette dernière sera donc éteinte au début de l'exécution du programme. C'est ce que réalise le code ci‑dessous avec en particulier l'appel de la fonction digitalWite à la ligne nº 6.

const int LED_PIN = 7;

void setup()
{
  pinMode(LED_PIN, OUTPUT);   
  digitalWrite(LED_PIN, LOW); 
}

Dans la forme de l'instruction d'appel de la fonction digitalWrite ci‑dessus, les arguments pin et level sont, comme pour tout appel de fonction, des expressions (sachant qu'ici, elles ne doivent prendre que valeurs entières positives).

  • On peut donc y coder bien évidemment des constantes booléennes 0 et 1, et préférentiellement les pseudo‑constantes Arduino LOW et HIGH ;
  • On peut aussi y coder des identificateurs de données déclarées constantes ou variables, comme par exemple la variable ledPin à la ligne nº 11 dans une boucle qui allume tous les led câblées sur une série de broches consécutives :
  •   for (int ledPin = FIRST_LED_PIN; ledPin <= LAST_LED_PIN; ledPin++) {
        digitalWrite(ledPin, HIGH);
      }
    
  • Et on peut même coder des opérations sur des données, y compris des appels d'autres fonctions, comme par exemple dans l'expression !digitalRead(LED_PIN) à la ligne nº 17 du programme minimal qui permet de faire clignoter une led :
  • void loop()
    {
      digitalWrite(ledPin, !digitalRead(LED_PIN));
      delay(BLINK_HALF_PERIOD);
    }
    

Attention ! Les circuits électroniques de la carte Arduino Uno ne peuvent débiter qu'un courant au maximum 40 mA sur une broche du port numérique. Cela est usuellement suffisant pour allumer une led mais pas pour exciter une bobine d'un relais ou faire tourner un moteur (sauf avec des modèles de très petite puissance).

Lorsque la sortie d'une carte doit débiter plus de 40 mA, il est donc nécessaire d'interposer un transistor associé à une alimentation externe. La sortie ne fait alors que commander le transistor qui joue le rôle de relais de puissance.

Lecture du niveau logique d'une entrée‑sortie

La lecture (ou la détection) du niveau logique du signal de tension présent sur une broche, qu'elle soit entrée ou sortie, se code par une expression appelant une fonction spécifique Arduino de la forme :
digitalRead(pin)  A
où l'expression à valeurs entières positives pin – attendue de type uint8_t, cf. chap. C3‑II  – détermine le numéro de la broche.

La valeur retournée par la fonction digitalRead est encodée dans le type int, mais concrètement, il s'agit d'une valeur booléenne. Elle exprime par 0 ou 1 le niveau logique LOW ou  HIGH de la tension lue sur la broche.

Comme avec toute fonction, la valeur retournée par digitalRead est utilisable pour un test, un calcul ou une affectation afin de la mémoriser. En revanche, il n'y a aucun sens à coder une instruction qui n'exploite pas la valeur de retour de digitalRead, comme par exemple :
digitalRead(BUTTON_PIN); // nonsense!
(cf. la remarque au chap. C2‑IV ).

Considérons encore un câblage typique similaire à l'exemple 2) supra  employant :

  • un bouton‑poussoir câblé en logique positive à la broche 8 via un résistor de pull‑down ;
  • la led intégrée à la carte (repérée L), dont le numéro de broche est défini par la pseudo‑constante Arduino LED_BUILTIN).

On souhaite coder un programme de commande monostable de la led par le bouton, c'est‑à‑dire que l'état de la led (éteinte ou allumée) soit l'exact miroir de l'état du bouton (au repos ou appuyé), conformément au chronogramme ci‑contre. Pour cela, on peut par exemple coder une bifurcation simple dont la condition est basée sur la lecture de la broche du bouton (cf. la ligne nº 14 dans le code ci‑dessous).

const int LED_PIN = LED_BUILTIN;
const int BUTTON_PIN = 8;

void setup()
{
  pinMode(LED_PIN, OUTPUT);   
  digitalWrite(LED_PIN, LOW); 

  pinMode(BUTTON_PIN, INPUT);
}

void loop()
{
  if (digitalRead(BUTTON_PIN) == LOW) {
    digitalWrite(LED_PIN, LOW);
  }
  else {
    digitalWrite(LED_PIN, HIGH);
  }
  delay(10); // only for a quicker Tinkercad simulation
}

  1. Attention ! Avant toute lecture sur une broche, on doit s'assurer que son niveau de tension est compatible avec les spécifications du constructeur. Dans le cas des cartes Arduino :
    • une surtension prolongée supérieure à 6 V,
    • ou une inversion de polarité au delà de −0,5 V,
    sont potentiellement destructrices pour le microcontrôleur embarqué.
  2. Dans le programme de l'exemple ci‑dessus, la fonction loop peut se coder plus concisément, sans bifurcation mais avec une composition de fonctions (cf. la ligne nº 14 ci‑dessous) :
  3. void loop()
    {
      digitalWrite(LED_PIN, digitalRead(BUTTON_PIN));
      delay(10); // only for a quicker Tinkercad simulation
    }
    
    Autrement dit, on écrit sur la sortie LED_PIN le niveau logique de tension lu sur l'entrée BUTTON_PIN.

Détection d'un front montant ou descendant sur un signal logique

Notion de front montant et descendant d'un signal

Souvent, lire le niveau logique du signal de tension présent sur une broche configurée en entrée ne suffit pas pour piloter un système.

Si l'on souhaite par exemple détecter l'occurrence d'un appui sur un bouton – et non pas simplement le fait qu'il soit appuyé ou non à un instant donné – il faut être capable d'observer un front montant ou descendant (en anglais, respectivement rising edge et falling edge) sur le signal de tension de la broche à laquelle ce bouton est relié. Ces deux types de fronts se traduisent respectivement par :

  • la montée du signal du niveau logique LOW au niveau HIGH,
  • la descente du signal du niveau logique HIGH au niveau LOW.

À chaque appui sur le bouton, l'un et l'autre de ces deux événements ne se produit qu'une seule fois (sauf en cas de phénomène de rebond électrique).

Pour une réactivité optimale aux appuis sur le bouton :

  • si ce dernier est câblé en logique positive (cf. supra ), il est évidemment nécessaire de détecter les fronts montants ;
  • alors que s'il est logique négative (cf. supra ), il faut détecter les fronts descendants.

Principe de détection

Pour détecter un front montant ou descendant sur le signal de tension d'une entrée d'une carte Arduino, il faut pouvoir comparer les niveaux de tension antérieur et actuel sur cette entrée. Une solution rudimentaire consiste à employer pour cela deux variables booléennes globales, c'est‑à‑dire hors des fonctions setup et loop.

Plus précisément, ces deux variables globales servent à mémoriser :

  • la valeur actuelle du niveau logique de tension sur l'entrée, c'est‑à‑dire durant l'itération en cours de la fonction loop ;
  • la valeur antérieure du niveau logique de tension sur l'entrée, c'est‑à‑dire durant l'itération précédente de la fonction loop.

Attention, deux précautions doivent être prises.

  1. Il faut initialiser ces deux variables avec la même valeur initiale :
    • LOW si l'entrée est câblée en logique positive ;
    • HIGH si l'entrée est câblée en logique négative.
  2. La mémorisation des valeurs doit être effectuée au début de chaque itération de la fonction loop, en codant :
    • la recopie dans la valeur antérieure de la valeur actuelle (en fait, celle de l'itération précédente de la fonction loop) ;
    • la mise à jour de la valeur actuelle par lecture de l'entrée.

Grâce à ces éléments algorithmiques, on peut alors détecter :

  • un front montant en testant si la valeur antérieure est au niveau LOW  et la valeur actuelle au niveau HIGH ;
  • un front descendant en testant si la valeur antérieure est au niveau HIGH  et la valeur actuelle au niveau LOW .

Exemple

Reprenons le montage typique (cf. supra ) du bouton‑poussoir câblé en logique positive commandant la led intégrée d'une carte Arduino.

On garde les mêmes identificateurs et la même fonction setup.

const int LED_PIN = LED_BUILTIN;
const int BUTTON_PIN = 8;

void setup()
{
  pinMode(LED_PIN, OUTPUT);   
  digitalWrite(LED_PIN, LOW); 

  pinMode(BUTTON_PIN, INPUT);
}

On souhaite maintenant réaliser une commande bistable de la led, c'est‑à‑dire qu'elle s'allume (et reste allumée) ou s'éteigne (et reste éteinte) à chaque appui sur le bouton, conformément au chronogramme ci‑contre.

Dans la fonction loop codée ci‑dessous, après les instructions de mémorisation des valeurs précédentes et actuelles du niveau logique sur la broche du bouton (lignes nº 17 & 18) :

  • on teste l'existence d'un front montant (instruction if en ligne nº 20) sur la broche du bouton ;
  • on écrit sur la broche de la led le niveau logique inverse qu'on y lit (instruction if en ligne nº 21) pour inverser son état (LOW → HIGH ; HIGH → LOW).

bool previousButtonLevel = LOW;   
bool currentButtonLevel  = LOW;

void loop()
{
  previousButtonLevel = currentButtonLevel;
  currentButtonLevel  = digitalRead(BUTTON_PIN);

  if (currentButtonLevel == HIGH && previousButtonLevel == LOW) { // rising edge
    if (digitalRead(LED_PIN) == LOW) {
      digitalWrite(LED_PIN, HIGH);
    }
    else {
      digitalWrite(LED_PIN, LOW);
    }  
  }
  delay(10); // only for a quicker Tinkercad simulation
}

  1. La détection des fronts montants est ici indispensable. En effet, si, au lieu de tester l'existence d'un front montant, on testait simplement que l'état du bouton – le fait qu'il soit appuyé ou non – en remplaçant la ligne nº 20 par :
  2.   if (digitalRead(BUTTON_PIN)) { // level detection (not edge detection)
    
    alors chaque appui sur le bouton commanderait un très grand nombre d'inversions de l'état de la led, donnant à cette dernière un aspect moins brillant. En effet, même un appui bref de 0,1 seconde constitue un « événement » très long au regard de la durée d'itération de la boucle loop (de l'odre de quelques micro‑secondes). Le microcontrôleur exécuterait donc quelques 10 000 fois l'instruction if de la ligne nº 21 (on raisonne ici sans prendre en compte l'appel de la fonction delay la ligne nº 28, qui est spécifique à la simulation sur Tinkercad).
  3. L'inversion du niveau logique sur la broche de la led (bifurcation if des lignes nº 21 à 26) peut se coder plus concisément par composition de fonctions et en employant l'opérateur de négation booléenne ! comme ci‑dessous :
  4.   digitalWrite(LED_PIN, !digitalRead(LED_PIN));