vendredi 5 avril 2019

STM32 : l'ADC






STM32 : l'ADC



Cet article présente différentes techniques de travail avec l'ADC du STM32 :
  • le polling
  • les interruptions
  • la DMA
Si l'on veut se familiariser avec le STM32, quelques articles sont conseillés :
https://riton-duino.blogspot.com/2018/03/stm32f103c8t6-et-arduino.html
https://riton-duino.blogspot.com/2019/03/stm32-environnements-de-developpement.html
https://riton-duino.blogspot.com/2019/03/stm32-boitier-st-link.html
https://riton-duino.blogspot.com/2019/03/stm32-duino-deboguer.html

1. STM32DUINO

Le framework STM32DUINO, utilisable avec l'IDE ARDUINO, possède la même interface que le librairie standard ARDUINO. La mesure analogique se fait de la même façon avec analogRead().

Dans cet article nous n'allons pas aborder cette manière de procéder. D'une part elle est ultra connue, d'autre part son principe de fonctionnement n'est pas des plus optimisés.

Pour chaque appel à ananlogRead() l'ADC est totalement reconfiguré, ce qui provoque une perte de temps parfaitement inutile.

Remarque : l'utilisation d'une entrée analogique requiert une configuration, contrairement à ce qui se passe sur un ARDUINO :

pinMode(analogPin, INPUT_ANALOG);

Nous allons plutôt aborder ici la mise en œuvre des mesures analogiques à l'aide de la librairie ST (Stm32Cube).

Remarque : il est parfaitement possible d'utiliser la librairie Stm32Cube tout en restant sous IDE ARDUINO.

2. Mise en œuvre

2.1 Le logiciel

La mise en œuvre logicielle des différents essais a été fait avec l'IDE ARDUINO ou PlatfomIO. Dans les deux cas le framework ARDUINO est utilisé.

Le framework ARDUINO permet de ne pas se préoccuper des initialisations de base, en particulier celle de l'horloge système et de la ligne série pour la console.

2.2 Le hardware

Quelque soit la méthode choisie, il y a une marche à suivre commune :
  • choisir les pins sur lesquelles on veut faire la mesure
  • les configurer
  • savoir par quel canal ADC elles sont gérées

2.2.1. Choisir les pins de mesure

La première chose à faire est de récupérer le pinout de la carte, comme ici pour une NUCLEO F401RE :


On voit que les pins A0, A1, A2, etc. correspondent à PA0, PA1, PA4, etc..

2.2.2. Configuration

Il faut les configurer en entrée analogique :

int adc_configureGpios(void)
{
  GPIO_InitTypeDef gpioInit;

  __GPIOA_CLK_ENABLE();
  gpioInit.Pin = GPIO_PIN_0;
  gpioInit.Mode = GPIO_MODE_ANALOG;
  gpioInit.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(GPIOA, &gpioInit);
  return true;
}


2.2.3. Les canaux ADC

Dans la datasheet du microcontrôleur on trouve pour chaque pin le canal ADC correspondant :

https://www.st.com/resource/en/datasheet/stm32f401re.pdf

En page 39, on voit que PA0, PA1, PA4 correspondent aux canaux suivants :
  • PA0 : ADC1_IN0
  • PA1 : ADC1_IN1
  • PA4 : ADC1_IN4
Après avoir configuré l'ADC, on lui affecte un canal :

ADC_ChannelConfTypeDef adcChannel[2] =
{
  {ADC_CHANNEL_0, 1, ADC_SAMPLETIME_112CYCLES, 0},
  {ADC_CHANNEL_1, 1, ADC_SAMPLETIME_112CYCLES, 0}
};


// ...
  if (HAL_ADC_ConfigChannel(&adcHandle, &adcChannel[0]) != HAL_OK) {
    Serial.println("HAL_ADC_ConfigChannel failed");
    return;
  }
// ...


Le code complet : https://bitbucket.org/henri_bachetti/stm32duino-samples/

3. Le polling

Le polling est une technique qui ne fait appel ni aux interruptions ni à la DMA. L'application, après avoir configuré l'ADC, demande une conversion et attend que l'ADC ait terminé celle-ci pour aller lire le résultat.

Un STM32 peut posséder un seul ADC ou plusieurs.

3.1. Un seul ADC

Lorsque l'on désire lire des échantillons sur un seul canal la méthode est simple :
  • configurer une GPIO en entrée analogique
  • configurer l'ADC
  • configurer un canal ADC correspondant à la GPIO
  • démarrer l'ADC
  • tant que l'on a besoin de lire :
    • demander une conversion
    • attendre que l'ADC soit prêt
    • lire la valeur
  • stopper l'ADC
Lorsque l'on désire lire des échantillons sur deux canaux :
  • configurer deux GPIO en entrée analogique
  • configurer l'ADC
  • tant que l'on a besoin de lire : 
    • configurer le premier canal ADC correspondant à la GPIO N°1
    • démarrer l'ADC
    • demander une conversion
    • attendre que l'ADC soit prêt
    • lire la valeur
    • stopper l'ADC
    • configurer le deuxième canal ADC correspondant à la GPIO N°2
    • démarrer l'ADC
    • demander une conversion
    • attendre que l'ADC soit prêt
    • lire la valeur
    • stopper l'ADC 
