Dans cette partie, vous allez manipuler un second périphérique très courant dans les microcontrôleurs : le convertisseur analogique-numérique, ou ADC en anglais (Analog to Digital Converter).
Caractéristiques d’un ADC
Un ADC est un périphérique permettant de convertir une tension d'entrée en une valeur numérique. Cette image est donc quantifiée, c’est-à-dire qu’elle ne peut prendre qu’un ensemble limité de valeurs.
Un ADC est décrit par trois caractéristiques :
un quatum qui est la valeur en tension d’un incrément de la valeur numérique,
l’étendue de mesure qui est la plage des tensions en entrée,
la résolution qui décrit le nombre de valeurs que peut prendre l’ADC sur l’étendue de mesure.
La figure ci-dessous décrit un ADC dont l’étendue est de 5V, c’est-à-dire que l’on peut mesurer une tension entre 0 et 5V, avec une résolution de 3bits (donc de 8 valeurs) soit un quantum de 0,625V.
Une autre caractéristique peut être associée à un ADC : sa précision. Elle dénote la qualité de la mesure qui est faite par rapport à la valeur réelle. Autrement dit, la précision renseigne sur la relation entre la tension réelle et la valeur numérique. Normalement, le circuit de l’ADC assure une bonne précision et sa résolution doit être en relation avec elle.
Principe du périphérique
Un périphérique ADC dispose de plusieurs voies analogiques (c'est-à-dire de plusieurs entrées) et donc d’un multiplexeur qui permet de sélectionner la voie sur laquelle faire la mesure.
Le cœur du périphérique est le module ADC qui procède à la conversion de la tension en une valeur numérique. Quel que soit la technologie utilisée, le circuit nécessite une horloge pour fonctionner et est un système séquentiel synchrone. Actuellement, le type d’ADC le plus courant fonctionne par un principe de pesées successives (ou à approximations successives).
La conversion n’est pas immédiate, il y a donc un temps lié à la mesure. Cette durée peut être décomposée en deux éléments :
le temps d’acquisition (dans un condensateur « tampon ») 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,
le temps de conversion qui est la durée nécessaire pour que le circuit réalise la conversion de l’échantillon en une valeur numérique.
Le temps minimum pour réaliser une conversion est donc la somme de ces deux durées et il n'est pas possible d'avoir une fréquence d'échantillonnage plus élevée.
Les bits permettant de contrôler un ADC sont usuellement :
un bit de mise en service pour démarrer le périphérique,
un bit de démarrage qui lance une conversion,
un drapeau signalant la fin de la conversion,
Une interruption peut aussi survenir lorsque le drapeau est levé, permettant ainsi un traitement de la mesure sous interruption.
Un ADC dispose de différents modes d’acquisition de ces voies (la liste ci-dessous n’est exhaustive) :
le mode single conversion permet de lancer une conversion sur une seule voie et de récupérer sa valeur soit en scrutant le drapeau soit sur interruption,
le mode continu permet de réaliser une nouvelle conversion dès que l'ADC en a terminé une. La conversion peut se faire sur la même voie ou sur une autre voie et ce de manière cyclique. Dès que la conversion est réalisée, celle-ci est signalée par le drapeau ou par l’interruption, par contre si la valeur n’est pas immédiatement traitée, elle est écrasée par la suivante. Pour différer le traitement de la mesure, les résultats de la conversion peuvent être stockés dans une table de registres internes au périphérique ou directement dans une table en RAM. Dans ce dernier cas, on parle de DMA (Direct Memory Acccess).
Utiliser un ADC du STM32F103
Le STM32F103 dispose de 2 ADC avec une résolution de 12 bits et avec 16 voies (notée INx). Les mesures ont une étendue comprise entre 0 et 3.3V. Afin de faire une application de démonstration, vous allez mesurer l’intensité d’un potentiomètre pour ensuite faire varier l’intensité d’une led.
Pour cela, vous allez supposer que le potentiomètre est connecté à la broche PB.0. D’après la documentation cette broche est connectée à la voie 8 de l’ADC1. Pour la led, vous allez réutiliser celle du chapitre précédent, ainsi que les fonctions liées à la PWM.
Commencez par une fonction pour configurer la broche 0 du port B en mode analog input (valeur 0000) :
void configure_gpio_pb0_analog_input(){
RCC->APB2ENR |= RCC_APB2ENR_IOPBEN;
GPIOB->CRL &= ~((0x1 << 0) | (0x1 << 1) | (0x1 << 2) | (0x1 << 3));
}
Le mode analog du GPIO permet de filtrer les bruits induits par le GPIO et donc d'avoir une mesure de meilleure qualité.
Ce que vous allez faire ensuite consiste à configurer un ADC pour lancer une conversion sur la voie 8 et en récupérer le résultat.
Commencez par écrire le prototype d’une fonction pour configurer l’ADC, soit
void configure_adc_in8(){
}
Comme tout périphérique, la première chose à faire est d’activer son horloge. Les ADC sur le STM32F103 sont liés à la grappe APB2, il faut donc commencer par la ligne de code
RCC->APB2ENR |= RCC_APB2ENR_ADC1EN;
Ensuite la documentation sur l’ADC page 218, nous indique que pour activer l’ADC, il faut mettre à 1 le bit ADON du registre CR2. La première fois que ce bit est passé à 1, l’ADC est allumé, ensuite à chaque fois qu’il passera à 1 une nouvelle conversion sera lancée. Ajoutez donc la ligne
ADC1->CR2|= ARC_CR2_ADON;
Maintenant, il s’agit de choisir la voie sur laquelle se fera la conversion. Pour cela, le périphérique dispose des registres, SQRx, dans lesquels est spécifiée la séquence des voies sur lesquelles les conversions sont réalisées. Par exemple, on peut définir une séquence composée de IN3, IN4, IN1, IN16 qui décrit l’ordre dans lequel seront réalisés les conversions, ici les voies 3, 4, 1 et 16. Le nombre de voies à convertir est à indiquer dans le champ L du registre SQR1.
Dans votre cas, vous voulez simplement convertir la voie 8. Il faut donc écrire dans L la valeur 0 (qui correspond à 1 conversion) et dans le premier champ de SQR3 la valeur 8, soit :
ADC1->SQR1&= ADC_SQR1_L;
ADC1->SQR3|= 8;
Pour bien faire les choses, vous allez aussi lancer la calibration de l’ADC afin de corriger les éventuelles erreurs de mesure. Pour cela il suffit de mettre à 1 le bit CAL du registre CR2 et attendre qu’il repasse à 0 ce qui signifie que la calibration a bien été réalisée, soit :
ADC1->CR2 |= ADC_CR2_CAL;
while (ADC1->CR2 & ADC_CR2_CAL);
Vous avez donc maintenant une fonction pour configurer votre ADC pour réaliser une mesure sur la voie 8 :
void configure_adc_in8(){
RCC->APB2ENR |= RCC_APB2ENR_ADC1EN; // validation horloge ADC1
RCC->CFGR |= RCC_CFGR_ADCPRE_DIV6; // passage de l'horloge ADC1 à 12MHz
ADC1->CR2|= ADC_CR2_ADON; // démarrage ADC1
ADC1->SQR1&= ADC_SQR1_L; // fixe le nombre de conversion à 1
ADC1->SQR3|= 8; // indique la voie à convertir
ADC1->CR2 |= ADC_CR2_CAL; // dÈbut de la calibration
while ((ADC1->CR2 & ADC_CR2_CAL)); // attente de la fin de la calibration
}
Maintenant réalisez une seconde fonction pour lancer la conversion et retourner le résultat. Nous avons vu que le lancement de la conversion se fait en écrivant 1 dans le bit ADON de CR2.
Pour détecter la fin de conversion, vous allez mettre en place un mécanisme de scrutation (nous n’utiliserons pas ici les interruptions) en guettant le passage à 1 du bit EOC du registre SR. Enfin, la valeur de la conversion se trouve dans le registre DR. Cette valeur est codée sur les 12 premiers bits du registre. Le code de cette fonction est alors
int convert_single(){
ADC1->CR2 |= ADC_CR2_ADON; // lancement de la conversion
While(!(ADC1->SR & ADC_SR_EOC) ) {} // attente de la fin de conversion
ADC1->SR &= ~ADC_SR_EOC; // validation de la conversion
return ADC1->DR & ~((0x0F) << 12); // retour de la conversion
}
En réutilisant les fonctions du chapitre précédent, le main
devient alors simplement
#include “stm32f10x.h”
int main (void)
{
int res = 0;
// Configuration de la PWM
configure_gpio_pa6_alternate_push_pull ();
configure_pwm_ch1_20khz(TIM3);
set_pulse_percentage(TIM3, 0);
// Configuration de l'ADC
configure_gpio_pb0_analog_input();
configure_adc_in8();
// Démarrage de la PWM
start_timer(TIM3);
while(1)
{
res = convert(); // conversion
set_pulse_percentage(TIM3, 100 * res / 0xFFF ) // mise à jour de l’intensité de la led
}
return 0;
}
La ligne set_pulse_percentage(100 * res / 0xFFF)
est un simple calcul proportionnel pour ramener la valeur mesurée par l’ADC (entre 0
et 0xFFF
puisque la résolution de l'ADC est de 12 bits) dans la plage de valeur admissible par la fonction set_pulse_percentage
.
Vous pouvez maintenant tester le programme en simulation et constater que vous pouvez faire varier la durée d’impulsion du PWM en fonction de la valeur que vous passez à l’ADC.
À la fin de ce chapitre, vous êtes capable :
de décrire le fonctionnement d'un ADC,
de configurer et de faire une conversion avec un ADC sur un STM32.