Leçons de niveau 14

Micro contrôleurs AVR/La conversion analogique numérique

Une page de Wikiversité.
Sauter à la navigation Sauter à la recherche
Début de la boite de navigation du chapitre
La conversion analogique numérique
Icône de la faculté
Chapitre no 11
Leçon : Micro contrôleurs AVR
Chap. préc. :Gerarduino
Chap. suiv. :AVR et robotique : ASURO
fin de la boite de navigation du chapitre
Icon falscher Titel.svg
En raison de limitations techniques, la typographie souhaitable du titre, « Micro contrôleurs AVR : La conversion analogique numérique
Micro contrôleurs AVR/La conversion analogique numérique
 », n'a pu être restituée correctement ci-dessus.

Nous allons présenter dans ce chapitre la conversion analogique numérique. Même si nous nous contentons de l’application aux AVR, un certain nombre de principes sont étudiés. Des applications simples sont aussi présentées.

La conversion analogique numérique dans le monde Arduino[modifier | modifier le wikicode]

L'environnement Arduino possède une primitive simple d'utilisation : analogRead. Elle retourne un nombre sur 10 bits puisque les convertisseurs sont des convertisseurs 10 bits. Cela veut dire qu’ils sont capables de retourner une valeur entre 0 et 1023, valeur représentant une tension entre 0 et 5V. Par exemple le programme

// programme d'exemple lecture conversion et envoi sur liaison
void setup()
{
  Serial.begin(9600);
}
 
void loop()
{
  Serial.print("Photocoupleur : ");
   Serial.println(analogRead(A2),DEC);
   delay(500);  
}

enverra dans la liaison série la valeur lue sur l'entrée repérée par A2.

Malgré la simplicité de cet exemple, il est nécessaire de se poser des questions importantes lorsqu'on utilise la conversion, en particulier sur la tension de référence.

Documentation de la conversion analogique numérique[modifier | modifier le wikicode]

La documentation est donnée sous forme de schéma un peu plus loin. Commençons donc par des généralités.

Généralités[modifier | modifier le wikicode]

La compréhension de cette partie nécessite d’avoir à l'esprit des idées générales sur le fonctionnement d'une conversion analogique numérique. Nous les rappelons maintenant :

  • Une conversion n’est pas instantanée
  • elle se fait avec un "algorithme" qu’il faut "dérouler" donc nécessite une horloge
  • Une tension de référence est nécessaire pour réaliser une conversion car celle-ci est essentiellement basée sur une comparaison.
    • En interne, elle est de 1,1 V dans les versions ATMagaX8/XX8 mais de 2,56 V dans les ATMega8/16/32.
    • En externe elle peut utiliser les broches AREF ou VREF. VREF permet d’utiliser Vcc comme référence.

La formule magique qui permet de calculer la conversion est :

où ADC est un nombre entier sur 10 bits. Ve est la tension d'entrée présente sur le convertisseur tandis que, comme son nom l'indique, Vref est la tension de référence.


Comment choisit-on la tension de référence avec la librairie Arduino[modifier | modifier le wikicode]

Par exemple si l’on désire utiliser la tension de référence interne (discutée plus loin) on doit écrire

void setup() {
    //permet de choisir une tension de référence de 1.1V
    analogReference(INTERNAL);
}

tandis que l’utilisation d'une référence externe (sur le broche AREF) se fait par :

void setup() {
    //permet de choisir une tension de référence externe à la carte
    analogReference(EXTERNAL);
}

Exercice 1[modifier | modifier le wikicode]

Une tension de référence de 2,56 V est utilisée comme référence pour une convertisseur d'un ATMega8 sur 10 bits.

1°) Quelle est la résolution en tension de ce convertisseur ?

2°) A quelle tension correspond le nombre 0x12F ?

3°) Une tension de 2V est présente sur l'entrée. Quelle sera la valeur de la conversion ?

4°) Un thermomètre LM35C peut mesurer une température entre -40 °C et +110 °C avec une précision de 1,5 °C et une résolution de 10mV/°C. Quel nombre retournera le convertisseur pour une température de 45 °C ?

Il est grand temps d'expliquer le fonctionnement de la conversion.

