débutant qui teste les pin sans comprendre

Bonjour,
je possède une carte WAVGAT, et lorsque je "teste" les pin 2 à 13 pour vérifier qu'elles soient bien toutes en LOW lorsque aucun bouton y est connecté avec le bout de code (surement pas très propre) suivant:

int buttonState = 0;

void setup()
{

  Serial.begin(9600); //Initialisation de la communication avec le moniteur série
  pinMode(2, INPUT);
  pinMode(3, INPUT);
  pinMode(4, INPUT);
  pinMode(5, INPUT);
  pinMode(6, INPUT);
  pinMode(7, INPUT);
  pinMode(8, INPUT);
  pinMode(9, INPUT);
  pinMode(10, INPUT);
  pinMode(11, INPUT);
  pinMode(12, INPUT);
  pinMode(13, INPUT);

}
void loop()
{

  boolean z = digitalRead(2); //
  Serial.println(z);
  boolean a = digitalRead(3); //
  Serial.println(a);
  boolean b = digitalRead(4); //
  Serial.println(b);
  boolean c = digitalRead(5); //
  Serial.println(c);
  boolean d = digitalRead(6); //
  Serial.println(d);
  boolean e = digitalRead(7); //
  Serial.println(e);
  boolean f = digitalRead(8); //
  Serial.println(f);
  boolean g = digitalRead(9); //
  Serial.println(g);
  boolean h = digitalRead(10); //
  Serial.println(h);
  boolean i = digitalRead(11); //
  Serial.println(i);
  boolean j = digitalRead(12); //
  Serial.println(j);
  boolean k = digitalRead(13); //
  Serial.println(k);


  delay(2000);

}

J'obtient le résultat suivant:

1
1
1
1
1
1
1
1
1
1
1
0

Et j’avoue ne pas comprendre...
Je devrait être à 0 sur toutes les entrée (et non pas que la 13 qui a sa led interne)...
Du coup, si je code une action dépendante d'un BP et que celui-ci est absent, alors mon code se lance...

Pourquoi devraient elles être à LOW si vous ne les reliez pas à GND?

votre code pourrait se faire avec une boucle for si vous voulez qu'il soit un peu plus concis et la lecture d'une pin numérique ne retourne pas un booléen mais un entier qui sera soit HIGH , soit LOW.

Je pensai que si le pin ne recevait pas du 5v il était forcément en LOW et que j’aurai donc 0 en réponse.

bonsoir

effectivement votre logique n' est pas exempt de bon sens ,

cependant , si vous allez sur ce lien :
https://www.locoduino.org/spip.php?article122
vous pouvez lire :

"Les entrées du microcontrôleur équipant l’Arduino sont très sensibles et elles réagissent à la tension qui leur est appliquée.
En conséquence, l’entrée laissée en l’air peut avoir n’importe quelle valeur comprise entre 0 et 5 V et être interprétée comme un LOW ou un HIGH par l’instruction digitalRead(...)."

sur celui-ci:

that pins configured as pinMode(pin, INPUT) with nothing connected to them,.....
random changes in pin state......electrical noise from the environment"

par contre c' est bien de ne pas avoir fait de test sur les pins zéro et un "réservé pour le upload"...

Comme PBZOOM l'a mentionné,

en utilisant pinMode(pin, INPUT) l'état du pin est indéfini si rien n'y est connecté.
On ici le choix de connecter en externe :

soit une résistance de tirage au "+" : l'état de pin sera au niveau logique haut "1"

soit une résistance de tirage au "*" : l'état de pin sera au niveau logique bas "0"

L'Atmega qui équipe l'arduino, possède une résisance interne de tirage au "+".

Elle s'active en utilisant pinMode(pin, INPUT_PULLUP)

Les pin positionnées en entrée sont dites "à haute impédance".
En langage commun cela veut dire :

  1. qu'elles n'envoient aucune tension
  2. Mais comme elles présentent "une impédance", c'est à dire ici une résistance de plusieurs millions d'ohms et une petite capacité parasite en parallèle,elles peuvent capter les ondes qui passent à coté : aussi bien la radio que la télé, le Wifi, sans oublier le micro-onde de la cuisine.
  3. Pour les micros Atmel avr, qui équipent les cartes basiques arduino, toute tension supérieure à 2,6 V est considérée comme un état haut.
    Toute tension inférieure à 2,4V est considérée comme un état bas.
    La zone entre 2,4V et 2,5V est la zone d'hystérésis (voir ce nom).

