I. Objectif▲
L'objectif est d'acquérir une tension aux bornes d'un capteur de température analogique (LM335) en interfaçant un convertisseur analogique-numérique externe (Microchip MCP3208) au Raspberry Pi. Le programme d'acquisition sera réalisé en langage C accompagné de la bibliothèque bcm2835 (le bcm2835 étant le processeur Broadcom dont sont équipés les Raspberry Pi modèles A, B, B+ et Zero). Les données seront visualisées en temps réel dans un navigateur grâce à l'outil Node-RED.
II. Matériel requis▲
Matériels |
Remarques |
---|---|
|
|
|
|
ou
|
|
|
|
|
|
|
|
III. Fonctionnement du convertisseur analogique-numérique MCP3208▲
Un signal analogique est un signal qui varie de façon continue, comme la température d'un lieu au cours du temps, alors qu'un signal numérique varie de façon discrète dans le temps.
Un convertisseur analogique-numérique (ou en anglais ADC pour Analog to Digital Converter) est un dispositif électronique dont la fonction est de traduire une tension analogique VE en une valeur numérique N codée sur n bits.
Transformé en une succession de 0 et de 1, le signal peut être plus facilement transporté, stocké et traité par l'informatique, mais le signal quantifié présente des échelettes qui traduisent la perte d'information lors du passage dans le numérique :
III-A. Caractéristique de transfert▲
La figure ci-dessous montre la caractéristique de transfert d'un convertisseur analogique-numérique 3 bits. La plage de tension (VREF = tension de référence, la tension maximale admissible en entrée du convertisseur) est divisée en plages d'égales dimensions et de largeur q. Il y a autant de plages que de valeurs possibles de la sortie numérique (c.-à-d. ici 23=8 valeurs). On peut ainsi associer une valeur numérique à chaque plage et former des paliers :
La largeur q de ces paliers (le pas de quantification), parfois appelée quantum, définit la résolution en tension du convertisseur :
kitxmlcodelatexdvpq= \frac{V_{REF}}{2^{n}}finkitxmlcodelatexdvpPlus q est petit, plus l'« effet d'escalier » dû à la quantification s'estompe, et la caractéristique de transfert s'approche de la caractéristique de transfert idéale en rouge.
Par convention, la caractéristique de transfert est décalée d'un demi-quantum pour réduire l'erreur de quantification en valeur absolue (±1/2.q, sauf pour le dernier palier).
Ainsi, le quantum pour le MCP3208 de résolution 12 bits avec une tension de référence de 3,3 V est :
kitxmlcodelatexdvpq= \frac{3,3}{2^{12}}=\frac{3,3}{4096}=0,0008 \, V< 1 \, mVfinkitxmlcodelatexdvpOutre les erreurs de quantification dues à la résolution du convertisseur, les convertisseurs analogique-numérique présentent d'autres défauts identifiés qui font dévier ses caractéristiques de la droite caractéristique idéale.
Ces erreurs sont généralement exprimées en multiple du quantum (quantum noté LSB Least Significant Bit dans les documentations, car le quantum est la plus petite variation en volts correspondant au changement du bit de poids faible).
Par exemple, l'erreur de non-linéarité différentielle (ou DNL pour Differential Nonlinearity) est l'écart entre la largeur réelle du palier et la largeur idéale (de taille égale à la valeur du quantum). Si la largeur réelle est égale au quantum, l'erreur DNL est nulle, et toute largeur qui s'écarte du quantum donne lieu à une erreur DNL.
Pour plus de détails :
- ADC and DAC Glossary - Maxim Integrated
- ST Application Note AN1636 - Understanding and minimising ADC conversion errors
- Understanding analog to digital converter specifications
L'erreur totale ne s'obtient pas en faisant le cumul de toutes les erreurs (mais elle peut être la somme de plusieurs types d'erreurs) ! En général, l'erreur de décalage (offset) et l'erreur de gain peuvent être annulées ou compensées par logiciel. L'erreur de non-linéarité intégrale (INL) qui est l'image des cumuls des erreurs DNL sur la plage de tension donne alors une bonne estimation de l'erreur maximale. Pour limiter les effets de non-linéarité, on peut faire la moyenne sur plusieurs conversions.
Il y a d'autres sources d'erreurs qui n'ont pas été évoquées et qui peuvent influer sur la précision de la conversion : bruit d'alimentation, qualité de la régulation, impédance d'entrée, bruit électromagnétique…
III-B. Principe de la conversion analogique-numérique par approximations successives▲
Le schéma bloc fonctionnel du MCP3208 tel qu'on le trouve dans la documentation constructeur (datasheet) est le suivant :
La structure est celle d'un convertisseur fonctionnant par approximations successives.
Lorsque le mode single-ended est choisi (une voie d'entrée, la tension mesurée étant référencée à la masse), le multiplexeur en entrée (Input Channel Mux) sélectionne l'entrée choisie parmi CH0 à CH7. La tension d'entrée échantillonnée et bloquée (Sample and Hold) constitue une des deux entrées du comparateur (Comparator). Le rôle de l'échantillonneur-bloqueur est d'acquérir une valeur de tension à l'entrée du convertisseur à un instant donné. Il doit également maintenir la tension acquise stable pendant la durée de la conversion. L'autre entrée du comparateur provient du convertisseur numérique-analogique (DAC pour Digital to Analogic Converter) dont l'entrée est fournie par le registre 12 bits d'« approximations successives » (SAR pour Successive Approximation Register). Le rôle du DAC est de produire une tension analogique en fonction de la valeur contenue dans ce registre 12 bits selon la formule :
kitxmlcodelatexdvp\text{tension analogique} = \frac{\text{valeur SAR}}{2^{12} - 1} \cdot V_{REF}finkitxmlcodelatexdvpSi on prend VREF = VDD = 3,3 V, on a par exemple :
Valeur SAR |
Tension analogique DAC |
---|---|
211 = 2048 = 0b1000 0000 0000 |
(2048/4095) x 3,3 = 1,65 V |
210 = 1024 = 0b0100 0000 0000 |
(1024/4095) x 3,3 = 0,83 V |
29 = 512 = 0b0010 0000 0000 |
(512/4095) x 3,3 = 0,41 V |
28 = 256 = 0b0001 0000 0000 |
(256/4095) x 3,3 = 0,21 V |
… |
… |
20 = 1 = 0b0000 0000 0001 |
(1/4095) x 3,3 = 0,0008 V |
Maintenant, supposez que la tension échantillonnée et bloquée sur une des entrées CHx vaut 2,02 V. L'algorithme par approximations successives réalisera les étapes suivantes à chaque coup d'horloge CLK :
- Premièrement, le SAR est initialisé à zéro.
- Le bit 11 de poids fort du SAR est mis à 1, la tension produite en sortie du DAC est donc de 1,65 V (50 % de VREF). Cette tension est ensuite comparée avec celle de l'entrée CHx. Comme 2,02 est supérieur à 1,65 ; le signal en sortie du comparateur est à l'état haut et le bit 11 du SAR est maintenu à 1 : SAR = 0b1000 0000 0000.
- Le bit 10 du SAR est mis à 1, c'est-à-dire 0b1100 0000 0000 = 3072, soit une tension produite par le DAC de 2,48 V (75 % de VREF). Cette fois, l'entrée (2,02 V) est inférieure à 2,48 V ; le signal en sortie du comparateur bascule à l'état bas et le bit 10 du SAR rebascule à 0 : SAR = 0b1000 0000 0000. On sait à cet instant que la tension sur l'entrée CHx est comprise entre 50 % et 75 % de la tension de référence VREF.
- Le bit 9 du SAR est mis à 1, c'est-à-dire 0b1010 0000 000 = 2560, soit une tension produite par le DAC de 2,06 V (62,5 % de VREF). L'entrée (2,02 V) est inférieure à 2,06 V ; le signal en sortie du comparateur est à l'état bas et le bit 9 du SAR rebascule à 0 : SAR = 0b1000 0000 0000. On sait à cet instant que la tension sur l'entrée CHx est comprise entre 50 % et 62,5 % de la tension de référence VREF.
- Le bit 8 du SAR est mis à 1, c'est-à-dire 0b1001 0000 000 = 2304, soit une tension produite par le DAC de 1,86 V (56,3 % de VREF). L'entrée (2,02 V) est supérieure à 1,86 V ; le signal en sortie du comparateur est à l'état haut et le bit 8 du SAR est maintenu à 1 : SAR = 0b1001 0000 0000. On sait à cet instant que la tension sur l'entrée CHx est comprise entre 56,3 % et 62,5 % de la tension de référence VREF.
- Etc. À chaque nouveau coup d'horloge, l'intervalle de recherche est divisé par deux.
Une fois que le dernier bit 0 du SAR est testé, l'algorithme se termine. À ce moment, on pourra vérifier que le SAR vaut 0b1001 1100 1010 = 2506, soit une tension produite par le DAC égale à (2506/4095) x 3,3 = 2,0195 V, valeur constituant une très bonne approximation des 2,2 V en entrée.
Cette dernière valeur du SAR est alors envoyée bit par bit sur l'interface SPI.
III-C. Liaison série SPI▲
III-C-1. Généralités▲
Dans les systèmes à base de microcontrôleurs, les échanges de données entre les composants se font maintenant le plus souvent avec des bus série.
Dans le cas particulier du MCP3208, la communication série de données numériques entre le Pi et le MCP3208 doit permettre de :
- configurer les entrées analogiques ;
- démarrer une conversion ;
- récupérer la valeur numérique après conversion.
Le bus série SPI (Synchronous Peripheral Interface) a été initialement développé par Motorola. D'autres fabricants (Microchip, Atmel, Texas Instruments, etc.) ont adopté ce type de liaison et de nombreux composants communiquant en SPI sont apparus (mémoires, horloges temps réels, convertisseurs analogique-numérique, numérique-analogique, capteurs divers…). En général, la mise en œuvre du bus SPI ne pose pas de problèmes, mais avec la multiplicité des modes de fonctionnement (fréquence, polarisation, phase de l'horloge…), et le manque de standardisation ou de spécifications techniques claires, on peut rencontrer des difficultés.
Quelques caractéristiques du bus SPI :
- Bus synchrone série à haut débit, multipoint (un maître et un ou plusieurs esclaves).
- Protocole simple. Pas d'adressage (mais sélection de l'esclave).
- Transmission à courte distance en full duplex sur deux lignes unidirectionnelles.
- Pas de contrôle physique de transmission des données.
Les échanges de données avec un microcontrôleur se font généralement octet par octet. La transmission s'effectue sur deux fils unidirectionnels nommés MOSI (Master Output Slave Input) et MISO (Master Input Slave Output).
Une horloge indépendante (fil SCLK pour Serial Clock) fixée par le maître synchronise les échanges (en général sur front de l'horloge). La fréquence de l'horloge peut aller jusqu'à 20 MHz, voire plus selon les performances des circuits reliés au bus.
Il n'y a pas d'adressage des esclaves (comme sur un bus i2C par exemple). L'esclave devient actif au moyen d'une ligne de sélection dédiée (fil /SSx : Slave Select x ou /CEx : Chip Enable x, généralement actif à l'état bas).
Le bus est donc constitué de trois fils auxquels il faut ajouter les fils de sélection d'esclave.
Il faut imaginer la liaison SPI maître-esclave transmettant les données selon le principe du registre à décalage (shift register) :
Une fois l'esclave sélectionné en abaissant sa ligne CEx, le transfert est possible et la communication est initiée par le basculement du signal d'horloge par le périphérique maître (ici, le Raspberry Pi). Sur front de l'horloge, le maître et l'esclave envoient chacun un bit sur leur ligne respective. Au bout de huit cycles d'horloge, un octet a été transféré du maître vers l'esclave, puis de l'esclave vers le maître (liaison full duplex). Cet échange simultané est souvent dissimulé par logiciel. Par exemple lorsque le maître veut envoyer des données vers l'esclave (opération d'écriture), les données retournées par l'esclave sont ignorées par le maître. Inversement, lors d'une opération de lecture des données envoyées par l'esclave, le maître envoie un octet nul qui sera ignoré par l'esclave. En fin de transmission avec l'esclave, sa ligne correspondante CEx doit être désactivée.
Deux bits de configuration définissent les modes de transmission du bus SPI. Ces deux bits sont nommés CPOL (Clock Polarity) et CPHA (Clock Phase). Il existe donc quatre modes de transmission différents. Pour une transmission correcte, il faut que les périphériques reliés au bus soient configurés selon le même mode.
- CPOL détermine si au repos l'horloge est au niveau bas (CPOL=0) ou haut (CPOL=1).
- CPHA détermine à quels fronts de l'horloge les données alignées sont échantillonnées et envoyées.
En général, les composants sont compatibles avec le Mode 0 qui constitue le mode par défaut (CPOL=0 et CPHA=0) :
III-C-2. Test de la liaison SPI▲
Si vous avez un doute sur le fonctionnement matériel de la liaison SPI du Raspberry Pi, il existe un moyen de diagnostic en mode loopback. Dans ce mode de fonctionnement, les broches MOSI et MISO du Pi sont reliées entre elles puis on regarde si les données envoyées sur la ligne MOSI sont bien reçues à l'identique sur la ligne MISO.
La procédure est la suivante.
- Par défaut, le pilote Linux gérant la liaison SPI n'est probablement pas activé dans le noyau. Dans un terminal :
ls /dev/spidev*
ls: impossible d'accéder à /dev/spidev*: Aucun fichier ou dossier de ce type
Vous pouvez activer le pilote soit depuis le menu Préférences/Configuration du Raspberry Pi de l'interface graphique du bureau Raspbian, soit depuis l'utilitaire raspi-config. Une fois le pilote activé, les deux interfaces pour chaque broche de sélection CE0 et CE1 apparaissent :
ls /dev/spidev*
/dev/spidev0.0
/dev/spidev0.1
- Récupérez le programme de test spidev_test.c, puis compilez-le :
wget https://raw.githubusercontent.com/raspberrypi/linux/rpi-3
.10
.y/Documentation/spi/spidev_test.c
gcc -o spidev_test spidev_test.c
Ce programme de test envoie une séquence d'octets sur la ligne MOSI et affiche les octets reçus sur la ligne MISO.
uint8_t tx[] =
{
0xFF
, 0xFF
, 0xFF
, 0xFF
, 0xFF
, 0xFF
,
0x40
, 0x00
, 0x00
, 0x00
, 0x00
, 0x95
,
0xFF
, 0xFF
, 0xFF
, 0xFF
, 0xFF
, 0xFF
,
0xFF
, 0xFF
, 0xFF
, 0xFF
, 0xFF
, 0xFF
,
0xFF
, 0xFF
, 0xFF
, 0xFF
, 0xFF
, 0xFF
,
0xDE
, 0xAD
, 0xBE
, 0xEF
, 0xBA
, 0xAD
,
0xF0
, 0x0D
,
}
;
Si vous lancez le test une première fois sans précaution, vous obtenez une série de zéros :
Ce qui est normal dans un premier temps puisqu'il n'y a rien de connecté sur la ligne MISO.
- Dans un deuxième temps, avec un fil femelle-femelle, vous allez connecter la broche MOSI (GPIO 10, broche 19) à la broche MISO (GPIO 9, broche 21). Il est plus prudent de faire la connexion avec le Pi hors alimentation. Lancez à nouveau le programme de test :
Cette fois, la séquence d'octets envoyés sur la ligne MOSI est bien réceptionnée sur la ligne MISO. La communication SPI s'effectue correctement, par contre ce test ne permet pas de vérifier le bon fonctionnement des lignes de sélection CE0 et CE1.
Source : Loopback test
III-C-3. Analyse d'une communication SPI avec le convertisseur MCP3208▲
L'extrait suivant de la datasheet du MCP3208 montre comment se présentent les données pour une transaction SPI, octet par octet avec une configuration en Mode 0 :
Les bits utiles (Start Bit, SGL/DIFF, D2, D1, D0, Null, puis B11 à B0) sont complétés avec cinq zéros en début de transmission pour former trois octets complets. Ce qui donne une transaction en 24 cycles d'horloge. N'oubliez pas que la transmission s'effectue en full duplex, les données sont émises et reçues simultanément.
Une fois le bit de Start transmis s'ensuivent quatre bits (Single/Diff, D2, D0 et D1) de configuration des entrées analogiques du MCP3208 :
En mode differential, on sélectionne la paire de voies d'entrée pour une mesure différentielle. En mode single-end, on sélectionne une voie d'entrée et la mesure de tension est référencée à la masse.
En C, les trois octets à transmettre seront préparés dans un tableau :
unsigned
char
mcp3208_TransmittedData[]={
(
1
<<
2
) |
(
input_mode<<
1
) |
((
channel>>
2
) &
0x01
),
channel<<
6
,
0x00
}
;
Le MCP3208 commence à échantillonner l'entrée analogique sur le quatrième front montant de l'horloge après le bit de Start. L'échantillonnage est terminé sur le cinquième front descendant de l'horloge après le bit de Start. Le MCP3208 retourne un bit Null (à zéro), puis les 12 bits issus de la conversion analogique-numérique (en commençant par le bit 11 de poids fort).
En C, les trois octets retournés dans un tableau sont récupérés et traités pour obtenir la valeur numérique de la conversion dans une variable :
unsigned
char
mcp3208_ReceivedData[3
];
unsigned
int
result;
// lancement de la communication SPI, 3 octets transmis et 3 octets en retour
bcm2835_spi_transfernb
(
mcp3208_TransmittedData, mcp3208_ReceivedData, 3
);
result =
(
mcp3208_ReceivedData[1
] &
0x0f
)<<
8
|
mcp3208_ReceivedData[2
];
IV. Capteur de température analogique LM335▲
Afin de tester le convertisseur analogique-numérique… un capteur de température analogique LM335, trouvé dans mes tiroirs.
Le LM335 se comporte comme une diode Zener avec une tension de claquage directement proportionnelle à la température en Kelvin (10 mV/°K). Correctement calibré à 25 °C, sa précision est inférieure au 1 °C sur une plage de température de plus de 100 °C (gamme de mesure entre -40 et +100 °C).
Si on souhaite l'utiliser pour mesurer des températures ambiantes en intérieur, disons entre 15 °C et 35 °C, la tension entre ses bornes V+ et V- devrait varier entre :
Vmin = (273,15 + 15) x 10 x 10-3 = 2,88 V
Vmax = (273,15 + 35) x 10 x 10-3 = 3,08 V
Pour une meilleure précision, on a intérêt à ce que la tension d'entrée du convertisseur analogique-numérique évolue dans la totalité de la plage de tension entre 0 et la tension maximale VREF, ce qui est loin d'être le cas ici où la tension d'entrée évolue de façon resserrée autour des 3 V à température ambiante. Toutefois, le quantum du MCP3208 avec VREF = 3,3 V est inférieur au millivolt, soit une résolution en température inférieure à 0,1 °C. On peut donc envisager un montage basique comme celui de la figure ci-dessous sans adaptation de tension particulière, avec la tension OUTPUT qui constitue la tension d'entrée du convertisseur analogique-numérique :
Pour être correctement polarisé, le constructeur recommande un courant traversant le LM335 évoluant entre 0,4 et 5 mA. Dans cette plage de courant, les caractéristiques du LM335 sont indépendantes de la valeur du courant.
Si on alimente le capteur avec V+ = 5 V , la tension aux bornes du capteur évoluant autour de 3 V à température ambiante : la tension aux bornes de la résistance R1 vaut approximativement 5 - 3 = 2 V (loi des mailles en électricité). Si on veut limiter le courant à 2 mA, la loi d'Ohm impose une résistance R1 = 2 / 0,002 = 1 kΩ.
Pour améliorer la précision, le constructeur recommande de calibrer le capteur à 25 °C avec un potentiomètre 10 kΩ selon le montage ci-dessous :
Pour calibrer le capteur, on doit régler le potentiomètre de façon à mesurer une tension en sortie de 2,98 V à 25 °C.
Relation entre la température en degré Celsius et la valeur numérique N après conversion analogique-numérique :
kitxmlcodelatexdvp\left\{\begin{matrix} V^{+}-V^{-}=0,01 \times T\left ( ^oK \right ) \qquad \text{caractéristique du LM335} \\ V^{+}-V^{-}=\frac{N}{2^{12}-1}\times V_{REF} \qquad \text{conversion analogique-numérique} \\ \Theta \left ( ^oC \right )=T\left ( ^oK \right )-273,15 \qquad \text{conversion Kelvin-Celsius} \end{matrix}\right.finkitxmlcodelatexdvpSoit :
kitxmlcodelatexdvp\Theta \left ( ^oC \right )=\left (\frac{N}{2^{12}-1}\times V_{REF} \right ) \times 100-273,15finkitxmlcodelatexdvpV. Schéma du montage▲
____
Sur ce genre de schémas, les broches des composants et du Pi sont disposées de façon logique (les alimentations sont regroupées ensemble, comme les broches de la liaison SPI, les entrées analogiques, etc.). Cette disposition logique n'a souvent rien à voir avec la disposition physique des broches sur les composants :
La tension d'alimentation VDD du MCP3208 et la tension de référence VREF sont à 3,3 V. La sortie du capteur de température est connectée directement à l'entrée Channel 0. R2 est un potentiomètre pour calibrer le capteur pour plus de précision. Le curseur du potentiomètre est connecté sur la broche ADJ du LM335. Le MCP3208 sera sélectionné sur la broche CE0 (Chip Enable 0) du Pi (le Pi peut sélectionner jusqu'à deux esclaves SPI sur ses broches CE0 et CE1).
Si vous optez pour le HAT de Nation Electronics, vous profitez bien entendu de certains avantages :
Moins de câblage, des configurations par cavaliers (jumpers), reconnaissance du HAT au démarrage du Pi grâce à la mémoire EEPROM, etc. Vous enfichez le HAT sur le port GPIO du Pi, et vous avez pratiquement une carte d'extension plug and play. Les broches d'entrées analogiques sont facilement accessibles sur le côté de la carte, et la reprise des 40 broches du port GPIO permet d'y brancher d'autres composants ou même d'empiler un deuxième HAT.
Avec ce HAT, le MCP3208 est alimenté en 5 V pris sur la broche GPIO 5 V du Pi par défaut, mais on peut aussi l'alimenter avec une source extérieure grâce à un connecteur sur le côté de la carte (il faut retirer le cavalier Back Power dans ce cas). Alimenter le MCP3208 en 5 V plutôt qu'en 3,3 V permet d'augmenter sensiblement la vitesse d'acquisition (jusqu'à 100 000 échantillons par seconde théoriquement d'après la documentation). Les niveaux logiques de tension du port GPIO du Pi étant sur du 0 - 3,3 V, l'électronique de la carte prévoit un indispensable convertisseur de niveau logique 3,3 - 5 V pour les échanges sur la liaison SPI.
VI. Acquisition de la température en langage C▲
VI-A. Installation de la bibliothèque bcm2835▲
Cette bibliothèque open source en C permet d'accéder au port GPIO du Pi sans passer par les pilotes Linux.
Lien vers la bibliothèque bcm2835 à installer sur le Pi : C library for Broadcom BCM 2835 as used in Raspberry Pi
Vous y trouverez le lien de téléchargement de la dernière version (fichier tar.gz).
La procédure d'installation y est également décrite :
# download the latest version of the library, say bcm2835-1.xx.tar.gz, then:
tar zxvf bcm2835-1
.xx.tar.gz
cd bcm2835-1
.xx
./configure
make
sudo make check
sudo make install
VI-B. Le code▲
La bibliothèque bcm2835 comporte les fonctions nécessaires pour gérer une communication SPI : SPI access.
#include <bcm2835.h>
#include <stdio.h>
#include <stdlib.h>
unsigned
int
readADC
(
unsigned
char
);
unsigned
int
median
(
unsigned
int
n, unsigned
int
*
x);
static
int
compare (
void
const
*
a, void
const
*
b);
int
main (
int
argc, char
**
argv){
unsigned
char
channel =
0
; // acquisition sur CH0
double
vref =
3
.3
; // tension de reference VREF=3,3V
unsigned
int
nbSamples =
5
; // filtre median sur 5 echantillons
unsigned
int
x[nbSamples];
unsigned
int
result;
unsigned
int
i;
double
temperature;
if
(!
bcm2835_init
(
))
{
printf
(
"
Echec bcm2835_init.
\n
"
);
return
EXIT_FAILURE;
}
if
(!
bcm2835_spi_begin
(
))
{
printf
(
"
Echec bcm2835_spi_begin.
\n
"
);
return
EXIT_FAILURE;
}
bcm2835_spi_setBitOrder
(
BCM2835_SPI_BIT_ORDER_MSBFIRST); // bit de poids fort en premier
bcm2835_spi_setDataMode
(
BCM2835_SPI_MODE0); // Mode 0 : CPOL=0, CPHA=0
bcm2835_spi_setClockDivider
(
BCM2835_SPI_CLOCK_DIVIDER_2048); // diviseur frequence
bcm2835_spi_chipSelect
(
BCM2835_SPI_CS0); // Chip Select 0
bcm2835_spi_setChipSelectPolarity
(
BCM2835_SPI_CS0, LOW); // Chip Select actif au niveau bas
// Lancement des conversions
for
(
i=
0
; i<
nbSamples; i++
)
{
x[i] =
readADC
(
channel);
}
result =
median
(
nbSamples,x); // valeur mediane
temperature =
((
double
)result /
(
double
)4095
*
vref) *
100
-
273
.15
;
fprintf
(
stdout,"
%.1f
\n
"
, temperature);
bcm2835_spi_end
(
);
bcm2835_close
(
);
return
EXIT_SUCCESS;
}
unsigned
int
readADC
(
unsigned
char
channel) {
// echantillonnage et conversion sur l'entree channel
unsigned
char
mcp3208_ReceivedData[3
];
unsigned
char
input_mode =
1
; // single ended = 1, differential = 0
unsigned
int
result;
// octets transmis
unsigned
char
mcp3208_TransmittedData[]={
(
1
<<
2
) |
(
input_mode<<
1
) |
((
channel>>
2
) &
0x01
),
channel<<
6
,
0x00
}
;
// lancement de la transaction
bcm2835_spi_transfernb
(
mcp3208_TransmittedData, mcp3208_ReceivedData, 3
);
// Recuperation de la valeur issue de la conversion
result =
(
mcp3208_ReceivedData[1
] &
0x0f
)<<
8
|
mcp3208_ReceivedData[2
];
return
result;
}
unsigned
int
median
(
unsigned
int
n, unsigned
int
*
x) {
// valeur mediane du tableau
qsort (
x, n, sizeof
(
unsigned
int
), compare);
if
(
n%
2
==
0
) {
return
((
x[n/
2
] +
x[n/
2
-
1
]) /
2
);
}
else
{
return
x[n/
2
];
}
}
static
int
compare (
void
const
*
a, void
const
*
b) {
// definition du tri par ordre croissant
unsigned
int
const
*
pa =
a;
unsigned
int
const
*
pb =
b;
return
*
pa -
*
pb;
}
Le code est compilé avec la commande :
gcc -Wall -o temperature_mcp3208 temperature_mcp3208.c -lbcm2835 -std
=
c99
Les étapes du code :
- Initialiser la communication SPI.
- Démarrer la conversion analogique-numérique de 5 échantillons, les valeurs sont contenues dans un tableau.
- Extraire la valeur médiane afin d'éliminer les quelques valeurs aberrantes dues aux parasites. Pour cela, on extrait la valeur au milieu du tableau trié avec la fonction qsort()).
- Convertir la valeur médiane en une valeur de température en degré Celsius.
- Afficher la valeur de la température formatée au 1/10 °C dans la console.
VI-C. Remarques sur la vitesse d'acquisition▲
La documentation du MCP3208 annonce une fréquence maximale de l'horloge SPI à 2 MHz pour une tension d'alimentation VDD = 5 V (et seulement 1 MHz avec VDD = 2,7 V). Avec une transaction SPI comportant 20 bits utiles, cela donne en théorie 100 000 échantillons convertis par seconde, ce qui est confirmé par la documentation qui annonce la valeur maximale de 100 ksps (kilo samples per second).
L'horloge SPI du Pi est configurée à la ligne :
bcm2835_spi_setClockDivider
(
BCM2835_SPI_CLOCK_DIVIDER_2048);
La fréquence de l'horloge SPI est calculée à partir de la fréquence de l'horloge principale et d'un diviseur (le diviseur est nécessairement une puissance de 2) :
Pour le Pi 3 : Fréquence horloge SPI = 400 MHz/diviseur
Pour les Pi 1 et 2 : Fréquence horloge SPI = 250 MHz/diviseur
Voir les explications ici : bcm2835SPIClockDivider
Possédant un Pi 3 avec le HAT MCP3208 de Nation Electronics, la fréquence avec un diviseur de 2048 est égale à 400 Mhz/2048 = 195 kHz. En théorie, on devrait pouvoir pousser la fréquence à 400 Mhz/256 = 1,5 MHz et s'approcher des performances maximales annoncées par le constructeur.
En pratique, avec Raspberry Pi sous Linux, il faut tempérer les performances, et on peut s'en rendre compte en visualisant les signaux avec un analyseur logique :
- les données qui transitent sur les lignes MISO et MOSI sont des paquets de 8 bits, et il faut donc au moins 24 cycles d'horloge, au lieu de 20 en théorie, pour échanger les trois octets. Ce qui ne donne plus que 2 Mhz/24 = 83 000 échantillons par seconde au maximum.
- Rajoutez à cela un délai avant de lancer la communication une fois la ligne CEx activée, et un délai entre chaque octet (environ 1,5 cycle d'horloge à chaque fois)… sans compter d'éventuelles latences de Linux.
Si on mesure le temps pris (avec clock_gettime) pour réaliser 10 000 acquisitions dans une boucle, voici un exemple de résultat obtenu (Pi 3 + HAT MCP3208 avec un diviseur 2048) :
Soit la durée d'une transaction correspondant à environ 28 cycles d'horloge.
La vitesse d'acquisition maximale tombe en théorie autour de 2 MHz / 28 = 71 ksps et le diviseur de l'horloge SPI ne devrait pas descendre en dessous de 256 (les valeurs obtenues à la conversion analogique-numérique deviennent en effet aberrantes avec un diviseur de 128).
De la vitesse de 100 ksps annoncée par le constructeur, nous descendons à 55 ksps en pratique, et encore moins si le MCP3208 n'est alimenté qu'en 3,3 V…
Évidemment, ces considérations n'ont pas grande importance pour quelques acquisitions à effectuer dans l'heure d'un signal qui varie très lentement, mais il faut en tenir compte en fonction de la rapidité de variation de vos signaux analogiques et même du nombre de signaux à acquérir (le MCP3208 ne permet de faire des conversions que pour une seule entrée à la fois).
On rappelle que pour un signal donné, le théorème de Shannon permet de connaître la fréquence d'échantillonnage minimale (au moins deux fois la fréquence maximale recherchée dans le signal). Si ce critère n'est pas respecté, le spectre du signal échantillonné présente des fréquences parasites qui n'appartiennent pas au signal de départ (phénomène de repliement spectral ou aliasing).
Pour plus de détails, chez National Instruments :
VII. Visualisation des données temporelles avec Node-RED▲
Comment visualiser des données de températures acquises en temps réel dans un navigateur Web ?
VII-A. Installation et premier flux▲
Node-RED est un outil de programmation graphique et open source soutenu par IBM pour mettre en œuvre des objets connectés. Uniquement en connectant des « nœuds » configurables à la souris, on fait circuler des flux de données pour piloter le port GPIO du Pi, réaliser des interfaces graphiques Web, accéder à une base de données (locale ou dans le cloud) ou à une plateforme IdO (Internet des Objets) pour stocker/visualiser/analyser des données, envoyer des mails ou des tweets suite à un événement, et beaucoup d'autres choses encore.
Node-RED est installé par défaut sur Raspbian Jessie. En cas de soucis, il faudra peut-être mettre à jour votre distribution Linux et la version de Node-RED. Voir Node-RED - Running on Raspberry Pi.
sudo su
apt-get update
apt-get upgrade
update-nodejs-and-nodered
Pour démarrer/arrêter le serveur Node-RED :
node-red-start
node-red-stop
Après avoir démarré le serveur, rendez-vous dans un navigateur à l'adresse http://adresse IP du Pi:1880/.
On commence par programmer un flux très simple :
Depuis de la palette des nœuds à gauche, faites glisser les nœuds suivants dans la fenêtre principale du flux Flow1, puis reliez-les entre eux à la souris :
Le nœud exec permet d'exécuter un programme extérieur. Il sera configuré pour lancer l'acquisition d'une température. Pour lancer le programme du chapitre précédent, cela donne :
Cliquez sur le bouton pour déployer le flux. À droite de l'interface Node-RED, sélectionnez l'onglet Debug. Puis cliquez sur le bouton carré du nœud d'injection (renommé Timestamp par défaut) pour injecter les données dans le flux. De nouvelles données de température devraient s'afficher dans la fenêtre Debug à chaque injection dans le flux :
VII-B. Visualisation avancée dans un tableau de bord▲
Pour visualiser les données dans une interface plus agréable, sous forme de jauge ou de courbe de température, il faudra installer des nœuds supplémentaires.
Pour cela, cliquez sur le bouton pour accéder au menu Node-RED, puis sélectionnez le menu Manage palette.
Recherchez et installez le module intitulé node-red-dashboard :
Il faudra éventuellement arrêter et relancer le serveur Node-RED pour découvrir les nouveaux outils dans la palette :
Vous pouvez maintenant reprendre le flux précédent et rajouter les nouveaux nœuds suivants :
Le flux devient :
Vous pouvez paramétrer le format d'affichage du graphique et de la jauge. Créez et paramétrez un tab node « Chez moi » avec deux group node « myGauge » et « myChart ».
Si on veut une acquisition de température toutes les cinq minutes, il faut paramétrer le timestamp :
Redéployez la mise à jour du flux. Pour visualiser le tableau de bord, cliquez sur le lien :
Exemple obtenu après plus de deux heures d'acquisition :
Après un peu de mise en forme du tableau de bord, et 24 heures d'acquisition plus tard :
VIII. Conclusion▲
Le MCP3208 de Microchip est un composant qui, à moindres frais, répond à l'absence de port analogique sur la célèbre carte Raspberry Pi. L'électronique du HAT MCP3208 de Nation Electronics offre en plus des facilités de réglage et de connexion ainsi qu'une alimentation du MCP3208 en 5 V augmentant sensiblement la vitesse d'échantillonnage et de conversion pour des signaux analogiques rapides. La bibliothèque bcm2835 en langage C propose des fonctions d'accès bas niveau au port GPIO, notamment liées au protocole de communication SPI, et ceci sans faire appel aux pilotes Linux. Bien calibré, le LM335 offre une précision au degré près et les courbes (chaque point de la courbe est la valeur médiane sur cinq échantillons) montrent une fidélité des mesures à ±0,1 °C. Node-RED est aussi un outil de choix pour l'Internet des Objets, il permet de réaliser quasiment sans aucune programmation de véritables prototypes d'applications connectées.
Je remercie Vincent PETIT pour la relecture technique et Claude Leloup pour la correction orthographique.