Des registres pour la conversion analogique numérique[modifier | modifier le wikicode]

Pour le registre ADMUX, le dessin est autosuffisant. Notez quand même les différentes possibilités sur les choix de la référence et de l'entrée convertie. Les entrées AREF et AVCC existent et doivent être connectées à la masse par l'intermédiaire d'une capacité de 100nF. AVCC est en plus connectée à VCC.

Conversion Analogue Numérique dan un ATMega328

Les quatre bits de sélection MUX3:0 laissent penser qu’il y a 16 entrées possibles à sélectionner, mais ce n’est pas vraiment le cas comme le montre le dessin. Huit sont clairement des convertisseurs tandis que d'autres sont reliés en interne et d'autres encore pas utilisées.

Pour le registre ADCSRA, ADEN autorise la conversion tandis que ADSC la fait démarrer (SC=Start Conversion). Ce bit est à 1 pendant toute la durée de conversion et repasse à 0 lorsque celle-ci est terminée.

  • ADATE relève du déclenchement automatique. On le mettra systématiquement à 0. Il est lié au registre ADCSRB que nous n'étudierons pas.
  • ADIF est le bit qui est positionné à 1 quand la conversion est terminée. Il peut être lié à une interruption en positionnant ADIE à 1. L'interruption effacera le bit ADIF. Si aucune interruption est utilisée, il faut effacer ADIF en écrivant un 1 dedans.

La division d'horloge doit être choisie pour être réalisée à 200kHz au maximum.

Exercice 2[modifier | modifier le wikicode]

Donner le morceau de programme qui lance la conversion et attend la fin de conversion. Deux techniques sont à explorer :

  • une avec le bit ADSC
  • une avec le bit ADIF (qui est un flag et nécessite une attention particulière)

Exercice 3[modifier | modifier le wikicode]

Pouvez-vous expliquer le result=ADCH du programme ci-dessous ainsi que chacun des commentaires. Y a-t-il erreur dans un commentaire ?

#include <avr/io.h>
int main() {
        unsigned char result;
        // Choose AREF pin for the comparison voltage
        //   (it is assumed AREF is connected to the +5V supply)
        // Choose channel 3 in the multiplexer
        // Left align the result
        ADMUX = (1 << REFS0) | (1 << ADLAR) | (3);
        // Start the ADC unit,
        // set the conversion cycle 16 times slower than the duty cycle
        ADCSRA = (1 << ADEN) | (1 << ADPS2) | (1 << ADSC);
        // Wait for the measuring process to finish
        while (ADCSRA & (1 << ADSC)) continue;
        // Read the 8-bit value
        result = ADCH;
}

Exemple d'utilisation avec interruption[modifier | modifier le wikicode]

Voici un exemple d'utilisation de la conversion analogique/numérique et son interruption associée.

#include <avr/io.h>
#include <avr/interrupt.h>
ISR(ADC_vect) {
  PORTD = ADCL;
  PORTB = ADCH;
  ADCSRA |= (1<<ADSC);
}
int main() {
  unsigned char result;
  DDRB = 0xFF;
  DDRD = 0xFF;
  DDRA = 0; // make port A an input for ADC
  ADCSRA = 0x8F; //enable interrupt select clk/128
  ADMUX = 0xC0; //{{Unité|2.56|{{Abréviation|V|volt}}}}ref and ADC
  sei();
  ADCSRA |= (1 << ADSC); //start conversion
  while (1); // wait forever
  return 0;
}

Nous ne donnerons pas plus d'information sur ce morceau de code et laissons le lecteur le lire attentivement.

Applications : lecture de plusieurs interrupteurs[modifier | modifier le wikicode]

L'utilisation d'un bit de PORT par interrupteur devient vite très consommatrice de broches du composant. Nous allons étudier un moyen simple d’éviter cette augmentation.

Réseau de résistances et interrupteurs pour Convertisseur Analogique Numérique

Voici un premier schéma d'exemple ci-contre

Exercice 4[modifier | modifier le wikicode]

1°) Trouver les valeurs des tensions en fonction de l'appui sur les boutons poussoirs. On supposera pour ce calcul que Vcc = 5V.