Nota :
Résistance ou impédance ?
En courant continu il n'y a que la résistance, en courant alternatif il faut ajouter l'inductance et la capacité : on parle alors d'impédance.
L'impédance a pour symbole la lettre Z.
U =RI en continu ou u = Z i en alternatif.
Comme les électroniciens sont des fainéants ils n'utilisent que le terme "impédance" qu'elle que soit la nature du courant continu ou alternatif.

@J-M-L
Je ne voudrais pas déclencher une polémique, le sujet n'en vaut pas la peine, mais le choix de booléens par Fada34 ne me parait pas du tout fada (désolé je n'ai pas pu m'empêcher) .
Définir l'état d'une sortie qui ne peut avoir que 2 états par un type de variable qui ne peut avoir que deux états ne me semble pas incongru ni illogique même s'il n'est pas habituel, après tout ce n'est qu'une question de convention.
Qu'ont fait Wiring/Arduino si n'est de définir un type supplémentaire de booléens avec HIGH et LOW alors que le registre PINX ne donne que des 1 ou des 0.
L'important est que Fada24 sache ce qu'il fait et connaisse les conséquences de ses choix.

Il me semble que le type booléen existe déjà en C/C++ j'ai déjà vu le type "bool", vu qu'un type booléen existait déjà je n'ai pas compris ce type "boolean" de Wiring/arduino, sans doute que quelque chose m'échappe.

@J-M-L
Je ne voudrais pas déclencher une polémique, le sujet n'en vaut pas la peine, mais le choix de booléens par Fada34 ne me parait pas du tout fada (désolé je n'ai pas pu m'empêcher) .
Définir l'état d'une sortie qui ne peut avoir que 2 états par un type de variable qui ne peut avoir que deux états ne me semble pas incongru ni illogique même s'il n'est pas habituel, après tout ce n'est qu'une question de convention.
Qu'ont fait Wiring/Arduino si n'est de définir un type supplémentaire de booléens avec HIGH et LOW alors que le registre PINX ne donne que des 1 ou des 0.

Sans polémique le code marche et bcp de développeurs prennent ce raccourci.

Cependant en théorie de programmation le type booleen est un type à part entière qui vaut valeur de vérité: VRAI ou FAUX. Il se trouve que l’implementation En C ou C++ fait de 0 faux et 1 pour true (ou d’autres valeurs non nulles seront converties à VRAI) - mais ce 1 n’est pas une constante numérique standard, c’est pour le compilateur la représentation de VRAI

Dans d’autres langages l'affectation d’un entier à un booleen déclencherait une erreur de type. en C ou C++ il y a conversion automatique. Notez aussi que la norme en C++ ne garantit pas que un booleen soit stocké sur un seul octet, donc si c’est pour économiser de la mémoire autant prendre un uint8_t (encore théoriquement car sur arduino c’est bien un octet)

Pour être tout à fait propre il faudrait écrire boolean z = digitalRead(2) == HIGH; // ou LOW suivant ce qu’on veut testeret dans ce cas on affecte bien une valeur de vérité à un booleen, on est cohérent dans le typage formel.

Pour moi c’est une bonne habitude à prendre quand on apprend à programmer - ça force un peu à réfléchir, un peu comme quand en électricité ou physique on réfléchit aux unités qu’on manipule pour voir la cohérence d’une formule.

68tjs:
Il me semble que le type booléen existe déjà en C/C++ j'ai déjà vu le type "bool", vu qu'un type booléen existait déjà je n'ai pas compris ce type "boolean" de Wiring/arduino, sans doute que quelque chose m'échappe.

Bonjour,

Entièrement d'accord. Pourquoi redéfinir le type bool.
Ceux qui débutent en C/C++ vont croire que le type boolean est un type C++ alors que c'est un type défini dans Arduino.h.

Cependant en théorie de programmation le type booleen est un type à part entière qui vaut valeur de vérité: VRAI ou FAUX.

Tout à fait d'accord mais la notion est extensible.
Si j'écris :
if (digitalRead(xx)){
//bla bla
}
Dans ce cas, dans l'esprit, "digitalRead(xx)" n'est pas considéré comme ayant la valeur 0 ou 1 (provenant du registre PINX) mais comme un booléen puisque le résultat dans un if doit être un booléen, cela dépasse le mécanisme 1 ou 0 qui n'est qu'un outil parce qu'il en faut bien un.
Perso je ne trouve rien a redire a ce choix, si c'est vraiment choix.