On voit que la lecture de deux canaux demande pas mal de travail et que l'on perd par mal de temps à changer de canal.

Voir exemple : f401re-polling

3.2. Deux ADC

Lorsque l'on désire lire des échantillons sur deux canaux :
  • configurer deux GPIO en entrée analogique
  • configurer l'ADC1
  • configurer l'ADC2
  • configurer le premier canal ADC correspondant à la GPIO N°1
  • configurer le deuxième canal ADC correspondant à la GPIO N°2
  • démarrer l'ADC1
  • démarrer l'ADC2
  • tant que l'on a besoin de lire :
    • demander une conversion sur l'ADC1
    • demander une conversion sur l'ADC2
    • attendre que l'ADC soit prêt
    • lire la valeur
    • attendre que l'ADC soit prêt
    • lire la valeur
  • stopper l'ADC1
  • stopper l'ADC2
On voit que la lecture de deux canaux se fait sur deux ADC différents avec chacun un canal différent et que l'on ne perd plus de temps à changer de canal.

Le lecture peut très bien se faire sur deux ADC utilisant le même canal si l'on a besoin d'un nombre d'échantillons par seconde plus important.

Voir exemples : f429zi-dual-channel-poll, l476rg-dual-channel-poll

4. Les interruptions

L'utilisation des interruptions permet à l'application de ne pas aller lire l'ADC en permanence, en particulier lorsqu'elle est occupée à faire autre chose. L'application, après avoir configuré l'ADC, demande le démarrage de la conversion sous interruption et va consulter le résultat lorsque cela est nécessaire.

Il est à noter que si le temps d'échantillonnage est trop court l'exécution des interruptions risque de monopoliser  tout le temps CPU et l'application principale n'aura plus l'occasion de s'exécuter.
Voir exemple : f429zi-dual-channel-interrupt

Dans cet exemple utilisant deux canaux sur deux ADC différents, la principale difficulté réside dans la mise à jour des valeurs lues par la routine d'interruption. En effet le STM32 possède un seul vecteur d'interruption ADC. Pour savoir quel ADC a provoqué l'interruption, il faut aller consulter le registre de statut de chaque ADC : le bit EOC (End Of Conversion).

Avec cette technique il est possible d'effectuer une surveillance de tension par exemple, et réagir en exécutant une action même si l'application n'est pas apte à l'exécuter à ce moment-là.

Rappel : l'exécution d'un routine d'interruption doit être courte, obligatoirement plus courte que l'intervalle entre deux interruption.
On peut par exemple allumer un voyant, qui revient à manipuler une GPIO.
Mais envoyer un message par la ligne série en mode polling sera impossible. Il faudra utiliser là aussi une émission sous interruption.

5. La DMA

La DMA se rapproche de la gestion par interruptions, à la différence près que les transferts de données entre l'ADC et la mémoire sont faits sans intervention du processeur.

Voir exemple : f429zi-dual-channel-dma

Cet exemple utilise deux canaux sur un seul ADC. Le nombre de samples / seconde est évidemment deux fois moins important que dans l'exemple précédent utilisant 2 ADCs.

6. Les chiffres

Ceux-ci sont éloquents : https://bitbucket.org/henri_bachetti/stm32duino-samples/

Il est possible de monter jusqu'à presque 1.000.000 de samples par seconde avec certaines cartes.

Cette repository permet d'avoir accès au code.

7. Conclusion

L'ADC du STM32, d'une part est un ADC 12 bits, contrairement à celui d'un ARDUINO : 10 bits.
Certaines cartes sont équipées de plusieurs ADC, ce qui permet une rapidité de mesure accrue.


Cordialement
Henri

8. Mises à jour

10/03/2019 : 1. STM32DUINO

2 commentaires:

  1. Pierre Schirrer7 juin 2024 à 06:21

    Salut Riton. J'essaie depuis quelques jours sans succès de configurer un STM32F405 pour l'utilisation de multiples canaux ADC sur 2 ADC ( 7 sur ADC1 et 6 sur ADC2) en DMA. Le démarrage de la conversion est triggée par logiciel. En principe, les 13 conversions sont effectuées à la suite et les données récupérées et transférées dans un buffer en DMA. J'ai utilisé le call back de HAL pour informer que la conversion est terminée. Ca ne fonctionne que pour les 7 canaux de l'ADC1, je ne reçois jamais les données de l'ADC2. Je pourrais te fournir le source si besoin. A +

    RépondreSupprimer
    Réponses
    1. Salut Pierrot. J'espère que tu vas bien.
      Oui, je pourrais jeter un œil à ton code. Le plus simple est de le poster sur le forum ARDUINO avec ta question. D'autres personnes pourront éventuellement participer, bien que ta problématique dépasse probablement les compétences de la plupart des intervenants.
      Mais tu peux aussi utiliser un autre moyen à ta convenance (partage google, github, etc.).

      Supprimer