Je reprends la suite de ma série de billets sur la programmation de la Raspberry Pi Pico avec le SDK C/C++ officiel de la Fondation.
Parmi les périphériques que l'on retrouve dans tout microcontrôleur qui se respecte, un convertisseur analogique-numérique (ou ADC pour Analog Digital Converter) est intégré à la puce RP2040 de la carte. Vous trouverez ses caractéristiques dans la datasheet du RP2040.
Il s'agit d'un convertisseur fonctionnant par approximation successive (SAR- Successive Approximation Register) de résolution 12bits. Le périphérique comprend un multiplexeur capable de diriger l'entrée du convertisseur vers les broches GPIO[26..28] de la carte.
Avec la tension de référence par défaut VREF=3,3V, on calcule la résolution en Volt (quantum), soit:
q=VREF/2n=3,3/212=0,81mV
Une résolution inférieure au millivolt et une vitesse d'acquisition des échantillons annoncée jusqu'à 500ksps (kilo samples per second), le maker qui doit juste lire le port analogique connecté au curseur de son potentiomètre rotatif a de quoi être (largement) satisfait des performances. Si on rajoute les fonctionnalités telles que: acquisition one-shot ou en free-running, multi-entrées en round robin, accès à la pile FIFO des résultats, accès DMA, interruptions, etc. n'en demandez plus...
Commençons par un programme de démonstration de ce convertisseur. Je travaille dans VSCode avec sa nouvelle extension pour les RaspberryPiPico. Je configure donc un nouveau projet C/C++:
661163
Comme j'utilise toujours ma sonde de débogage, je sélectionne la feature UART pour faire remonter les valeurs de conversion jusqu'au PC de développement.
Mais surprise... Je ne vois pas de feature à cocher pour le convertisseur A/N, je vais devoir modifier le fichier de configuration CMakeLists.txt à la main pour la compilation avec le makefile. OK, un oubli dans une version Beta de l'extension VSCode ou j'ai manqué un truc, exécution...
661164
Liaison UART activée, ajout de la librairie standard hadware-adc
Voici le fichier source adc-test.c, largement inspiré des démonstrations fournies dans la documentation, et que j'ai compilé avec succès:
#include
#include "pico/stdlib.h"
#include "hardware/gpio.h"
#include "hardware/adc.h"
int main() {
stdio_init_all();
//printf("ADC Example, measuring GPIO26\n");
adc_init(); // Make sure GPIO is high-impedance, no pullups etc
adc_gpio_init(26); // Select ADC input 0 (GPIO26)
adc_select_input(0);
const float conversion_factor = 3.3f / (1 << 12); // 12-bit conversion, assume max value == ADC_VREF == 3.3 V
while (1) {
uint16_t result = adc_read();
//printf("Raw value: 0x%03x, voltage: %f V\n", result, result * conversion_factor);
printf("%u\r\n", result);
sleep_ms(20);
}
}
Rien d'extraordinaire... On initialise, on dirige l'entrée du convertisseur vers la GPIO, et on fait une acquisition one-shot toutes les 20ms dans une boucle infinie. La valeur issue de la conversion A/N est dirigée de la sortie standard vers la sortie GPIO UART0TX, transmise à la sonde via son entrée GPIO UART0RX et redirigée finalement vers l'USB du PC de développement sur le port ttyACM0 (115200bauds).
Je flashe le programme et on peut observer les valeurs dans le terminal série (onglet Serial Monitor):
661244
Je tourne le potentiomètre et je vois bien les valeurs entre 0 et 4095 (=212-1) qui défilent dans le terminal série.
Pour poursuivre cette démonstration, j'ai voulu évaluer statistiquement la population des échantillons grâce à un programme Python, juste par curiosité8-). Le programme Python ci-dessous (qui inclue les bibliothèques serial, numpy et matplotlib) se connecte au port série ttyACM0, collecte 1000 valeurs de conversion successives, trace un histogramme sur lequel on superpose la courbe «en cloche» de la loi normale:
import serial # gestion du port série
import matplotlib.pyplot as plt # pour faire de beaux graphiques
import numpy as np
# connexion Linux au port série
serial_port = serial.Serial(port = "/dev/ttyACM0", baudrate = 115200)
# les mesures
mesures = []
serial_port.flushInput()
for i in range(1000):
val = serial_port.readline()
try:
m = int(val)
mesures.append(m)
except:
pass
# fermeture du port série
serial_port.close()
#print(mesures)
moyenne = np.mean(mesures) # calcul de la moyenne
std_m = np.std(mesures, ddof=1) # calcul de l'écart-type
print("Moyenne numérique = ", moyenne, "\nÉcart type numérique = ", std_m)
# La fonction de la loi normale
def gaussienne(x, A, moyenne, ecarttype):
return A / (ecarttype*np.sqrt(2*np.pi)) * np.exp(-(x-moyenne)**2 / (2*ecarttype**2))
#Intervalle de définition
x = np.linspace(min(mesures), max(mesures), 100)
y = gaussienne(x, 1000, moyenne, std_m)
# l'intervalle des mesures doit être adapté avec vos valeurs
plt.hist(mesures, range= [int(moyenne)-7.5, int(moyenne)+8.5], bins=16, edgecolor = 'black')
plt.xlabel("Valeur numérique")
plt.ylabel("Fréquence")
plt.legend(["mean:"+str(round(moyenne,2))+" std:"+str(round(std_m,2))])
plt.plot(x, y, 'red')
plt.show()
Par exemple, le potentiomètre rotatif étant réglé à une position intermédiaire quelconque, les valeurs dans le terminal série fluctuent autour de 2513. Je lance alors le programme Python et j'obtiens le graphique suivant:
661197
Si je répète l'opération pour la même position du potentiomètre, et si j'augmente à 5000 ou 10000 échantillons, j'obtiens des répartitions très similaires avec des valeurs de moyennes et d'écarts-types très proches.
Quelques constats de ces graphiques:
Je n'ai rien démontré rigoureusement, mais tout cela est rassurant sur la justesse et la précision des mesures, et même sur la qualité du convertisseur A/N d'autant plus que la qualité du montage avec la plaquette de câblage et la longueur des fils que j'ai utilisés:aie: peuvent perturber les mesures.
Bref, rien d'excitant, et je ne pensais même pas rédiger un billet avec ce programme Python assez inutile, mais...
Comment doit-on interpréter les résultats pour quelques positions bien définies du potentiomètre? Par exemple pour N=1535:
661198
Dans le terminal série, il y a beaucoup moins de fluctuations que d'habitude. On constate en effet sur le graphe qu'il n'y a plus de distribution en cloche, et que 85% de la population sont pile sur N=1535! Quelques échantillons sont un peu au-dessus de la moyenne, mais quasiment aucun échantillon n'est en-dessous. Cette dissymétrie par rapport à la moyenne est suspecte...:koi: Ce convertisseur serait-il ultra-précis pour certaines valeurs? (La réponse est... non, c'est même le contraire!)
En fait, il n'y a pas de hasard, et des explications seront données dans la deuxième partie de ce billet, mais ce convertisseur présente des défauts...
Soutenez le club developpez.com en souscrivant un abonnement pour que nous puissions continuer à vous proposer des publications.