68tjs:
Tout à fait d'accord mais la notion est extensible.
Si j'écris :
if (digitalRead(xx)){
//bla bla
}
Dans ce cas, dans l'esprit, "digitalRead(xx)" n'est pas considéré comme ayant la valeur 0 ou 1 (provenant du registre PINX) mais comme un booléen puisque le résultat dans un if doit être un booléen, cela dépasse le mécanisme 1 ou 0 qui n'est qu'un outil parce qu'il en faut bien un.
Perso je ne trouve rien a redire a ce choix, si c'est vraiment choix.

quand vous faites cela vous effectuez un raccourci que le compilateur traite pour vous.

il voit que digitalRead(xx) est un entier puisqu'il connait la signature de la fonction digitalRead() et il sait faire la promotion d'un entier vers un booléen par une comparaison avec 0 (instruction en langage machine pré-câblée de branchement si par comparaison (BRNE en ATMEGA) - c'est pour cela que en C ils avaient d'ailleurs pris cette convention pour vrai et faux

Mais en pratique donc le compilateur écrit - sans que vous ne le voyez

if (digitalRead(xx) != 0){
     //bla bla
}

et dans ce cas vous avez bien dans le if une condition, ce qui est attendu puisque ce que l'on met dans le if c'est une condition, donc une valeur de vérité d'un point de vue type.

On voit d'ailleurs très bien la différence sémantique sur les type si vous faites des opérations. Par exemple avec ce code

boolean booleen;
uint8_t octet;

void setup() {
  Serial.begin(115200);
  booleen = 1;
  octet = 1;
  Serial.print(F("Booleen = ")); Serial.println(booleen);
  Serial.print(F("octet = ")); Serial.println(octet);  
  booleen += 1;
  octet +=1;
  Serial.print(F("Booleen+1= ")); Serial.println(booleen);
  Serial.print(F("octet+1 = ")); Serial.println(octet);  
}

void loop() {}

Vous aurez dans la console

[color=purple]
Booleen = 1
octet = 1
Booleen+1= 1
octet+1 = 2
[/color]

On voit bien que lorsqu'on essaye de rajouter 1 à un booléen, le compilateur fait une opération d'affection logique (valeur de vérité). il fait 1+1 = 2 et ensuite compare 2 avec 0, c'est pas 0 donc c'est vrai, donc remet TRUE (1) dans la variable booleen. Alors que si vous avez pris un type entier alors l'opération n'est pas manipulée en cachette dans votre dos.

--> la C permet des raccourcis à l'écriture sur ce type mais il faut bien comprendre qu'un type booléen n'est pas équivalent à un entier et que lorsqu'on affecte un entier dans le booléen le compilo génère un test pour vous. De même mettre un entier comme condition dans un if, du genre if (entier) est en fait manipulé ensuite par le compilateur pour rajouter le test de comparaison

kamill:
Bonjour,

Entièrement d'accord. Pourquoi redéfinir le type bool.
Ceux qui débutent en C/C++ vont croire que le type boolean est un type C++ alors que c'est un type défini dans Arduino.h.

oui c'est du même ordre que redéfinir en #define B0010 au lieu de laisser le programmeur utiliser 0b0010... ils ont voulu bien faire en croyant simplifier

cela évolue avec les version du compilateur, non, la manière de gérer les booléens ? J'ai souvenir de pouvoir faire booleen+=1 pour en changer l'état il n'y a pas si longtemps (en incrémentant le premier bit de l'octet, on le faisait passer de 0 à 1 et inversement, et c'est même en cours d'informatique que j'avais appris à faire ça, en école d'ingénieur en 2001...), ce qui ne fonctionne pas avec la version actuelle comme vous venez de l'expliquer puisque c'est alors la valeur totale qui est prise en compte et non plus le seul premier bit.

Ça m'avait valu d'ailleurs une loooooongue recherche de bug quand le comportement a changé... très bonne piqûre de rappel sur le fait de ne pas trop utiliser de raccourcis :stuck_out_tongue: (que je me suis empressé d'oublier...)

@ J-M-L
Tout cela je l'ai bien compris.
Je fais de l'abstraction :
La fonction digitalRead c'est un voltmètre tout ou rien.
Le voltmètre à l'entrée du micro mesure 5V -> je dis présence de signal = vraie
Le voltmètre à l'entrée du micro ne mesure rien (ou 0V c'est la même chose) -> je dis présence de signal = faux
C'est bien une opération logique que je fais.

