Forum Électro-Bidouilleur

Merci de vous connecter ou de vous inscrire.

Connexion avec identifiant, mot de passe et durée de la session
Recherche avancée  

Nouvelles:

Le Forum est maintenant chiffré (préambule https). Bien sûr, des liens externes insérés dans les sujets vont demeurer inchangés. Mais la composition des pages du Forum est désormais sécurisée. Si des problèmes d'affichage surviennent, veillez à vider votre cache pour ce site.

Auteur Sujet: Programmer l'ADC de L'ATmega328P (exemple carte Arduino UNO)  (Lu 1716 fois)

sylvainmahe

  • Full Member
  • ***
  • Messages: 200
    • Voir le profil
Programmer l'ADC de L'ATmega328P (exemple carte Arduino UNO)
« le: avril 21, 2023, 10:13:32 am »

Bonjour à  vous,

J'ai souhaité écrire cette aide au fil de l'écoute des conversations dans les laboratoires de fabrication notamment, en effet il me semble important d'apporter des éléments de compréhension et de calculs à  chacun pour les cartes Arduino UNO afin de programmer le microcontrôleur ATmega328P en restant dans les spécifications constructeur.

Cette aide peut ouvrir des portes d'entrées et de sorties de ou vers d'autres paradigmes de comment penser la machine que nous programmons. Mais ceci est une démarche qui doit être entreprise volontairement. Je ne peux que montrer un des chemins possibles.

Je pense que le mieux ici est de commencer par quelque chose de simple : lire une broche de la carte Arduino UNO reliée en interne au convertisseur analogique/numérique du microcontrôleur ATmega328P, ceci sans utiliser l'ide Arduino car il bride la compréhension du fonctionnement interne du microcontrôleur.

Cette idée m'est venue en entendant la phrase suivante :

Citer
"Je peux faire lire à  ma carte Arduino UNO un signal avec une impédance de 1MΩ en entrée du convertisseur analogique/numérique."

Vous allez voir que cette affirmation est importante, car qui prendra cette information pourra y croire et faire des montages qui ne fonctionneront pas, ou fonctionneront dans certaines conditions extrêmes et extrêmement spécifiques qui dérogent complètement à  la fiche technique du microcontrôleur considéré.

Exemple complet en langage C de lecture ADC avec l'ATmega328P :

La première chose à  prendre en considération lorsque nous souhaitons programmer le microcontrôleur d'une carte Arduino UNO, est la fiche technique de l'ATmega328P : https://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-7810-Automotive-Microcontrollers-ATmega328P_Datasheet.pdf

À l'aide de cette fiche technique nous pouvons écrire le programme suivant qui est complet (je vais expliquer les différentes lignes ci-après) :

#define _ADCSRA (*(volatile unsigned char *)(0x7a))
#define _ADMUX (*(volatile unsigned char *)(0x7c))
#define _ADCLH (*(volatile unsigned int *)(0x78))

void startAnalog()
{
      _ADCSRA = 0b10000101;
}

unsigned int readAnalog()
{
      _ADMUX = 0b01000000;
      _ADCSRA |= 0b01000000;

      while ((_ADCSRA & 0b01000000) != 0b00000000)
      {
      }

      return _ADCLH;
}

int main()
{
      startAnalog();

      while (true)
      {
            readAnalog();
      }

      return 0;
}

Cet exemple est minimaliste mais permet sans bibliothèque additionnelle et sans ide Arduino de faire fonctionner le convertisseur analogique/numérique du microcontrôleur connecté sur la broche PC0.

Dans le programme la première chose à  écrire se sont les directives préprocesseur qui permettent d’appeler les pointeurs des adresses de registres dont vous allez avoir besoin :

#define _ADCSRA (*(volatile unsigned char *)(0x7a))
#define _ADMUX (*(volatile unsigned char *)(0x7c))
#define _ADCLH (*(volatile unsigned int *)(0x78))

