«

»

Fév 04

Ada & ADC

Bienvenue dans ce nouvel épisode de « Ada & Cie. » !

Aujourd’hui, nous allons parler du module ADC. ADC signifie Analog to Digital Converter soit Convertisseur Analogique Numérique en français (CAN). Dans la partie sur les timers, nous avions remarqué que nous pouvions simuler un comportement analogique grâce aux PWMs. Nous allons maintenant réaliser le processus inverse. Car oui, le monde réel est constitué de valeurs analogiques ! Il faut donc pouvoir les transformer en données compréhensibles par l’ordinateur, c’est à dire en données numériques, c’est à dire une suite de 0 et de 1.

La carte de j’utilise (stm32f4 discovery) ainsi que toutes les stm32f4 possèdent 3 modules ADC. Pour les configurer nous auront besoin de deux genres de paramètres : ceux qui s’appliquent à tous les ADCs et ceux qui au contraire sont spécifiques à chaque module. Découvrons ensemble comment fonctionne un ADC à travers ses paramètres. Les fonctions se trouvent toutes dans le package Stm32.ADC.

LES PARAMÈTRES COMMUNS

Pour définir les paramètres communs, nous allons utiliser un record du nom de ADC_Common_Params. Celui-ci est composé de 4 champs.

Mode de type ADC_Mode : Il est possible de faire travailler les ADCs ensemble. Pour cela, il va exister plusieurs modes de fonctionnement. Tout d’abord, si l’on souhaite que chacun soit indépendant, on choisira Mode_Independent. Ainsi, il n’y aura aucune synchronisation entre les ADCs. Sinon, on peut décider que les conversions se passerons groupées. Cela sera utile quand on se servira des DMA (transferts direct dans la mémoire sans passer par le programme). Dans ce cas, ADC1 est toujours le maître, ADC2 lui est inférieur et ADC3 encore plus. On a le choix entre des modes où deux ADCs fonctionnent ensemble : les DualMode (ADC1 et ADC2), et les modes où trois ADCs fonctionnent ensemble : les TripleMode.

 

dualmode

 

regulart

(souces : Doc)

Les interactions pourront être de plusieurs natures. Tout d’abord, on a ce que l’on appelle le mode regular simultaneous. Les données sont lues en paralèle et stockées dans le registre de données de chaque ADC. Il diffère du mode injected simultaneous du fait que le registre de donnée utilisé n’est pas le même.

Nous avons ensuite le mode interleaved. Une conversion peut en réalité être déclenchée de deux manières : par le logiciel ou par une source extérieure appelée trigger. On peut, en choisissant le mode interleaved, utiliser un seul déclencheur pour plusieurs ADCs. Ils se déclenchent alors en cascade : d’abord ADC1, puis après quelques cycles ADC2, puis de même ADC3.

 

interleaved

(souces : Doc)

En choisissant le mode alternate trigger, c’est toujours le même déclencheur qui va être utilisé, mais cette fois de manière alternée : au premier coup, c’est ADC1 qui fait une conversion, au deuxième c’est ADC2, au troisième ADC3, au quatrième ADC1 et ainsi de suite…

alternate

(souces : Doc)

Tous ces modes peuvent être combinés. Le mode regular simultaneous avec le alternate trigger va faire en sorte que les conversions se passent normalement mais puissent être interrompues par un groupe injecté à chaque trigger, la première fois pour ADC1, deuxième ADC2,… Les nouvelles données sont écrites dans le registre pour le mode injecté. On peut aussi choisir le mode regular simultaneous et injected simultaneous pour pouvoir injectées des valeurs à lire dans le flux de conversion quand on le souhaite et comme on le souhaite.

regalter

(souces : Doc)

Pour plus de précision, vous pouvez vous référer à la documentation où les choses sont expliquées plus en détail (Doc).

En résumé nous avons le choix entre :

  • Mode_Independent
  • DualMode_RegSimult_InjecSimult
  • DualMode_RegSimult_AlterTrig
  • DualMode_InjecSimult
  • DualMode_RegSimult
  • DualMode_Interl
  • DualMode_AlterTrig
  • TripleMode_RegSimult_InjecSimult
  • TripleMode_RegSimult_AlterTrig
  • TripleMode_InjecSimult
  • TripleMode_RegSimult
  • TripleMode_Interl
  • TripleMode_AlterTrig

Prescaler de type Prescaler_Type : l’ADC est régulé par une horloge, APB2. Elle permet de définir la vitesse de fonctionnement de l’ADC. On peut choisir de la ralentir par 2, 4, 8 ou 16. Pour cela, on choisira Div_2, Div_4, Div_8 ou Div_16.