L'affectation niveau 5V à l'état logique 1 est fait par Atmel dans le registre PINX, pour faire cela Atmel a choisi la norme du C qui affecte le chiffre 1 à VRAI et 0 à FAUX, donc c'est cohérent.
PINX ne contient que des états logiques ce qui veut dire que pour moi la fonction digitalRead(x) ne renvoi pas un nombre mais un état logique.
Que Wiring ait choisi de la déclarer en unint8_t est un autre problème et ce n'est pas le mien.
Que Wiring ait choisi de définir deux états logiques supplémentaires HIGH et LOW qui sont inconnus d'Atmel et du microcontrôleur en est un autre, c'est pour moi du même domaine que le type boolean au lieu du type bool et les constantes B0011 au lieu de l'écriture 0b0011.

En électronique logique dans tous les tableaux de Karnaugh on écrit bien 1 pour "vrai" et 0 pour "faux", ce n'est qu'une écriture.

Edit :
Je viens par acquis de conscience d'ouvrir Arduino.h pour voir le prototype de digitalRead :

int digitalRead(uint8_t);

J'espère que le compilateur est intelligent parce que utiliser un entier sur deux octets pour y stoker soit 0 soit 1.........

68tjs:
Je viens par acquis de conscience d'ouvrir Arduino.h pour voir le prototype de digitalRead :

int digitalRead(uint8_t);

J'espère que le compilateur est intelligent parce que utiliser un entier sur deux octets pour y stoker soit 0 soit 1.........

ça par contre, je ne vois pas bien comment le compilateur prendrait l'initiative de changer un type explicitement déclaré...

@68tjs

bricofoy a raison - si la signature de la fonction demande un int le compilateur et l'optimiser ne vont pas s'amuser unilatéralement à retourner un seul octet.

Si vous regardez le code source vous voyez que (en gros) ils retournent HIGH ou LOW

if (*portInputRegister(port) & bit) return HIGH;
 return LOW;

or HIGH et LOW sont définis ici non pas comme des constantes typées, mais par un #define

#define HIGH 0x1
#define LOW  0x0

et donc seront soumis aux lois de l'usage par défaut de constantes numériques lors de leur remplacement dans le code. en C ou C++ ça veut dire qu'ils seront considérés comme des int (et sur un ATMEGA ce sera donc 2 octets en entier signé, sur un ESP ce sera 4 octets)

68tjs:
Je fais de l'abstraction :
La fonction digitalRead c'est un voltmètre tout ou rien.
Le voltmètre à l'entrée du micro mesure 5V -> je dis présence de signal = vraie
Le voltmètre à l'entrée du micro ne mesure rien (ou 0V c'est la même chose) -> je dis présence de signal = faux
C'est bien une opération logique que je fais.

Oui dans votre tête vous effectuez cette abstraction, c'est comme cela que vous vous représentez la fonction... Mais ce n'est pas ce que fait le code. Pour faire ce que vous dites il aurait fallu définir la fonction comme cela:

bool presenceTension(uint8_t pin)
{
 uint8_t timer = digitalPinToTimer(pin);
 uint8_t bit = digitalPinToBitMask(pin);
 uint8_t port = digitalPinToPort(pin);

 if (port == NOT_A_PIN) return false;

 // If the pin that support PWM output, we need to turn it off
 // before getting a digital reading.
 if (timer != NOT_ON_TIMER) turnOffPWM(timer);

 return ((*portInputRegister(port) & bit) != 0);
}

et dans ce cas le compilateur n'a pas d'opération magique à faire derrière votre dos, vous retournez bien une valeur de vérité.

En pratique la fonction digitalRead retourne un entier signé qui vaut 1 ou 0 et ensuite - et seulement ensuite - suivant ce que vous faites avec cette valeur, le compilateur va générer du code en plus pour convertir cet entier en valeur de vérité:

// imaginons que la pin 2 soit HIGH
int  x = (digitalRead(2) + 1) * 5; //  x vaudra 10
bool x = (digitalRead(2) + 1) * 5; // x vaudra true

Dans le premier cas on traite le résultat de digitalRead comme un entier, et donc x vaudra 10, le compilateur ne rajoute aucun code. Dans le second cas le compilateur évalue la partie droite, trouve 10 et comme il voit qu'ensuite il faut affecter cela dans un booléen, le compilateur génère du code (selon la norme) qui va comparer le résultat obtenu à 0 et si c'est zéro mettre false dans x, si ce n'est pas zéro mettre true dans x.

