XI. Les entrées analogiques▲
Dans cet exercice, nous allons examiner la méthode d’acquisition des signaux analogiques tels que ceux délivrés par divers capteurs à mesure continue. On utilise ces capteurs délivrant un signal de manière continue pour mesurer des températures, forces, accélérations, etc. Cette capacité doit être combinée aux techniques d’entrées/sorties numériques déjà vues afin de bâtir un système indiquant la force appliquée par une série de LED. Plus la force appliquée est grande, plus il y aura de LED allumées.
Les microcontrôleurs utilisent des Convertisseurs Analogique-Numérique (CAN ou ADC pour Analog to Digital Converter) pour mesurer des tensions d’entrée variables en continu. L’ATmega 328P possède un CAN 10 bits précédé d’un multiplexeur à six canaux. Ces six lignes sont reliées au port des entrées analogiques de l’Arduino, connecteurs A0 à A5. La tension de référence par défaut du convertisseur est le 5 V de la carte. Elle représente la tension maximale qui peut être numérisée/mesurée. La tension minimale est la masse ou zéro volt (si des signaux négatifs sont nécessaires, leur niveau devra être décalé d’une façon ou d’une autre). Un convertisseur 10 bits distingue 210 soit 1024 niveaux discrets. Cela signifie que chaque pas représente un peu moins de cinq millivolts (NDLR quantum=5/1024≈4,9 mV ; la résolution du convertisseur).
Pour utiliser le CAN, une tension de référence doit être initialement configurée. Après cela, à chaque fois qu’une valeur/mesure est requise, il suffira d’appeler la fonction analogRead(). Le seul argument de la fonction est le numéro du canal d’entrée désiré (de zéro à cinq pour la Uno). La fonction retourne la valeur numérisée issue de la conversion sous forme d’un entier entre 0 et 1023. Bien qu’il soit possible d’accéder directement aux registres du CAN, la bibliothèque Arduino est assez efficace et nous ne gagnerions au mieux que quelques octets à ne pas l’utiliser.
Nous allons commencer par quelque chose de simple pour montrer comment lire une broche d’entrée analogique. Notez que, à la différence des entrées numériques, l’initialisation par pinMode() ou par un registre de direction (DDRx) n’est pas nécessaire lors de l’utilisation de la fonction analogRead().
Connectez les extrémités d’un potentiomètre de 10 kΩ aux +5 V et masse GND de la carte. Connectez le curseur au connecteur d’entrée analogique 0 (A0).
Entrez le code ci-dessous, compilez-le et transférez-le dans la carte.
/*
Lecture
analogique
V1
.
Lit
la
tension
issue
d
’
un
potentiomètre
et
l
’
envoie
sur
le
moniteur
série
.
Valeurs
de
0
Ã
1023
*/
#
define
ANALOG_IN_PIN
0
int
prior =
0
;
void
setup
(
)
{
Serial.begin
(
9600
);
analogReference
(
DEFAULT );
}
void
loop
(
)
{
int
a;
a =
analogRead
(
ANALOG_IN_PIN );
if
(
a !
=
prior )
{
Serial.println
(
a);
prior =
a;
}
}
Le code est explicite. Sa particularité intéressante est que, plutôt que d'afficher directement la valeur issue de la conversion, on la compare à la valeur précédente et, seulement si elle est différente, le résultat est affiché. Cela économise du temps de traitement et accélère les choses.
Ouvrez le moniteur série. Tournez le curseur du potentiomètre dans le sens horaire et anti-horaire. Les valeurs doivent changer dans le moniteur série lorsqu’on tourne le curseur. Elles doivent évoluer entre 0 et 1023 (il peut manquer une valeur à cause du bruit). Littéralement, on peut déterminer la position réelle du curseur en multipliant la valeur lue par le quantum (5 V/1024, soit environ 4,883 millivolts), et réaliser ainsi un voltmètre très rudimentaire.
XI-A. Capteur de force▲
Remplaçons le potentiomètre par un capteur analogique. Pour cet exercice, nous utiliserons une jauge de déformation (souvent appelée abusivement « jauge de contrainte », FSR en anglais pour Force Sensing Resistor). Une jauge de déformation est un composant à deux bornes, prenant le plus souvent la forme d’un film plat, dont la résistance dépend de la force qui lui est appliquée.
Note de la rédaction

