Echantillonner une valeur analogique

Ce tutoriel illustre comment convertir un signal analogique, dont la valeur est comprise entre 0V et 3.3V, en un signal numérique, dont la valeur est un nombre entier compris entre 0 et 4095.

Cette opération est assurée par un circuit intégré dans le STM32WB55 appelé Convertisseur Analogique-Numérique, abrégé par CAN en français. Dans tous nos tutoriels, pour éviter d’éventuelles confusions entre les acronymes français et anglo-saxons, nous ferons le choix d’utiliser systématiquement les acronymes anglo-saxons. Donc, nous désignerons par la suite les convertisseurs analogiques-numériques par l’acronyme ADC (pour Analog to Digital Converters).

Le nombre d’ADC intégré au STM32WB55 étant limité, mais aussi par suite de choix lors de la conception de la NUCLEO-WB55, toutes les broches de sortie ne permettent pas de réaliser des conversions analogiques-numériques. Si on s’intéresse aux connecteurs Arduino, seules les broches A0 à A5 sont reliées à des ADC.

Quelques précisions sur les ADC

Un ADC est décrit par trois caractéristiques :

  • Son quantum qui est la valeur minimum de la tension d’entrée qu’il peut convertir, associée à un incrément de la valeur numérique qu’il renvoie.
  • Son étendue de mesure ou tension de référence qui est la valeur maximum de la tension d’entrée qu’il peut convertir.
  • Sa résolution égale au maximum + 1 de la valeur numérique qu’il renvoie lorsque la tension d’entrée est égale à la tension de référence.

La figure qui suit illustre comment ces caractéristiques sont liées pour un ADC dont l’étendue est de 3.3V (c’est-à-dire que l’on peut mesurer une tension entre 0 et 3.3V) avec une résolution de 3 bits (donc de 8 valeurs) soit un quantum de 3.3 / (8-1) = 0.4715 (le “-1” vient du fait que l’ADC renvoie des valeurs entre 0 et 7 auxquelles doivent correspondre des tensions en 0 et 3.3V).


Principe d'un ADC 3 bits


La légende de cette figure mentionne un registre de conversion (ODR pour Output Data Register ; les registres sont des petites mémoires que l’on retrouve au cœur de tous les composants électroniques programmables). La valeur renvoyée par un ADC est écrite dans son registre ODR, et le STM32WB55 viendra la lire à cet emplacement.

La plupart des ADC nécessitent une horloge pour fonctionner, ce sont des systèmes séquentiels synchrones. Actuellement, le type d’ADC le plus courant fonctionne selon un principe dichotomique d’approximations successives (« Successive Approximations Register », on parle de « SAR ADC »).

La conversion n’est pas immédiate, sa latence peut être décomposée en deux périodes :

  • Le temps d’acquisition qui est la durée de l’échantillonnage de la tension avant de procéder à sa conversion. Cette durée doit être configurée en fonction du comportement dynamique du signal que l’on mesure. Concrètement, l’étage d’entrée de l’ADC est un condensateur qui doit avoir le temps de se charger jusqu’à atteindre la valeur de la tension d’entrée. Le temps de charge de ce condensateur dépend de l’impédance de la source mesurée et doit être ajusté en conséquence.
  • Le temps de conversion qui est la durée nécessaire pour que le circuit réalise la conversion de de la valeur capturée par le condensateur d’entrée en une valeur numérique selon l’l’algorithme SAR.

Le temps minimum pour réaliser une conversion est donc la somme de ces deux durées, mais on préfère communiquer sur son inverse, désigné par la fréquence d’échantillonnage.

L’ADC intégré au ST32WB55 est un SAR ADC avec

  • Une tension de référence de 3.3V
  • Une résolution de 12 bits, soir 212 = 4096 valeurs possibles, de 0 à 4095
  • Un quantum de 3.3V / 4096 = 0.0008V (approximativement)
  • Une fréquence d’échantillonnage maximum de 4.26 millions d’échantillons par seconde. Si un signal varie plus vite que cela, son échantillonnage et donc sa conversion ne seront pas réalisables.

Matériel requis

  1. La carte NUCLEO-WB55
  2. N’importe quelle référence de potentiomètre 10 KOhm, par exemple un PTV09A-4020F-B103 ou encore un module potentiomètre Grove.

Pour cette démonstration nous utiliserons donc un potentiomètre que nous brancherons sur A0 comme ceci :


potentiometre 2


Le code MicroPython

Les scripts présentés ci-après sont disponibles dans la zone de téléchargement.

Le script qui suit met à jour la valeur lue de la tension sur le potentiomètre un peu moins de deux fois par seconde. En fait, on effectue 500 conversions avec l’ADC et en fait la moyenne pour afficher une valeur raisonnablement précise à peu près toutes les demi-secondes. Bien évidemment, si vous manipulez le potentiomètre trop rapidement (plus qu’une fois par seconde) la valeur de la tension estimée ne sera jamais stable et le résultat sera difficile à interpréter.