@bricofoy

bricofoy:
cela évolue avec les version du compilateur, non, la manière de gérer les booléens ? J'ai souvenir de pouvoir faire booleen+=1 pour en changer l'état il n'y a pas si longtemps (en incrémentant le premier bit de l'octet, on le faisait passer de 0 à 1 et inversement, et c'est même en cours d'informatique que j'avais appris à faire ça, en école d'ingénieur en 2001...), ce qui ne fonctionne pas avec la version actuelle comme vous venez de l'expliquer puisque c'est alors la valeur totale qui est prise en compte et non plus le seul premier bit.

Tout à fait, depuis C++11 le standard interdit de décrémenter un booléen (résultat indéterminé, dépendant du compilo) et après C++17 le standard ne supporte plus l'incrémentation d'un booléen. (donc b++ par exemple qu'on pouvait utiliser pour alterner le bit de poids faible).

Bref - tout ce que je dis c'est qu'à mon avis il vaut mieux rester maître des types que l'on utilise. Le typage fort évite bien des désagrément et des sessions de debug pénibles. Comprendre ce que fait le compilateur dans votre dos et suivre les évolutions de la norme c'est pas toujours simple. Donc ma recommandation, si vous utilisez un booléen, mettez dedans une valeur de vérité (le résultat d'un test). Comme cela vous maitrisez le code. Si vous devez faire des opérations sur un type booléen, les seules autorisées sont celles de la logique booléenne (&&, || etc)

MAIS je suis bien d'accord que la norme définit explicitement la promotion d'une valeur intégrale vers une valeur de vérité, en disant que 0 devient faux et tout le reste devient vrai. Donc si vous êtes à l'aise avec ce que génère le compilo pour vous, pas de soucis non plus à utiliser cela - c'est la norme donc tout à fait correct.

Pour ma part je réserve cela à l'écriture des expressions dans un if ou un while par exemple quand j'ai un peu la flemme de rajouter le != 0 dans la condition et que je sais que le compilateur va le faire pour moi

On ne va pas se prendre la tête et on va en rester là.
Je reste persuadé que mon raisonnement est cohérent avec le matériel et les choix fait par Atmel même s'il n'est pas entièrement juste avec les choix logiciels.

Oui oui - votre raisonnement est cohérent - c’est juste que ce n’est pas ce que fait le code.

La fonction aurait pu être codée pour retourner une valeur de vérité puisque comme vous le dites c’est un état tout ou rien que l’on mesure.

Je pense que les auteurs ont voulu introduire un autre concept, la notion de HIGH ou LOW pour une PIN.

Ce concept n’existant pas sous forme de type, ils ont pris la décision (totalement idiote je vous rejoins) de coder cela sur un int. ça aurait pu représenter un demi intérêt s’ils retournaient -1 si la lecture de la pin n’était pas possible mais comme on le voit dans le code, ils ont pris par convention idiote que si on essaye de lire une PIN inconnue alors on retourne LOW… Et même dans ce cas, ils auraient pu stocker le résultat dans un int8_t histoire de gagner un peu de mémoire…

bref - ce qu’ils ont fait est moche :slight_smile:

J-M-L:
Ce concept n'existant pas sous forme de type, ils ont pris la décision (totalement idiote je vous rejoins) de coder cela sur un int.

la cible étant un microcontrôleur avec 1k de ram, c'est peu de le dire...

Il y a mieux !
L'erreur a été faite par Wiring publié en 2004. La première version arduino date de 2005.
Chacun sait que jusqu'à la version 0.23 Arduino n'était qu'une copie de Wiring adaptée aux micro de la UNO et de la Mega.
Quand Wiring a sorti sa version 1.0 elle comportait des améliorations.
Pour ce qui concerne ce sujet la fonction digitalRead() a été abandonné au profit de pinRead() qui est cette fois déclarée comme un uint8_t.

Aucune des versions arduino postérieures à 023 n'a repris les améliorations ou corrections de Wiring.
D'habitude un fork c'est pour améliorer le produit plus rapidement.

Il ne faut pas se faire d'illusions, devant le buzz que Banzi a réussit à créer autour d'arduino, Wiring est cliniquement mort (la dernière version date de 2014).