IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Voir le flux RSS

Le blog de f-leb

[Actualité] [Raspberry Pi Pico][SDK C/C++] Test du convertisseur analogique-numérique - Partie 1/2

Noter ce billet
par , 14/11/2024 à 09h00 (3781 Affichages)
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 12 bits. 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,3 V, on calcule la résolution en Volt (quantum), soit :
q = VREF / 2n = 3,3 / 212 = 0,81 mV

Une résolution inférieure au millivolt et une vitesse d'acquisition des échantillons annoncée jusqu'à 500 ksps (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 VS Code avec sa nouvelle extension pour les Raspberry Pi Pico. Je configure donc un nouveau projet C/C++ :

Nom : new-pico-project-adc.png
Affichages : 3755
Taille : 45,0 Ko

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 VS Code ou j'ai manqué un truc, exécution...

Nom : cmakelists-hwadc.png
Affichages : 1570
Taille : 58,9 Ko
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 :
Code C : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <stdio.h>
#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[26], et on fait une acquisition one-shot toutes les 20 ms dans une boucle infinie. La valeur issue de la conversion A/N est dirigée de la sortie standard vers la sortie GPIO UART0 TX, transmise à la sonde via son entrée GPIO UART0 RX et redirigée finalement vers l'USB du PC de développement sur le port ttyACM0 (115 200 bauds).

Je flashe le programme et on peut observer les valeurs dans le terminal série (onglet Serial Monitor) :

Nom : terminal-output-adc.png
Affichages : 1557
Taille : 268,0 Ko

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é. 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 :

Code Python : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
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 :

Nom : test2513.png
Affichages : 1544
Taille : 21,4 Ko

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 :
  • Vout = VREF x N/4095 = 3,3 x 2513/4095 = 2,03 V, valeur que je retrouve au multimètre au 1/100è de V près entre la masse et le curseur du potentiomètre. Le convertisseur ne renvoie pas n'importe quoi, c'est le minimum...
  • Ce n'est pas très rigoureux comme constat, mais (à vue de pif) la répartition suit une loi normale, je le constate aussi pour d'autres positions du potentiomètre.
  • L'écart-type est ici de 2,86, valeur que je retrouve à peu près pour d'autres positions du potentiomètre (écart-type toujours inférieur à 3). Si on admet que la population vérifie la loi normale, cela veut dire aussi que dans l'intervalle [moy - 2x(écart-type), moy + 2x(écart-type)], on retrouve 95% des échantillons. Avec un quantum=0,81 mV et un écart-type=3 dans le pire cas, 95% des échantillons sont à moins de 5 mV autour de la moyenne (2x3x0,81 = 4,83 mV).


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 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 :

Nom : test1535.png
Affichages : 1551
Taille : 21,1 Ko

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... 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...

Envoyer le billet « [Raspberry Pi Pico][SDK C/C++] Test du convertisseur analogique-numérique - Partie 1/2 » dans le blog Viadeo Envoyer le billet « [Raspberry Pi Pico][SDK C/C++] Test du convertisseur analogique-numérique - Partie 1/2 » dans le blog Twitter Envoyer le billet « [Raspberry Pi Pico][SDK C/C++] Test du convertisseur analogique-numérique - Partie 1/2 » dans le blog Google Envoyer le billet « [Raspberry Pi Pico][SDK C/C++] Test du convertisseur analogique-numérique - Partie 1/2 » dans le blog Facebook Envoyer le billet « [Raspberry Pi Pico][SDK C/C++] Test du convertisseur analogique-numérique - Partie 1/2 » dans le blog Digg Envoyer le billet « [Raspberry Pi Pico][SDK C/C++] Test du convertisseur analogique-numérique - Partie 1/2 » dans le blog Delicious Envoyer le billet « [Raspberry Pi Pico][SDK C/C++] Test du convertisseur analogique-numérique - Partie 1/2 » dans le blog MySpace Envoyer le billet « [Raspberry Pi Pico][SDK C/C++] Test du convertisseur analogique-numérique - Partie 1/2 » dans le blog Yahoo

Mis à jour 20/11/2024 à 08h13 par f-leb

Catégories
Raspberry Pi , Python , Raspberry Pi Pico

Commentaires

  1. Avatar de Jules34
    • |
    • permalink
    Comme d'habitude merci pour ces tutos intéressant, pour nous autres les moldu de la programmation !
  2. Avatar de f-leb
    • |
    • permalink
    Quoi ! Des sang-de-bourbe sur Developpez !
    Enfin... Je retourne travailler avec ma Pico magique à 133MHz...