Éditez le script main.py contenu dans le répertoire du disque USB virtuel associé à la NUCLEO-WB55 : PYBFLASH.

 # Objet du script :
# Exemple de configuration d'un ADC pour numériser une tension d'entrée variable grâce à un potentiomètre.
# Pour augmenter la précision, on calcule la moyenne de la tension d'entrée sur 500 mesures (1 mesure / milliseconde)
# Matériel requis : potentiomètre (Grove ou autre), idéalement conçu pour fonctionner entre 0 et 3.3V et connecté sur A0.

import pyb # pour gérer les GPIO
from time import sleep_ms # Pour temporiser

print( "L'ADC avec MicroPython c'est facile" )

# Tension de référence / étendue de mesure de l'ADC : +3.3V
varef = 3.3

# Résolution de l'ADC 12 bits = 2^12 = 4096 (mini = 0, maxi = 4095)
RESOLUTION = const(4096)

# Quantum de l'ADC
quantum = varef / (RESOLUTION -1)

# Initialisation de l'ADC sur la broche A0
adc_A0 = pyb.ADC(pyb.Pin( 'A0' ))

# Initialisations pour calcul de la moyenne
Nb_Mesures = 500
Inv_Nb_Mesures = 1 / Nb_Mesures

while True: # Boucle "infinie" (sans clause sortie)
	
	somme_tension = 0
	moyenne_tension = 0
	
	# Calcul de la moyenne de la tension aux bornes du potentiomètre

	for i in range(Nb_Mesures): # On fait Nb_Mesures conversions de la tension d'entrée
		
		# Lit la conversion de l'ADC (un nombre entre 0 et 4095 proportionnel à la tension d'entrée)
		valeur_numerique = adc_A0.read()
		
		# On calcule à présent la tension (valeur analogique) 
		tension = valeur_numerique * quantum

		# On l'ajoute à la valeur calculée à l'itération précédente
		somme_tension = somme_tension + tension

		# Temporisation pendant 1 ms
		sleep_ms(1)
	
	# On divise par Nb_Mesures pour calculer la moyenne de la tension du potentiomètre
	moyenne_tension = somme_tension * Inv_Nb_Mesures 
	
	# Affichage de la tension moyenne sur le port série de l'USB USER
	print( "La valeur moyenne de la tension est : %.2f V" %moyenne_tension)

Vous pouvez lancer le terminal PuTTY et observer la valeur en volts qui évolue lorsque vous tournez le potentiomètre :


Sortie Potentiometre


Pour aller plus loin

Les scripts présentés ci-après sont disponibles dans la zone de téléchargement.

L’API MicroPython permet de tirer profit des ADC du STM32WB55 en utilisant les fonctions disponibles dans le module pyb.ADC, spécifique à la carte Pyboard (laquelle est justement conçue autour d’un autre microcontrôleur de la famille STM32). L’exemple qui suit montre comment échantillonner des valeurs et les enregistrer directement dans un tableau en mémoire en uilisant Timer pour cadencer l’opération.

Par exemple, le code ci-dessous montre comment lancer 100 conversions et les charger dans un tableau d’entiers non signés de type ‘H’ codés sur deux octets (donc 200 octets au total enregistrés dans un tableau de cette taille).

# Objet du script :
# Exemple de configuration d'un ADC pour numériser une tension d'entrée variable grâce à un potentiomètre.
# Les mesures sont pilotées par un Timer à raison de 10 par seconde.
# Matériel requis : potentiomètre (Grove ou autre), idéalement conçu pour fonctionner entre 0 et 3.3V et connecté sur A0.

import pyb # pour gérer les GPIO
import array # pour enregistrer les données dans un tableau
from pyb import Timer # pour gérer les timers

# Initialisation de l'ADC sur la broche A0
adc = pyb.ADC(pyb.Pin( 'A0' ))

# Initialisation du timer 1 à la fréquence de 10 Hertz
tim = Timer(1, freq=10)

# Initialisation d'un tableau tampon contenant 200 octets regroupés en entiers non signés ('H', deux octets).
# Ce tableau contiendra donc 100 éléments de deux octets.
buf = array.array('H', bytearray(200))  

# Echantillonnage de 100 valeurs analogiques, à la fréquence de 10 Hz, donc pendant 100 secondes
adc.read_timed(buf, tim)

# Boucle sur l'ensemble des 100 éléments de buf, et affichage de leurs valeurs
for val in buf:
	print(val)

Vous trouverez d’autres explications sur des fonctions “avancées” des ADC en MicroPython pour la NUCLEO-WB55 dans la documentation des bibliothèques spécifiques à la carte Pyboard, ici.