analogRead()
Après avoir joué avec analogWrite(), nous allons voir sa sœur analogRead(). Cette dernière instruction permet de mesurer la tension qui est présente sur une entrée analogique. Comme le microcontrôleur ne peut que travailler sur des quantités numériques, cette instruction va convertir la tension de la broche analogique en valeur numérique utilisable. On dira qu'il y a un convertisseur analogique numérique (CAN).
Dans le microcontrôleur, il n'y a en fait qu'un seul CAN, et quand on demande une lecture analogique, cela va aiguiller le signal de la broche intéressée vers le CAN, et faire la conversion. On peut utiliser et lire les valeurs de plusieurs entrées analogiques, car on lit les valeurs les unes après les autres. Les broches qui peuvent être utilisées sont repérées par la lettre A suivies d'un nombre. Sur une Uno ce sont les broches A0 à A5, la Nano en a deux de plus A0 à A7, et la Mega en a 16, A0 à A15.
Le programme
Nous allons faire un programme qui affiche toutes les demi-secondes la valeur lue sur la broche A0. Le setup() contiendra donc l'initialisation de la liaison série pour l'affichage:
Serial.begin(115200);
et dans la boucle loop(), on affichera la lecture de A0 et on attendra une demi-seconde (avec un delay(500);). Pour l'affichage de la valeur lue, analogRead() retourne l'image de la tension. C'est le résultat de la fonction. On peut donc écrire:
valeurLue = analogRead(A0); // Lecture Serial.println(valeurLue); // Et affichage
Il faudra déclarer au préalable valeurLue. Mais on peut aussi le faire en une seule fois car on n'a besoin de la valeur qu'une seule fois:
Serial.println(analogRead(A0)); // Lecture et affichage
Pour comprendre cette écriture, on passe A0 à la fonction analogRead(). Cette fonction va donc lire la tension de la broche A0, la transforme en un nombre et c'est ce nombre que l'on passe à la fonction Serial.println() qui l'affiche.
Interprétation
Faites tourner le programme. Aide?void setup() { Serial.begin(115200); // Pour pouvoir utiliser la console } void loop() { Serial.println(analogRead(A0)); // Lecture et affichage delay(500); } Vous devez voir une liste de nombres s'afficher.
Mettez un doigt sur le broche A0 et un doigt (de l'autre main, c'est plus facile) sur la partie métallique de la prise USB. La succession des nombres devrait être une suite de 0. les doigts imposent un 0V et on lit alors la valeur numérique 0. Mettez toujours un doigt sur A0, et baladez vous avec l'autre. Si il passe sur le 5V, vous devriez avoir une valeur qui monte. La valeur maximale que l'on obtient est de 1023.
On vient de voir que la conversion retourne une valeur entre 0 et 1023. Il faut 10 bits pour représenter cette valeur (210=1024). On dira que l'on a un convertisseur 10 bits. La correspondance est:
0V --> 0 5V --> 1023
La résolution (plus petit écart mesurable) est donc de 1/1024 de 5V soit environ 5mV. On dira que la résolution est de 5mV, mais on dit plus souvent que la résolution et de 10 bits, car 10 bits est une valeur invariable. Par contre, si on change la tension des 5V en une autre tension, cela va changer la résolution mesurée en tension.
On remarque que plus le nombre de bits est important, plus la résolution est faible, est meilleure. Mais il faut aussi que la stabilité des alimentation et de la référence de tension soit correcte. C'est pour cela que le convertisseur est de 10 bits, pas plus.
valeurLue
Revenons à la solution avec:
void loop { valeurLue = analogRead(A0); // Lecture Serial.println(valeurLue); // Et affichage delay(500); // Pour ne pas défiler trop vite }
Pour faire ceci, nous avons dit qu'il fallait déclarer valeurLue. Quand on déclare une variable, on devrait toujours se poser la question de la taille, pour ne pas prendre une variable mal adaptée. Ici la valeur la plus grande est de 1023, il faut donc une variable 16 bits. On pourrait prendre un int, mais on remarquera que la valeur est toujours positive. Un 16 bits non signé est donc parfaitement adaptée, un word ou un uint16_t (sur une Uno, c'est pareil).
La deuxième question à se poser est la place de la déclaration de valeurLue. On peut la déclarer globale, à l'extérieur de loop(), souvent d'ailleurs avant setup():
word valeurLue; void setup { ... } void loop { ... }
Mais ici on peut aussi la déclarer locale à le fonction loop() soit sur l'ensemble (au début de loop()):
void setup { ... } void loop { word valeurLue; valeurLue = analogRead(A0); // Lecture Serial.println(valeurLue); // Et affichage delay(500); // Pour ne pas défiler trop vite }
soit encore juste au moment où l'on en a besoin:
void setup { ... } void loop { word valeurLue = analogRead(A0); // Lecture Serial.println(valeurLue); // Et affichage delay(500); // Pour ne pas défiler trop vite }
Chacun va choisir l'écriture qu'il veut, il n'y a pas un bon et un mauvais choix. Ne pas utiliser de variable intermédiaire est sans doute la forme que j'utiliserai ici car on n'a pas besoin d'utiliser valeurLue une seconde fois. Dans un programme plus conséquent, il est probable que j'utilise une autre forme.
Notez quand même que si on déclare la variable globale, elle ne sera initialisée qu'une seule fois, et le programme peut tourner plus vite (mais dans le cas présent, comme on a une attente de 0,5s, cela ne changera rien). Si on la déclare locale la variable occupe de la place seulement si on est dans la fonction; ici, c'est tout le temps, mais quand on écrira de gros programmes avec pleins de fonctions, cela peut libérer de la place.