ADCSRA permet de démarrer le convertisseur analogique/numérique, de choisir le pré-diviseur d'horloge, et aussi de démarrer une conversion et de savoir si elle s'est terminée.

ADMUX permet d'indiquer le bon canal connecté à  la bonne broche que nous souhaitons lire, et d'indiquer si la référence de tension est interne ou externe.

ADCLH est la valeur numérique sur 10 bits de la tension mesurée sur le canal choisi.

La fonction principale :

int main()
{
      startAnalog();

      while (true)
      {
            readAnalog();
      }

      return 0;
}

Nous souhaitons démarrer le convertisseur analogique/numérique, et effectuer une lecture d'une façon périodique, c'est pourquoi nous pouvons écrire deux fonctions : startAnalog et readAnalog
Nul besoin de plus dans cette fonction principale.

La fonction startAnalog est très simple, mais renseigner un octet cohérent pour ce registre ADCSRA mérite que l'on s'y attarde plus qu'un petit peu :

void startAnalog()
{
      _ADCSRA = 0b10000101;
}

Détail de l'octet 0b10000101 :

_ADCSRA = 0b10000101;
ADC activé & pré-diviseur d'horloge = 32

La fréquence du microcontrôleur est de 16MHz. La fréquence du multiplexeur du convertisseur analogique numérique est donc égale à  :

16000000Hz à· 32 = 500000Hz = 500kHz

La fiche technique du microcontrôleur indique que la ligne du convertisseur analogique/numérique a une capacité de 14pF.

Le calcul du temps alloué à  la charge de la capacité de 14pF à  la fréquence du multiplexeur sera de :

1000000µs ෠(16000000Hz ෠32) = 2µs

Nous allons considérer un potentiomètre de résistance 10kΩ ± 5% connecté au convertisseur analogique/numérique par la broche PC0 du microcontrôleur.

La fiche technique du microcontrôleur nous indique une largeur mémoire du convertisseur analogique/numérique sur 10 bits.

La résolution du convertisseur analogique/numérique est donc de :

(2 ^ 10) - 1 = 1023

La charge et la décharge d'un condensateur est un phénomène physique exponentiel, ceci fait appel à  la constante d'Euler qui se calcule de cette façon :

(1 + 1 ෠inf) ^ inf ≈ 2,718

La charge d'un condensateur fait appel à  la constante de temps Tau. Cette constante de temps se calcule via la multiplication de la résistance par la capacité. Nous pouvons donc multiplier la résistance maximale du potentiomètre par la capacité de la ligne du convertisseur analogique/numérique :

10500 à— 0,000000000014 = 0,000000147s = 147ns

La constante de temps Tau est donc de 147ns.

Afin d'être certain que la capacité de 14pF soit assez chargée ou assez déchargée dans le temps imparti par le multiplexage du convertisseur analogique/numérique, il nous faut calculer jusqu'à  quelle valeur nous souhaitons que cette capacité ce charge. Une bonne idée consiste à  la charger assez pour tomber supérieur ou égal à  la résolution du convertisseur analogique/numérique.

Comme indiqué le phénomène physique étant exponentiel, nous pouvons utiliser la bijection réciproque de la fonction exponentiel, soit la fonction logarithme naturel, car :

e ^ ln (x) = x

Le calcul va permettre de connaître le nombre de fois que nous devons multiplier la constante de temps Tau.

Le nombre de fois la constante de temps Tau égale à  la résolution du convertisseur analogique/numérique est donc de :

ln ((2 ^ 10) - 1) ≈ 6,93

Le temps de charge de la capacité de 14pF avec la résistance de 10k ± 5% et adc 10 bits est alors de :

10500 à— 0,000000000014 à— ln ((2 ^ 10) - 1) ≈ 1,018µs

Je rappelle la période du multiplexeur du convertisseur analogique/numérique avec pré-diviseur d'horloge de 32 qui est de :

1000000 ෠(16000000 ෠32) = 2µs