2°) Calculer les ADC correspondants sur 10 bits si VREF = 5V.

3°) Écrire un programme complet capable d'afficher complètement l'état des 4 interrupteurs sous forme binaire.

Exercice 5[modifier | modifier le wikicode]

On peut faire encore plus que dans le cas de l'exercice 4 en décodant tout un clavier avec une seule entrée.

Relier un clavier entier sur un CAN

1°) Calculer dans un tableau les tensions et valeurs 10 bits correspondantes pour l'appui d'une touche du clavier si la tension Vcc est fixée à 5V (ainsi que VREF).

2°) Peut-on prendre en compte l'appui de plusieurs touches ?

3°) Écrire un sous programme de lecture du clavier qui retourne la touche appuyée. On utilisera obligatoirement un switch.

Détection avec interruption[modifier | modifier le wikicode]

En robotique mobile, des interrupteurs peuvent être utilisés pour détecter des obstacles. Il faut alors réagir de toute urgence. Une interruption peut être adaptée à cette situation.

L'utilisation d'interruption dans ce contexte est cependant assez subtil dans la mesure où l'interruption CAN est seulement déclenchée quand la conversion est terminée. Il faut donc essayer de coupler tout cela avec ce que l’on appelle une interruption externe.

Préalable sur l'interruption externe[modifier | modifier le wikicode]

Cette partie détaille l’utilisation des interruptions INT0 et INT1, attachées aux pin PD2 et PD3 pour l'AVR ATMega328.

Registre EICRA[modifier | modifier le wikicode]

Le registre EICRA permet de choisir le mode de déclenchement de l'interruption avec deux bits de réglage par interruption (soit 4 pour l'ATMega328).

EICRA bit 7 6 5 4 3 2 1 0
Fonction ----- ----- ----- ----- ISC11 ISC10 ISC01 ISC00
Valeur initiale 0 0 0 0 0 0 0 0

Le tableau suivant donne la valeur des bits ISCx0 et ISCx1 pour configurer le mode de déclenchement associé à l'interruption INTx :

ISCx1 ISCx0 Déclenchement de l'interruption sur :
0 0 Un niveau bas sur l'entrée INTx
0 1 Un changement d'état sur l'entrée INTx
1 0 Un front descendant sur l'entrée INTx
1 1 Un front montant sur l'entrée INTx

Registre EIMSK[modifier | modifier le wikicode]

Le registre EIMSK permet d'autoriser ou non les interruptions INT1 et INT0.

EIMSK bit 7 6 5 4 3 2 1 0
Fonction ----- ----- ----- ----- ----- ----- INT1 INT0
Valeur initiale 0 0 0 0 0 0 0 0

Une mise à '1' du bit INTx permet d'autoriser l'interruption associée.

Registre EIFR Exernal Interrupt Flag Register[modifier | modifier le wikicode]

Le registre EIFR permet d'observer l'état des interruptions INT1 et INT0.

EIFR bit 7 6 5 4 3 2 1 0
Fonction ----- ----- ----- ----- ----- ----- INTF1 INTF0
Valeur initiale 0 0 0 0 0 0 0 0

Le bit INTFx passe à '1' lors du déclenchement de l'interruption.

Exemple[modifier | modifier le wikicode]

Pour déclencher une interruption à chaque changement d'état de la patte PD2 (donc sur les fronts montant et descendant), on pourra utiliser le code suivant :

ISR(INT0_vect) // programme d'interruption : le programme principal est interrompu,
{ // l'interruption exécutée et ensuite le programme principal continu normalement son exécution
  PORTB^=0x01; // modification de la sortie PB0
}
void setup() {
  DDRB=0x0F; // configuration de PB0 en sortie
  cli(); // arrêt des interruptions
  EICRA=0x01; // mode de déclenchement de l'interruption
  EIMSK=0x01; // choix des interruptions actives
  sei(); // autorisation des interruptions
}

void loop() {

}


Application au robot Asuro[modifier | modifier le wikicode]

Les détecteurs de contact de l'ASURO