Si aucune force n’est appliquée, la résistance est très élevée, généralement largement supérieure au mégaohm. Avec la force maximum (telle qu’une forte pression des doigts sur la partie sensible), la résistance peut chuter à une valeur aussi basse que 100 Ω. Retirez le potentiomètre et reliez une borne de la jauge de déformation au +5 V et l’autre à une résistance de 10 kΩ. Le point commun à la jauge et la résistance devra être relié à l’entrée A0, tandis que la patte libre de la résistance ira à la masse. Ce montage n’est rien d’autre qu’un diviseur de tension dont l’une des branches est la jauge de déformation.
Lorsqu’on applique une force, la résistance diminue, donc la tension au point commun augmente. Ne modifiez pas le code. Notez simplement comment les valeurs changent dans le moniteur série lorsqu’une force est appliquée sur la jauge : plus la pression est grande, plus la valeur est élevée. Pour finir, échangez les positions de la résistance et de la jauge. À une pression correspond maintenant une baisse des valeurs dans le moniteur série.
XI-B. Affichage à LED▲
Maintenant que le capteur et la fonction de lecture d'entrée analogique ont été testés, combinons cela à un affichage à LED. Plutôt que d'afficher les valeurs sur le moniteur série, nous allons allumer une série de LED. Cela nécessitera de programmer des sorties sur le port numérique, comme vu dans un chapitre précédentLes sorties numériques. Pour rester simple et juste « vérifier le concept », nous allumerons jusqu’à quatre LED. Chacune nécessitera un pilotage par un transistor (ou un ensemble de Darlington tel qu’un ULN2003 qui contient sept amplificateurs dans un seul composant). Nous utiliserons les quatre bits de poids faible du port B pour les sorties, connecteurs 8 à 11 de l’Arduino. Voici le code ci-dessous :
/*
Lecture
analogique
V2
.
Utilise
la
jauge
de
déformation
(
reliée
a
la
masse
)
et
la
10k
(
connectée
au
5V
)
,
sur
la
broche
A0
.
Allumage
de
4
LED
sur
le
port
B
bits
0
a
3
pour
indiquer
la
force
.
Allumage
Ã
l
’
état
haut
.
*/
/
#
define
ANALOG_IN_PIN
0
//
Bits
Ã
utiliser
sur
le
port
B
pour
les
LED
#
define
LEDMASK0
0x0
1
#
define
LEDMASK1
0x0
2
#
define
LEDMASK2
0x0
4
#
define
LEDMASK3
0x0
8
#
define
LEDMASK
(LEDMASK0
|
LEDMASK1
|
LEDMASK2
|
LEDMASK3
)
void
lightLEDsBar
(
int
a );
int
prior=
0
;
void
setup
(
)
{
Serial.begin
(
9600
);
analogReference
(
DEFAULT );
//
Configure
les
bits
de
pilotage
des
LED
en
sorties
DDRB |
=
LEDMASK;
}
void
loop
(
)
{
int
a;
a =
analogRead
(
ANALOG_IN_PIN );
if
(
a !
=
prior )
{
Serial.println
(
a);
lightLEDsBar
(
a );
prior =
a;
}
}
void
lightLEDsBar
(
int
a )
{
if
(
a >
820
)
PORTB |
=
LEDMASK; //
Allume
tout
else
{
if
(
a >
615
)
{
PORTB &
=
(
~
LEDMASK3); //
Éteint
le
haut
PORTB |
=
(
LEDMASK2 |
LEDMASK1 |
LEDMASK0); //
Allume
tout
en
dessous
}
else
{
if
(
a >
410
)
{
PORTB &
=
(
~
(
LEDMASK3 |
LEDMASK2));
PORTB |
=
(
LEDMASK1 |
LEDMASK0);
}
else
{
if
(
a >
205
)
{
PORTB &
=
(
~
(
LEDMASK3 |
LEDMASK2 |
LEDMASK1));
PORTB |
=
LEDMASK0;
}
else
PORTB &
=
(
~
LEDMASK);
}
}
}
}
Il est ici primordial de configurer les bits 0 à 3 de DDRB en sorties, elles piloteront nos LED. Des masques de bits sont définis pour chaque sortie afin de faciliter la maintenance du code. Une nouvelle fonction a été créée pour gérer l’allumage : lightLEDsBar(). Cette fonction créera un bargraphe fondé sur la valeur qu’on lui aura passée. Comme il y a quatre LED, nous pouvons diviser les 1024 points de mesure en cinq segments. Si la valeur est en dessous de 20 % (env. 205), aucune LED n’est allumée. De 20 % à 40 %, seule la première LED sera allumée. Avec l’augmentation des valeurs, de plus en plus de LED seront allumées, jusqu’à 80 % et plus, où toutes les LED seront allumées. La fonction est un peu plus évoluée qu’une cascade de if/else. Pour une zone donnée, les LED du haut sont d’abord éteintes, puis les LED restantes en bas sont allumées. Remarquez que la méthode ci-dessus est plus efficace que l’utilisation de la fonction digitalWrite(), car nous pouvons modifier l’état de plusieurs bits en une fois, en accédant directement au port B. Au contraire, digitalWrite() nécessiterait un appel pour chaque bit/LED. Le code du moniteur série a été conservé, vous pourrez ainsi vérifier les résultats et vous assurer que tout fonctionne comme prévu.
Tout en laissant la jauge de déformation en place, câblez les quatre circuits de pilotage des LED sur les broches 8 à 11. Un courant de 10 mA dans les LED sera suffisant. Entrez le code ci-dessus, compilez et transférez-le. Pour tester, ouvrez simplement le moniteur série et appuyez sur la partie sensible de la jauge. Plus la force appliquée augmente, plus les valeurs sur le moniteur série diminuent et moins les LED s’allument. Vous pouvez envisager cela comme un « absence-de-force-mètre ». Bien que d’une logique assez déroutante, cela peut être très utile. Il arrive qu’on souhaite garder les choses « sous pression », une force appliquée trop faible devenant un signal d’alarme (autrement dit, plus de LED allumées signifient un plus gros problème).
Parfois, les LED sont disposées pour imiter un indicateur analogique. Dans un pareil cas, on peut vouloir n’allumer qu’une seule LED plutôt que toutes celles qui sont en dessous. Nous appellerons cela le « mode point », à l’opposé du « mode barre ». La version trois du programme présente le mode point sous une forme intéressante. La partie moniteur série a été retirée, vous pourrez donc noter une diminution de la taille du programme.
/*
Lecture
analogique
V3
.
Utilise
la
jauge
de
déformation
(
reliée
a
la
masse
)
et
la
10k
(
au
5V
)
,
sur
la
broche
A0
.
Allumage
d
’
une
seule
LED
sur
le
port
B
bits
0
a
3
pour
indiquer
la
force
.
Visualisation
en
mode
point
.
Allumage
a
l
’
état
haut
.
*/
#
define
ANALOG_IN_PIN
0
//
Bits
Ã
utiliser
sur
le
port
B
pour
les
LED
#
define
LEDMASK0
0x0
1
#
define
LEDMASK1
0x0
2
#
define
LEDMASK2
0x0
4
#
define
LEDMASK3
0x0
8
#
define
LEDMASK
(LEDMASK0
|
LEDMASK1
|
LEDMASK2
|
LEDMASK3
)
void
lightLEDsDot
(
int
a );
int
prior=
0
;
void
setup
(
)
{
analogReference
(
DEFAULT );
DDRB |
=
LEDMASK; //
Configure
les
bits
de
pilotage
des
LED
en
sorties
}
void
loop
(
)
{
int
a;
a =
analogRead
(
ANALOG_IN_PIN );
if
(
a !
=
prior )
{
lightLEDsDot
(
a );
prior =
a;
}
}
int
thresh[] =
{
820
, 615
, 410
, 205
}
;
void
lightLEDsDot
(
int
a )
{
int
*
p;
p =
thresh;
if
(
a >
*
p+
+
)
{
PORTB &
=
(
~
(
LEDMASK2 |
LEDMASK1 |
LEDMASK0)); //
Éteint
les
trois
du
bas
PORTB |
=
(
LEDMASK3); //
allume
la
plus
haute
}
else
{
if
(
a >
*
p+
+
)
{
PORTB &
=
(
~
(
LEDMASK3 |
LEDMASK1 |
LEDMASK0));
PORTB |
=
(
LEDMASK2);
}
else
{
if
(
a >
*
p+
+
)
{
PORTB &
=
(
~
(
LEDMASK3 |
LEDMASK2 |
LEDMASK0));
PORTB |
=
LEDMASK1;
}
else
{
if
(
a >
*
p )
{
PORTB &
=
(
~
(
LEDMASK3 |
LEDMASK2 |
LEDMASK1));
PORTB |
=
LEDMASK0;
}
else
PORTB &
=
(
~
LEDMASK);
}
}
}
}
La variante en mode point est similaire à celle avec un bargraphe, sauf que toutes les LED sont éteintes, sauf la plus haute. Vous pourriez vous demander pourquoi chercher la complication à vouloir éteindre les LED individuellement, plutôt que les éteindre toutes (faire un ET logique avec 0xF0), puis allumer celle que l’on désire. La raison est que cela peut provoquer un basculement fugitif (ON-OFF-ON) de la LED en question. Dans cette application, vous ne verrez probablement jamais la différence, mais ce basculement peut poser problème dans certaines applications. Quoiqu’il en soit, la quantité de code machine créé est à peu près la même dans les deux cas.
La modification la plus intéressante de la fonction est qu’on utilise un tableau de valeurs pour les seuils plutôt que des valeurs « en dur ». Cela rend le programme beaucoup plus flexible s’il fallait modifier les seuils ultérieurement. Pour utiliser un jeu de seuils différent, il suffit de faire pointer p sur la première valeur du tableau de seuils créé au début de la fonction. Notez également en particulier l’utilisation de la post-incrémentation du pointeur p. C’est généralement plus efficace que la classique indexation de tableau.
Défi
Modifiez le programme afin d’inverser la logique d’allumage, c.à .d. faites en sorte que plus forte est la pression, plus le nombre de LED allumées est grand et inversez le sens en mode point. Ne modifiez pas le câblage de la jauge. Faites-le uniquement en modifiant le programme. Ensuite, ajoutez un interrupteur sur la broche 7 de l’Arduino (port D.7). La position de cet interrupteur permettra à l’utilisateur de choisir un affichage en mode barre ou en mode point. Un traitement anti-rebond de cet inverseur ne sera pas nécessaire. En bonus, ajoutez un second interrupteur sur la broche 6 de l’Arduino (Port D.6), qui permettra de choisir entre l’actuelle échelle linéaire et une échelle logarithmique de 6 dB par LED (soit une réduction du niveau de moitié, la LED supérieure s’allumant au-dessus de 512). Proposez votre code et un schéma adéquat avec les valeurs des composants.