Avec donc un pré-diviseur d'horloge de 32 pour le multiplexeur du convertisseur analogique/numérique, et une impédance en entrée de 10500Ω, la capacité de 14pF aura le temps de se charger à  une valeur supérieure ou égale à  la résolution du convertisseur analogique/numérique, ce qui permet d'obtenir un fonctionnement très cohérent.

Nous pouvons également calculer la résistance maximale avec adc 10 bits et période de 2µs du multiplexeur :

(1 à· (16000000 à· 32)) à· (0,000000000014 à— ln ((2 ^ 10) -1)) ≈ 20,612kΩ

Toujours dans la fiche technique du microcontrôleur, il est indiqué que le multiplexeur du convertisseur analogique numérique peut fonctionner avec une impédance en entrée de 1kΩ à  100kΩ, ceci est cohérent avec le choix du pré-diviseur d'horloge qui peut aller de 2 à  128, cela se retrouve par calcul comme précédemment :

(1 à· (16000000 à· 2)) à· (0,000000000014 à— ln ((2 ^ 10) - 1)) ≈ 1,288kΩ

(1 à· (16000000 à· 128)) à· (0,000000000014 à— ln ((2 ^ 10) - 1)) ≈ 82,451kΩ

Il est indiqué également que le convertisseur analogique/numérique est optimisé pour une impédance en entrée de 10kΩ, soit la formule du temps de charge de la capacité de 14pF avec une impédance en entrée de 10kΩ ± 0% :

10000 à— 0,000000000014 à— ln ((2 ^ 10) - 1) ≈ 970ns

Avec une impédance de 10kΩ en entrée la capacité met 970ns à  se charger à  une valeur égale à  la résolution du convertisseur analogique/numérique.

Autre importance, pour ce cas une fréquence de fonctionnement du microcontrôleur plus élevée n'est pas forcément intéressante, un exemple avec une fréquence de microcontrôleur plus basse réglée sur 8MHz :

(1 à· (8000000 à· 2)) à· (0,000000000014 à— ln ((2 ^ 10) − 1)) ≈ 2.576kΩ

(1 à· (8000000 à· 128)) à· (0,000000000014 à— ln ((2 ^ 10) − 1)) ≈ 164.902kΩ

Les calculs ci-dessus montrent que la capacité de la ligne du convertisseur analogique/numérique sera chargée davantage avec un microcontrôleur cadencé à  8MHz.


À présent vous pouvez renseigner un octet cohérent avec votre matériel pour le registre ADCSRA.
Si vous n'utilisez qu'un port ADC vous ne verrez sans doute pas de phénomène négatif au fait d'utiliser une impédance d'entrée trop élevée par rapport à  la fréquence de multiplexage choisie, car la capacité sera chargée sur plusieurs échantillons successifs, mais la malfaçon n'en reste pas moins présente et sera bien visible lorsque vous utiliserez plusieurs ports.

La suite est plus succinct, explications de la fonction readAnalog :

_ADMUX = 0b01000000;
C'est le port PC0 relié à  l'ADC0 (convertisseur analogique/numérique canal 0) & le choix de la broche AVCC pour l'alimentation du convertisseur analogique/numérique et référence de tension avec condensateur externe via la broche AREF.

_ADCSRA |= 0b01000000;
Correspond au démarrage d'une conversion analogique/numérique.

while ((_ADCSRA & 0b01000000) != 0b00000000)
{
}

Ceci boucle tant que la conversion n'est pas terminée, le bit repasse à  0 si elle est terminée.

return _ADCLH;
C'est la valeur numérique sur 10 bits de la tension mesurée sur PC0.

Il ne manque plus qu'à  compiler le programme avec avr-gcc, le compilateur gcc dédiée au microcontrôleurs d'architecture AVR, et le téléverser dans le circuit intégré avec avrdude.

N'hésitez pas si vous avez des interrogations, des idées, contributions, suggestions à  apporter.
« Modifié: avril 22, 2023, 12:28:49 pm par sylvainmahe »
IP archivée