Voici un exemple concernant le robot Asuro qui utilise un ATMega8 présenté dans un autre chapitre. Sur l'ATMega8 une interruption liée à la conversion analogique numérique existe mais elle n'est déclenchée que lors de la terminaison de la conversion et non lors d'un changement sur une entrée. C'est pour cela que les concepteurs d'Asuro ont utilisé le schéma ci-contre. L'appui sur un interrupteur sera aussi détecté sur l'entrée PD3 qui peut elle déclencher une interruption.

Exercice 6[modifier | modifier le wikicode]

1°) Calculer les changements de tensions lors des appuis d'interrupteurs si la tension d'alimentation est 5V.

2°) On vous donne un extrait d'un morceau de programme pour le Robot ASURO. Pouvez-vous déduire de ce programme les valeurs manquantes de ce morceau de programme ?


i = ADCL + (ADCH << 8);
taste = ((1024.0/(float)i - 1.0) * 63.0 + 0.5);

// K1 ou K2 (ou non exclusif)
if (taste == || taste == || taste == ) { //links kollidiert 

// K5 ou K6 (ou non exclusif)
if (taste == || taste == || taste == ) { //rechts kollidiert 
 
Interrupteurs K1 en haut et dans l’ordre avec K6 en bas

Indications : links en allemand signifie gauche tandis que rechts signifie droite. Les interrupteurs sont montrés dans la photo ci-contre. Ils sont disposés dans l’ordre de K1 à K6 avec donc K1 en haut et K6 en bas. Cette information vous permet de trouver lesquels sont activés pour la gauche et pour la droite.

3°) Donner le squelette d'un programme qui arrête le robot quand un des interrupteurs est fermé.

Solution de l'exercice 6[modifier | modifier le wikicode]

1°) Si nous utilisons un tableur comme ci-dessous,

A B C D E
1 Vcc R23 VADC ADC
2 5 1000000
3 R30 68000 =A2*B3/(B2+B3) =(C3*1023/A2) K6

qui est à continuer vers le bas pour chacun des interrupteurs, nous obtenons les résultats :

A B C D E F
1 Vcc R23 VADC ADC ((( 1024.0/(float)i - 1.0)) * 63.0 + 0.5)
2 5 1000000
3 R30 68000 4,9275362319 1008,1739130435 K6 1,4889598068
4 R29 33000 4,8529411765 992,9117647059 K5 2,4725406558
5 R28 16000 4,7058823529 962,8235294118 K4 4,5029325513
6 R27 8200 4,4565217391 911,8043478261 K3 8,2520205994
7 R26 4000 4 818,4 K2 16,3269794721
8 R25 2000 3,3333333333 682 K1 32,0923753666
9 R25//R26 1333,3333333333 2,8571428571 584,5714285714 K1 & K2 47,857771261
10 R29//R30 22217,8217821782 4,7846481876 978,9390191898 K6 & K5 3,3999168849

Nous avons supposé la tension de référence à 5V dans ce calculs. Cette tension ne semble pas très adaptées pour certaines variations. Il n'est malheureusement pas possible de prendre la tension de référence de 2,56 V de l'ATMega8 de l'Asuro ni celle de 1,1 V de l'Arduino UNO.

2°)


i = ADCL + (ADCH << 8);
taste = ((1024.0/(float)i - 1.0) * 63.0 + 0.5);

// K1 ou K2 (ou non exclusif)
if (taste == 2 || taste == 4 || taste == 1) { //links kollidiert 

// K5 ou K6 (ou non exclusif)
if (taste == 32 || taste == 65 || taste == 22) { //rechts kollidiert 
 

3°) La documentation schématique de l'Asuro montre que c’est INT1 qu’il faut utiliser. On n'a pas le choix, ce sont les concepteurs qui ont choisi avec leur Circuit Imprimé. Il faut tester, ce que nous n'avons pas fait car pour certains interrupteurs, la variation de tension est tellement petite que nous doutons qu'elle puisse déclencher INT1.

Voir aussi[modifier | modifier le wikicode]

Articles[modifier | modifier le wikicode]

Livres[modifier | modifier le wikicode]

L'utilisation d'un convertisseur analogique numérique pour lire l'état de plusieurs interrupteurs est décrite pour une autre architecture dans :

  • ARM Microcontroller Interfacing Harware and Software, Warwick A. Smith, Elektor 2010