DMA_Access_Mode de type DMA_Mode_Type : ce champs va permettre de préciser comment les données vont être transférées à la mémoire par DMA quand on a choisit d’utiliser le multi-mode. On peut choisir :

  • Disabled : désactive le DMA.
  • Mode_1 : on envoie de manière alternées les données sortants de chaque ADC.
  • Mode_2 : on envoie des données sur 32bit composés de moitiés de données sortantes des ADCs
  • Mode_3 : idem mais un résultat sur 16 bits.

Pour plus de précision, vous pouvez vous référer à la documentation où les choses sont expliquées plus en détails (Doc).

Two_Sampling_Delay de type Two_Sampling_Delay_Type : ce champs définit la durée entre deux échantillons, c’est à dire deux mesures. On a le choix entre :

  • Delay_5Cycles
  • Delay_6Cycles
  • Delay_7Cycles
  • Delay_8Cycles
  • Delay_9Cycles
  • Delay_10Cycles
  • Delay_11Cycles
  • Delay_12Cycles
  • Delay_13Cycles
  • Delay_14Cycles
  • Delay_15Cycles
  • Delay_16Cycles
  • Delay_17Cycles
  • Delay_18Cycles
  • Delay_19Cycles
  • Delay_20Cycles

Maintenant que nous connaissons les paramètres communs aux ADCs, il nous faut les appliquer. Pour cela rien de plus simple : on utilise la fonction ADC_Init_Common.

ADC_Init_Common(Params : ADC_Common_Params)

Params est le record que l’on vient de définir.

LES PARAMÈTRES SPÉCIFIQUES

Chaque ADC peut être configuré avec des paramètres qui lui sont propres, qui sont définir sa manière de fonctionner. Pour rassembler les caractéristiques nous disposons du record ADC_Params. Décrivons chacun de ses composants.

Resolution de type Resolution_Type : ce champs va nous permettre de définir la précision de notre conversion. Plus on choisit d’écrire le résultat sur de nombreux bits, plus le résultat est précis. On peut donc choisir entre Resolution_12b, Resolution_10b, Resolution_8b ou Resolution_6b pour avoir une précision de 12, 10, 8 ou 6 bits.

Scan_Conv_Mode de type FunctionalState : permet de choisir si l’on veut utiliser ou non plusieurs channels en série. En réalité chaque ADC est divisé en 18 channels, chacun capable de faire une conversion différentes. Toutefois, il n’y a qu’un seul registre pour stocker les données. Les conversions se font donc en série, les unes après les autres, selon un ordre que l’on doit définir. En choisissant Enable, on peut démarrer la lecture d’un groupe de channel que l’on aura définit. Disable désactive cette option.

Continuous_Conv_Mode de type FunctionalState : permet de faire des conversions en continu. Si l’on choisit Enable, la même conversion (en groupe ou pas) sera indéfiniment répétée. Si l’on met Disable, une seule conversion aura lieu.

External_Trig_Conv_Edge de type External_Trigger_Edge_Type : permet de choisir l’action du déclencheur extérieur qui va faire débuter la conversion. On peut choisir :

  • None : aucun événement.
  • Rising : sur un front montant.
  • Falling : sur un front descendant.
  • Rising_Falling : à la fois sur un front montant et descendant.

External_Trig_Conv de type External_Trigger_Source_Type : permet de définir la source du déclencheur extérieur. Elles sont souvent liées à des timers. On peut choisir :

  • T1_CC1 : Timer 1 channel 1 signal
  • T1_CC2 : Timer 1 channel 2 signal
  • T1_CC3 : Timer 1 channel 3 signal
  • T1_CC4 : Timer 1 channel 4 signal
  • T2_TRGO : Timer 2 TRGO signal
  • T3_CC1 : Timer 3 channel 1 signal
  • T4_CC4 : Timer 4 channel 4 signal
  • T5_CC1 : Timer 5 channel 1 signal
  • T5_CC2 : Timer 5 channel 2 signal
  • T5_CC3 : Timer 5 channel 3 signal
  • T8_CC1 : Timer 8 channel 1 signal
  • T8_TRGO : Timer 8 TRGO signal.
  • Ext_IT11 : External pin event.

Data_Align de type Data_Align_Type : ce champs précise comment les données sont organisées dans le registre des données, et en particulier si elles sont à droite (Right) ou à gauche (Left) du registre.

alignment

Nbr_Of_Conversion de type Nbr_Of_Conversion_Type : précise le nombre de conversions à effectuer. Si l’on n’est pas en mode conversion continue, c’est le nombre de conversions que seront réalisées. Il s’agit d’un entier entre 1 et 8.

Nous avons tous les champs du record. Appliquons les à un ADC. Pour cela, nous utiliserons la fonction :

ADC_Init(ADC, Params);

où ADC est le numéro de l’ADC à configurer (entre 1 et 3) et Params sont les paramètres précédemment définis.

CONFIGURER UN CHANNEL

Nous allons maintenant apprendre à configurer un channel. C’est très simple, il faut utiliser la fonction :

Regular_Channel_Config(ADC, Channel, Rank, Sample_Time);

où :

  • ADC est le numéro de l’ADC (entre 1 et 3)
  • Channel est le numéro du channel, entre 1 et 18
  • Rank est le rang du channel dans le séquenceur, un entier entre 1 et 16.
  • Sample_Time est la durée d’un enchantillonage en nombre de cycles. On peut choisir entre Sample_Time_3Cycles, Sample_Time_15Cycles, Sample_Time_28Cycles, Sample_Time_56Cycles, Sample_Time_84Cycles, Sample_Time_112Cycles, Sample_Time_144Cycles, Sample_Time_480Cycles.

OPÉRATIONS SUR LES ADCs

Nous allons maintenant passer en revue quelques fonctions sur les ADCs.

Tout d’abord, une fois l’ADC configurer, il nous faut l’activer. Pour cela on utilise :

Configure_ADC(ADC, State);

Où :

  • ADC est le numéro de l’ADC, entre 1 et 3.
  • State : on choisit Enable pour l’activer, Disable pour le désactiver.

Il faut, pour pouvoir avoir des données, commencer la conversion. Pour cela, on dispose de la fonction :

Start_Conv(ADC);

où ADC est le numéro de l’ADC qui va commencer la conversion.

Pour lire une valeur, il suffit alors d’utiliser la fonction :

ADC_GetConversionValue(ADC);

où ADC est encore le numéro de l’ADC. Cette fonction retourne un Unsigned_16.

Il nous faudrait toutefois trouver un moyen de savoir quand une conversion est terminée. En effet, il se pourrait que l’on aille lire la valeur sans que celle-ci ne soit encore actualisée. Pour cela, nous allons faire appel aux flags. Les flags (drapeaux) permettent de signaler si certains événements ont eu lieu. Nous disposons des flags suivants :

  • AWD : le flag du watchdog (chien de garde), un dispositif permettant de surveiller que la tension reste bien entre deux valeurs (d’où son nom).
  • EOC : fin de conversion.
  • JEOC : fin d’une conversion injectée.
  • JSTRT : début de la conversion d’un groupe injecté.
  • STRT : début d’une conversion normale.
  • OVR : Overrun, ce qui signifie qu’une valeur qui n’a pas été lue à été ecrasée.

Pour aller lire ces flags, on utilise la fonction :

ADC_GetFlagStatus(ADC, Flag);

Où Flag est de type ADC_Flag et correspond à la liste ci-dessus.

On peut aussi nettoyer un flag pour indiquer que l’on a remarqué sa valeur grâce à la fonction :

ADC_Clear(ADC, Flag);

Enfin, la carte dispose d’un thermomètre intégré, il se situe sur le channel 16 de l’ADC1 sur ma carte (mais il peut aussi être sur le channel 18, c.f. la doc). Pour l’activer, nous utilisons :

ADC_TempSensorVrefintCmd(State);

où State est de type FunctionalState et active le thermomètre s’il vaut Enable, le désactive s’il vaut Disable.

Exercice

Voilà, nous disposons de tous les outils pour un petit exercice ! Coder un programme capable de lire la valeur de la température. Pour obtenir la température, on a la formule suivante :

Temperature = (VTemp-V25)/Avg_slope+25

Les valeurs sont définies dans la doc (plus spécifique pour ma carte). Pour moi, les valeurs sont :

  • V25 = 0.75 V
  • Avg_slope = 2.5 mV/C° (attention aux unités)

Pour obtenir Temp, tout dépend de la précision. Pour 12 bits, on a 2¹² – 1 = 4095 valeurs possibles, pour des tensions allant de 0 à 3.3, une règle de trois nous donne Vlue = Vtemp * 3.3/ 4095 Volts !

Une autre astuce : pour communiquer facilement avec l’ordinateur, vous pouvez utiliser le package Stm32.USB.

Solution :

ADC & INTERRUPTIONS Il y a le même problème dans la solution précédente qu’il y avait avec l’UART. Il nous faut des interruption pour pouvoir lire sans géner l’exécution globale du programme. Pour activer les interruptions, nous avons la fonction suivante : ADC_ITConfig (ADC, IT, State); Où IT est le flag de l’interruption à activer (ou désactiver selon la valeur de State). Il peut valoir : IT_AWD, IT_EOC, IT_JEOC ou IT_OVR. Exercice : Refaites le programme avec des interruptions cette fois. Solution :

Voilà, vous savez pleins de nouvelles choses sur l’ADC. Il nous restera à voir comment utiliser les DMAs et le tour sera joué !

A la prochaine !

Julien