S’il y a bien une chose qui manque à pas mal de microcontrôleurs « basiques », c’est bien un convertisseur numérique vers analogique (CNA, aussi appelé DAC, en anglais). C’est pourquoi je vous en présente un ici : le MCP4725, de Microchip.
En effet, si comme moi vous utilisez souvent des Arduino Uno, Nano, ou autres classiques, vous avez certainement dû remarquer qu’il leur manque quelque chose. En effet, on peut parfaitement recevoir (lire/mesurer) des tensions analogiques, mais jamais en émettre. Car il n’y a pas la moindre sortie prévue à cet effet (en fait, il n’y a que des sorties digitales !). Du coup, ajouter un MCP4725 à son Arduino permet de pouvoir générer une tension variable en sortie, et ce, en toute simplicité (car le pilotage se fait en i2c, c’est à dire avec 2 fils seulement). C’est pourquoi je vous propose de le découvrir aujourd’hui, dans ce tutorial !
Nous allons voir ici le convertisseur numérique analogique MCP4725, et ce, aussi bien en version « seul » (le circuit intégré, tel que fabriqué par Microchip), qu’en version montée sur « mini-plaquette PCB » (avec tous les composants « nécessaires » autour de lui, déjà câblés dessus). Bien sûr, libre à vous d’utiliser l’une ou l’autre de ces versions, selon vos besoins, … ou tout autre DAC d’ailleurs ! Sinon, vous pouvez également opter pour un autre microcontrôleur, qui pourra générer un signal analogique en sortie, tout en restant compatible avec l’IDE Arduino. Car oui, il en existe ! Je pense notamment au Seeeduino XIAO, dont je vous parlerai prochainement ! Mais pour l’heure, voyons ce circuit intégré compact, facile à mettre en œuvre, et pas cher, sans plus attendre 😉
Module | Description | Lien |
---|---|---|
Module MCP4725 monté sur PCB (avec sélection d'adresse i2c possible) |
Caractéristiques du MCP4725 (issues du datasheet), pinout, et schematic
Le MCP4725 se présente sous la forme d’un petit circuit intégré à 6 broches. Fabriqué par Microchip, on le retrouve aussi bien vendu « seul », au détail (en boitier SOT23), que déjà intégré, sur une mini-plaquette PCB, avec plusieurs composants additionnels intéressants, raccordés à lui.
Modèle | MCP4725 « seul » | MCP4725 « monté sur PCB » |
---|---|---|
Aperçu | ||
Avantages | Idéal pour l’intégrer de manière compacte, dans ses propres circuits imprimés PCB | Idéal pour les débutants, ou pour ne pas « se prendre la tête » (car tout est déjà intégré, avec tous les composants additionnels nécessaires raccordés dessus) |
Inconvénients | Pas facile à manipuler/souder, si vous débutez, et nécessite d’acheter les composants additionnels en plus | Prend plus de place que la puce seule, à cause des connectiques et trous de fixation |
Lien (description/prix) | Version « MCP4725 seul » | Version « MCP4725 complet » |
Quelle que soit la version que vous utilisez ici, les caractéristiques techniques du circuit intégré, en lui-même, seront bien évidemment les mêmes. Car les composants additionnels mis en œuvre au niveau de la plaquette PCB ne sont là que pour protéger le paramétrage physique I2C, découpler le circuit intégré MCP4725, et tirer les lignes i2c vers le haut (résistances de pull-up). En clair : il n’y a pas de mauvais choix, à opter pour l’une ou l’autre de ces versions 😉
Généralités
Comme évoqué en intro, le MCP4725 est un convertisseur numérique vers analogique. C’est-à-dire qu’il suffit de lui demander la tension qu’on souhaite en sortie, et il la sort ! Enfin… presque, car plus exactement, c’est un pourcentage de la tension d’alimentation, que le MCP 4725 envoie en sortie, comme nous le verrons par la suite !
Au passage, n’hésitez pas à vous référer à tout moment au datasheet du MCP4725 (en anglais), mis à disposition par le fabricant (Microchip). Cela pourra vous donner bien d’autres infos encore, au besoin.
Du reste, les caractéristiques principales de ce DAC sont :
- Une tension d’alimentation comprise entre 2,7 et 5,5 volts, en conditions normales (ce qui est donc idéal, si vous le raccordez à un Arduino !)
- Une consommation vraiment faible (210 µA, typiquement, et 60 nA environ, en mode « power-down »)
- Une résolution de 12 bits (soit 4096 niveaux de tension possibles, entre 0V et quasi +Vcc)
- Une mémoire EEPROM intégrée (permettant de mémoriser la valeur de tension demandée en sortie, pour pouvoir la générer automatiquement, lors de la prochaine mise sous tension)
- Un adressage possible sur 8 adresses I2C différentes (0x60 ou 0x61, pour les plus classiques, suivant si on relie la broche A0 à GND ou +Vcc ; et 0x62 à 0x67, si vous achetez une puce pré-gravée avec ces adresses, directement auprès du fabricant, ou de votre fournisseur)
- Une sortie « rail-to-rail », c’est à dire allant de 0 volt à +Vcc (ou plutôt « quasi » Vcc, car la tension max de sortie est limitée à 4095/4096*Vcc, en réalité ; mais ce qui est très proche de Vcc, quand même !)
- Un courant de sortie pouvant monter jusqu’à 15 mA, typiquement
- Le choix entre 3 vitesses possibles de communication I2C : le mode Standard (à 100 kbps), le mode Fast (à 400 kbps), et le mode High-Speed (à 3,4 Mbps) ; à noter que les arduino « classiques », tels que le Uno ou le Nano, seront limités à 400 kbps, tout au plus !
- Et la facilité de « pilotage » de ce convertisseur numérique-analogique, grâce aux « commandes i2c » (il n’y en a que 3 à connaître, au final)
Les inconvénients ou défauts de cette puce, car il y en a :
- On a que 1 seule sortie analogique (mais c’est déjà mieux que rien !)
- La marge d’erreur typique est de 0,02% de la plage maximale (Full Scale Range), mais peut atteindre 0,75% du FSR (ce qui explique, en partie, que la tension de sortie ne soit pas précisément à la tension voulue)
- La tension de référence est confondue avec la tension d’alimentation, ce qui induit inévitablement un manque de précision au niveau de la sortie. En effet, si on alimente par exemple le MCP4725 en +4,95 volts (au lieu de 5V), alors on ne pourra jamais atteindre le niveau des 5 volts. Ce qui induit d’ailleurs au passage « l’absolue » nécessité d’avoir une tension parfaite filtrée et régulée, au niveau de l’alim du circuit intégré, afin d’avoir un maximum de précision en sortie. Sinon : tout « bruit » présent sur l’alim, se retrouvera directement en sortie !
Le saviez-vous ? Par défaut, le réglage d’usine du MCP4725 permet à son DAC de sortir une tension égale à Vcc/2, sur sa sortie Vout. Du coup, si vous utilisez un MCP 4725 neuf, et que vous l’alimentez en +5V sur son entrée Vcc, alors vous devriez obtenir une tension de 2,5 volts en sortie ! C’est d’ailleurs un bon moyen de vérifier si la puce fonctionne bien, au premier démarrage !
Voilà pour les grandes lignes ! À présent, voyons le brochage du MCP 4725, en version seul, et en version « tout équipé » !
MCP4725 pinout (brochage de la puce Microchip, en version seule, ou sur mini-plaquette)
Avant de voir comment raccorder cette puce électronique, voyons tout d’abord son brochage. Et ce, aussi bien en « version seule » (en boitier SOT23, tel que vendu par le fabricant), qu’en « version intégrée » (c’est-à-dire : monté sur un mini-PCB, avec plusieurs composants additionnels tout autour).
En fait, que vous optiez pour l’une ou l’autre de ces versions, vous allez à peu de choses près vous retrouver avec les mêmes entrées/sorties. En effet :
- VCC est la tension d’alim (de 2,7 à 5,5 volts)
- GND est la masse (0V)
- Vout est la broche de sortie du convertisseur numérique/analogique
- A0 est la broche de définition d’adresse I2C, qui doit être mise à VCC ou GND, selon l’adresse i2c que vous souhaitez, pour ce DAC
- SCL est la ligne d’horloge i2c, support de communication (unidirectionnel)
- Et SDA est la ligne de données (bidirectionnel)
Au final, la seule différence entre ces deux versions est que l’une est une puce seule, et l’autre, une puce avec quelques composants additionnels raccordés dessus, pour faciliter son usage. D’ailleurs, voyons à présent le schéma électronique de cette carte PCB toute intégrée, pour y voir plus clair !
MCP4725 schematic (schéma électronique de la mini-plaquette PCB)
Si on suit les pistes d’une mini plaquette PCB du MCP4725, voici un exemple de schéma électronique qu’on pourrait dresser :
En somme, on s’aperçoit qu’il n’y a que 4 composants additionnels, adjoint au DAC :
- 1 x résistance de pull-up sur la ligne SDA, faisant 4,7k
- 1 x résistance de pull-up sur la ligne SCL, faisant 4,7k également
- 1 x condensateur de découplage, pour le circuit intégré MCP 4725
- 1 x résistance de protection de 10k, pour la sélection physique de l’adresse I2C, via l’entrée A0
À ceci s’ajoute 2 ponts de sélection :
- SJ1 : permettant la connexion/déconnexion individuelle des résistances de pull-up, des lignes i2c
- SJ2 : permettant le choix de l’adresse I2C du convertisseur numérique-analogique, via la broche A0 de la puce
Concernant la résistance de protection de 10 kohms, elle est uniquement là pour éviter tout risque de court-circuit franc, lorsqu’on réalise un pont de soudure pour sélectionner l’adresse I2C. En effet, comme le but est d’envoyer au milieu du pont (SJ2) soit GND soit VCC, la moindre erreur ou bavure provoquerait ici un beau court-circuit, sans résistance de limitation de courant !
Du coup, en mettant une résistance sur l’une des deux alim (ici GND), cela permet de sécuriser le tout. Au passage, la résistance étant du côté de la masse, cela veut dire que si vous faites une soudure des 3 pastilles de sélection (SJ2), ce sera VCC qui sera envoyé sur la broche A0 du MCP4725.
Enfin, à noter que les résistances de pull-up des lignes SCL et SDA peuvent être déconnectées, en rompant les liaisons au niveau des pastilles de sélection (SJ1).
Finalement, cette version PCB est simplement conçue dans un but pratique : permettre d’accéder rapidement aux broches du MCP4725, par exemple sur breadboard, tout en pouvant sélectionner physiquement l’adresse I2C d’un simple coup de fer à souder, et tout en pouvant activer ou désactiver les résistances de pull-up !
Influence de la charge, sur la tension de sortie
J’en profite pour vous glisser une chose en particulier ici : l’influence qu’a une charge branchée en sortie du MCP4725, sur la tension de cette sortie. En effet, on pourrait croire (à tort) que la tension de sortie est simplement fonction des conditions d’entrées, avec ce convertisseur numérique analogique. Or, en pratique, il y a une chose importante à savoir ici, d’où ce petit aparté !
Pour faire simple : plus vous tirerez de courant en sortie du DAC, et plus ça fera baisser sa tension. Du coup, cela peut donner des niveaux de tension faussés, si l’on ne prend pas garde à certaines choses !
Ici, pour être plus parlant, voici un graphe communiqué par le Microchip (le fabricant), qui donne Vout en fonction du courant tiré dessus :
Comme vous pouvez le constater, si vous tirez trop de courant, la tension de sortie du MCP 4725 va être fortement impactée. C’est pourquoi, l’idéal est de :
- Soit limiter le courant qu’on tire sur le DAC, volontairement
- Soit mettre un étage suiveur (avec un ampli op, par exemple), pour permettre un plus fort courant de sortie, sans faire chuter la tension de sortie du DAC
À vous de voir ! Le tout est simplement de ne pas faire n’importe quoi ici, sinon vous risquez fort d’obtenir des bizarreries en sortie 😉
Power-down mode
Juste un mot pour parler d’un mode de fonctionnement particulier du MCP4725 : le « power-down mode ». En fait, il s’agit tout simplement d’un mode visant à faire de « l’économie d’énergie », lorsque le DAC n’est plus requis. En gros, vous pouvez, via certains bits de configuration, désactiver le convertisseur lorsque vous n’en avez plus besoin. Ainsi, il consommera encore moins de courant !
Les bits en questions sont les bits PD0 et PD1. Ceux-ci sont présents dans le registre de pilotage du DAC, ainsi que dans sa mémoire non volatile (EEPROM). Et suivant leurs valeurs, la sortie du MCP4725 sera soit active, soit désactivée (avec une résistance de sortie mise à la masse, de valeur au choix, parmi 3 valeurs possibles).
Pour résumer cela, voici un tableau qui explique « tout » ça :
PD1 | PD0 | Signification |
---|---|---|
0 | 0 | Mode « normal » : sortie du DAC active |
0 | 1 | Mode « power-down » : sortie du DAC déconnectée (mais sortie reliée à la masse au travers d’une résistance de 1 kohms) |
1 | 0 | Mode « power-down » : sortie du DAC déconnectée (mais sortie reliée à la masse au travers d’une résistance de 100 kohms) |
1 | 1 | Mode « power-down » : sortie du DAC déconnectée (mais sortie reliée à la masse au travers d’une résistance de 500 kohms) |
Ainsi, on constate que le choix du fabricant a été de ne pas laisser la sortie Vout flottante, lorsque déconnectée, ni même de la mettre directement à la masse. En effet, cette sortie est ramenée au potentiel 0V via une résistance de mise à la masse, qui peut prendre 1 des 3 valeurs possibles suivantes : 1k, 100k, ou 500k.
Voilà pour cette partie ! Je ne rentrerai pas plus dans les détails ici, afin de ne pas vous noyer d’informations ! À présent, voyons comment se passe l’adressage i2c de ce petit convertisseur !
Module | Description | Lien |
---|---|---|
Module MCP4725 monté sur PCB (avec sélection d'adresse i2c possible) |
Comment changer d’adresse I2C (0x60 ou 0x61, hors demande spéciale auprès du fabricant)
Alors… de base, il faut savoir que les adresses I2C que peut prendre le MCP4725 sont 0x60 et x61, selon comment est câblée son entrée A0 (à la masse, ou à +Vcc). En fait, ceci reste valable tant que vous travaillerez avec des MCP4725 « standards », c’est-à-dire « non personnalisés ». Ce que j’entends par là, c’est qu’il est tout à fait possible d’avoir des versions légèrement modifiées, qui vous permettront d’avoir des paires d’adresses i2c différentes (mais dans une certaine limite, bien entendu). Et c’est ce que je vais essayer de vous expliquer ici !
Tout d’abord, avec un MCP4725 « classique » (tel qu’on en trouve chez « n’importe quel revendeur »), vous aurez le choix entre 2 adresses I2C possibles, selon :
- Si vous raccordez l’entrée A0 à GND, alors l’adresse sera 0x60
- Si vous raccordez l’entrée A0 à Vcc, alors l’adresse sera 0x61
Cela peut au passage se faire d’un simple coup de fer à souder, si vous travaillez sur « plaquette MCP4725 », comme visible ci-dessous :
À présent, comme je vous le disais juste avant, il y a d’autres adresses i2c possibles ; mais celles-ci seront toujours limitées à 2 adresses possibles, par circuit intégré MCP 4725.
Pour vous expliquer tout cela, il faut tout d’abord se rappeler qu’une adresse i2c s’écrit sur 7 bits. Et dans le cas du MCP4725, les 4 premiers bits seront toujours « 1100 ». Les 3 bits restants étant « libres », ils permettent donc de choisir entre 8 adresses I2C possibles, au final (« 2 à la puissance 3 » possibilités = 8).
En fait, c’est lorsque vous passerez commande auprès de votre fournisseur de composants électroniques, que vous aurez le choix de commander un MCP4725 modèle « standard », ou un MCP4725 « non standard ». Pour être plus clair, voici un tableau qui vous permettra de mieux comprendre :
Référence | Code puce | Type | Bit 6 | Bit 5 | Bit 4 | Bit 3 | Bit 2 | Bit 1 | Bit 0 | Adresses i2c possibles |
---|---|---|---|---|---|---|---|---|---|---|
MCP4725A0T-E/CH | AJxx | Par défaut | 1 | 1 | 0 | 0 | 0 | 0 | 0/1 | 0x60 ou 0x61 |
MCP4725A1T-E/CH | APxx | Sur commande | 1 | 1 | 0 | 0 | 0 | 1 | 0/1 | 0x62 ou 0x63 |
MCP4725A2T-E/CH | AQxx | Sur commande | 1 | 1 | 0 | 0 | 1 | 0 | 0/1 | 0x64 ou 0x65 |
MCP4725A3T-E/CH | ARxx | Sur commande | 1 | 1 | 0 | 0 | 1 | 1 | 0/1 | 0x66 ou 0x67 |
Nota 1 : tous ces bits (Bit6 à Bit0), donnent bien une adresse i2c sur 7 bits
Nota 2 : les bits 6 à 3 seront toujours fixes ici, et invariants (=1100)
Nota 3 : les bits 2 et 1 sont gravés « dans le dur », par le fabricant, lors de la fabrication de la puce (en général, ceux-ci sont donc égaux à 0, sauf « commande spéciale »)
Nota 4 : le bit 0 peut prendre deux valeurs, 0 ou 1, selon si la broche A0 du circuit intégré est reliée à la masse, ou à +Vcc
Comment raccorder la carte MCP4725 sur l’Arduino
Pour raccorder une carte MCP4725 à un Arduino, rien de plus simple ! Car il suffit de brancher les 2 lignes d’alimentation (0 et +5V), ainsi que les 2 lignes de communication i2c (SCL et SDA), et le tour est joué ! Voici d’ailleurs une illustration en image, de ce type de branchement, sur une carte Arduino Uno :
Ainsi, votre Arduino dispose désormais d’une sortie analogique pilotable par I2C, si vous le souhaitez !
Le saviez-vous ? De base, par défaut, le MCP4725 est configuré « en usine » pour délivrer en sortie une tension égale à Vcc/2, à la première mise sous tension. Ainsi, si par exemple vous alimentez votre MCP 4725 avec un Arduino « tournant » en 5 volts, vous devriez alors pouvoir mesurer 2,5 volts en sortie, sur la broche Vout. C’est d’ailleurs un bon moyen de savoir si le convertisseur numérique analogique est bien fonctionnel, au premier démarrage !
Au passage, vous remarquerez que n’apparaît ici aucune résistance de pull-up, sur les lignes SDA et SCL, alors que requises lorsqu’on travaille avec un Arduino. En fait, cela s’explique par le fait que les résistances de pull-up sont déjà intégrées sur la mini-plaquette MCP4725. Du coup, nul besoin d’en rajouter !
Par contre, en pratique, si vous mettez plusieurs de ces modules en parallèle (MCP4725 ou autre, mais toutes équipées de pull-up intégrées), alors cela risque de poser problème. Car mettre plusieurs résistances en parallèle revient à mettre une seule résistance de « beaucoup » plus faible valeur ; ce qui entraînera forcément un courant d’appel « beaucoup » plus élevé. Du coup, il peut être intéressant, dans certains cas, de déconnecter « manuellement » ces résistances de pull-up, si celles-ci sont déjà présentes « 1 fois », dans votre circuit électronique.
En fait, le module MCP4725 permet justement de pouvoir déconnecter ces résistances de pull-up intégrées, au besoin. Et cela se passe au dos de la carte. En effet, sur sa face arrière, vous trouverez un ensemble de 3 pastilles, déjà connectées entre elles d’usine (avec de fines pistes de cuivre, à peine visible parfois). Aussi, si vous souhaitez déconnecter ces résistances, il vous faudra alors rompre cette liaison (enlever le pont de soudure, si c’est soudé, ou mettre un petit coup de cutter, s’il s’agit d’une fine piste de cuivre).
Voici d’ailleurs une vue arrière du module MCP4725, avec les pastilles de couplage/découplage de ces résistances de pull-up intégrées :
Bien sûr, il n’est pas nécessaire de faire cette opération si vous n’avez qu’une seule carte i2c reliée à votre Arduino ! Par contre, si vous en avez plusieurs, alors là la question se posera 😉
Quelle librairie utiliser pour piloter ce DAC ?
Pour piloter ce convertisseur numérique/analogique (DAC) en i2c, j’ai relevé 3 façons de faire. Avec pour chacune, leurs avantages, et leurs inconvénients. Les voici :
- La librairie MCP4725 d’Adafruit, pour une façon simple et rapide de faire les choses
- La librairie MCP4725 de Rob Tillaart, pour aller plus loin dans le contrôle du DAC
- Et la classique librairie Wire, native dans l’IDE Arduino, qui permet de prendre le contrôle de la communication I2C, pour une maîtrise encore plus poussée du DAC
Aussi, je vous propose de vous partager ici ces trois librairies arduino, avec les fonctions qu’elles nous mettent à disposition, et qui peuvent nous intéresser pour piloter le MCP 4725 en toute sérénité ! Alors, en avant 😉
La librairie MCP4725 d’Adafruit (disponible dans le « gestionnaire de bibliothèque » Arduino)
Si vous débutez avec le module MCP4725, et que vous voulez « sortir » votre première tension de ce DAC, je vous recommande vraiment cette librairie. Car elle est vraiment simple d’utilisation. En effet, mis à part l’initialisation, il n’y a qu’une seule fonction à connaître, en fait !
Mais tout d’abord, si ce n’est pas encore fait, il va falloir « importer » cette librairie, dans votre IDE Arduino. Cela se fait en toute simplicité, en allant dans le menu Outils > Gérer les bibliothèques, et en tapant « MCP4725 » dans la zone de recherche. Ainsi, vous devriez voir s’afficher une liste de librairie possible. Celle qui faudra sélectionner ici est la librairie qui est notée « Adafruit MCP4725 », et cliquer sur installer. Au passage, si le logiciel vous demande d’installer des dépendances (librairies « secondaires », liées à celle-là), alors il faudra les installer aussi.
Une fois fait, vous devriez voir la mention « INSTALLED » s’afficher, vous indiquant que tout s’est bien passé !
Ensuite, tout se passe au niveau du code ! Et pour commencer, il faudra bien évidemment instancier cette classe. Cela se fait tout simplement, en mettant les lignes de code suivantes, dans l’entête de votre programme arduino :
#include <Adafruit_MCP4725.h>
Adafruit_MCP4725 dac;
En suivant, dans la fonction setup(), il faudra « démarrer » cette librairie, en faisant appel à la fonction « begin ». Cela s’écrit comme suit :
dac.begin(adresseI2CduMCP4725);
Avec la valeur « adresseI2CduMCP4725 » à remplacer (ou à définir) au début de votre programme, avec l’adresse i2c de votre carte MCP4725 (0x60, 0x61, ou autre).
Enfin, il suffira de faire appel à l’unique fonction utile ici, qui est « setVoltage() », et qui s’utilise de la manière suivante :
dac.setVoltage(valeurDeConsigne, memoriserOuNon);
À noter que :
- la « valeurDeConsigne » est un nombre à 12 bits, donc compris entre 0 et 4095. Ce qui correspond aux 12 bits de résolution du DAC, en fait, donc 4096 niveaux de tensions possibles en sortie.
- et « memoriserOuNon » est une valeur booléenne (true ou false, donc), qui indique au MCP 4725 s’il doit oui ou non « sauvegarder » cette consigne dans sa mémoire EEPROM, pour pouvoir l’utiliser à sa prochaine mise sous tension (en l’absence de directive).
Par contre, ne vous méprenez pas sur cette fonction ! Car malgré son nom, la fonction « setVoltage() » ne prend pas une valeur de tension en argument, mais bel et bien un nombre entre 0 et 4095. Et la tension en sortie Vout sera en fait une fraction de la tension d’alim du DAC. Par exemple : si vous écrivez setVoltage(2047, false), et que votre DAC est alimenté en 5 volts, alors la tension de sortie sera théoriquement égale à 2047/4095*5, soit 2,5 volts environ.
Du reste, si le détail de cette bibliothèque vous intéresse, n’hésitez pas à faire un saut sur la page GitHub de la librairie Adafruit MCP4725, pour avoir plus d’infos !
La librairie MCP4725 de Rob Tillaart (aussi disponible dans le « gestionnaire de bibliothèque » Arduino)
Maintenant, si vous souhaitez pousser un peu plus les choses, la librairie MCP4725 de Rob Tillaart est bien plus fournie ! En effet, celle-ci vous permettra non seulement de commander le DAC en « écriture » (lui transmettre des instructions pour qu’il enregistre une nouvelle consigne), mais également de faire des requêtes en « lecture », pour par exemple récupérer la valeur actuelle du DAC. Qui plus est, vous aurez également accès aux commandes de gestion d’énergie (power down) du MCP 4725, pour mettre en sommeil ou réveiller le convertisseur, selon vos besoins ! En bref, de quoi vous faire obéir, au doigt et à l’œil ! Ou presque 😉
Mais pour commencer, il faut tout d’abord « installer » cette librairie, c’est-à-dire l’importer depuis le « Gestionnaire de bibliothèques » de votre IDE Arduino (cela se fait depuis le menu « Outils »). Dans ce gestionnaire de librairies, il faudra saisir le texte « MCP4725 » dans le champ de recherche, et appuyer sur la touchée ENTRÉE. Ensuite, dans la liste qui s’affiche, il faudra sélectionner la librairie « MCP4725 by Rob Tillaart », comme visible ci-dessous.
Et cliquer sur « installer » pour lancer l’installation de celle-ci. Une fois fait, une mention « INSTALLED » devrait apparaître au niveau de cette bibliothèque arduino, vous indiquant que tout est bon, et prêt à être utilisé !
Ensuite, comme d’habitude, il faudra instancier cette librairie. Cela se fait en deux lignes, via le code suivant :
#include <MCP4725.h>
MCP4725 dac(adresseI2CduMCP4725); // Avec adresseI2CduMCP4725 = adresse i2c de votre carte MCP 4725
Une fois fait, il faudra bien entendu initialiser cette nouvelle classe, en entrant la ligne suivante dans la fonction setup(), de votre programme arduino :
dac.begin();
À noter que cette fonction « begin » renvoie un booléen (true ou false), selon si l’initialisation s’est bien passée ou non. Libre à vous d’utiliser cette fonctionnalité ou non, selon vos besoins !
Enfin, dans le reste de votre code de programmation, vous pourrez librement vous servir des fonctions suivantes :
- setValue(valeur), qui permet de modifier la valeur courante du DAC (et donc, sa tension en sortie)
- writeDAC(valeur, memorisationEEPROM), qui permet de changer la valeur courante du DAC (et donc la tension en sortie), mais également de pouvoir mémoriser ou non cette valeur, dans la mémoire EEPROM du MCP 4725
- readEEPROM(), qui permet de lire la valeur courante du DAC, ainsi que les bits de configuration du mode de fonctionnement (PDO et PD1), le tout stocké dans l’EEPROM de la puce électronique
- readDAC(), qui fait la même chose qu’au-dessus, mais en vous donnant également accès à l’info « ready / busy », qui dit si le MCP4725 est dispo ou occupé (dans le cas d’un enregistrement en cours, par exemple, en mémoire EEPROM)
Du reste, ce ne sont là que quelques fonctions parmi toutes celles disponibles avec cette librairie. D’ailleurs, n’hésitez pas à découvrir le reste des autres fonctions proposées par l’auteur de cette librairie. Mais attention, car certaines d’entre elles sont « expérimentales », telles que les définies l’auteur lui-même !
Remarque : les fonctions setValue(valeur) et writeDAC(valeur, FALSE) font exactement la même chose ici, c’est-à-dire modifier la valeur courante du DAC. À ceci près que la première utilisera la technique du « Write Fast Mode » spécifiée par le fabricant du MCP 4725, tandis que l’autre, la technique du « Write DAC Register » (un poil plus « lente » mais plus complète, si vous préférez). En fait, la méthode writeDAC prend tout son intérêt lorsqu’on écrit writeDAC(valeur, TRUE). Car dans ce cas, on active la mémorisation en mémoire EEPROM , ce qui n’est pas possible avec la méthode setValue, telle que spécifiée dans cette librairie.
Si vous souhaitez plus d’infos sur cette librairie, il vous suffira d’aller sur la page GitHub de la librairie MCP4725 de Rob Tillaart.
La librairie Wire (native, dans l’environnement de développement Arduino)
Une façon plus poussée de prendre le contrôle de la communication Arduino / MCP4725, est d’utiliser la bibliothèque Wire, nativement présente dans l’IDE Arduino. Ainsi, on peut soi-même envoyer les messages qu’on souhaite au DAC (convertisseur numérique analogique), sans passer par une librairie intermédiaire (dont on ne sait pas toujours ce qu’elle fait, ni comment).
Mais attention ! Car utiliser la librairie Wire a un prix, si je puis dire. En effet, cela sera plus complexe à mettre en œuvre, et sera moins évidente à maîtriser, lorsqu’on débute. Il vous faudra donc connaître les protocoles de communication à respecter, en terme de séquences d’envoi I2C. Mais rassurez-vous, car le fabricant communique tout ça dans son datasheet, et en substance, il n’y a pas tant de choses que cela à savoir !
Nota : je vous présenterai un exemple de séquence d’envoi i2c un peu plus loin, et pour être plus précis, dans le 3ème exemple de code. Ainsi, vous verrez une « séquence complète » de communication en utilisant la librairie Wire, et ce, afin de générer une onde sinusoïdale, dynamiquement ! Vous verrez, c’est sympa 😉
Du reste, la bibliothèque arduino Wire nous met à disposition plusieurs fonctions, à connaître :
- la fonction Wire.beginTransmission, qui permet d’initier l’envoi de données, de l’Arduino vers le MCP4725 (en spécifiant l’adresse de ce dernier, sur le bus I2C)
- la fonction Wire.write, qui permet d’envoyer les données (avec des instructions de commande et/ou de paramétrage, intégrées dedans)
- et la fonction Wire.endTransmission, qui permet de clôturer cet envoi de données
Pour plus d’informations, vous pouvez consulter la page de la librairie Wire sur le site Arduino (en anglais), pour avoir plus de détails sur chacune des fonctions, mises à disposition dans l’IDE arduino.
Module | Description | Lien |
---|---|---|
Module MCP4725 monté sur PCB (avec sélection d'adresse i2c possible) |
Code arduino #1 : fixer la tension de sortie du MCP 4725, à une certaine valeur (premiers pas)
Ah… passons enfin à la pratique ! Et pour commencer, je vous propose un petit programme arduino, permettant de sortir une tension fixe et définie, sur la broche Vout du MCP4725.
Pour ce faire, je vais me reposer sur le schéma suivant, mettant en œuvre un module MCP 4725 branché sur un Arduino Nano, avec un voltmètre branché en sortie pour faire des mesures. Ainsi, on pourra vérifier quel est l’écart entre la tension demandée, et la tension obtenue !
Comme vous pouvez le constater, le branchement est vraiment simple à faire ! Et côté programme, vous constaterez que ce n’est pas plus compliqué ! D’ailleurs, le voici :
/*
______ _ _///_ _ _ _
/ _ \ (_) | ___| | | | (_)
| [_| |__ ___ ___ _ ___ _ __ | |__ | | ___ ___| |_ _ __ ___ _ __ _ ___ _ _ ___
| ___/ _ \| __|| __| |/ _ \| '_ \_____| __|| |/ _ \/ _| _| '__/ \| '_ \| |/ \| | | |/ _ \
| | | ( ) |__ ||__ | | ( ) | | | |____| |__ | | __/| (_| |_| | | (_) | | | | | (_) | |_| | __/
\__| \__,_|___||___|_|\___/|_| [_| \____/|_|\___|\____\__\_| \___/|_| |_|_|\__ |\__,_|\___|
| |
\_|
Fichier : prgTestMCP4725-1-TensionFixeEnSortie.ino
Description : Programme permettant de sortir une tension donnée, définie dans le programme, sur la sortie Vout du MCP4725.
Auteur : Jérôme TOMSKI (https://passionelectronique.fr/)
Créé le : 14.12.2021
Librairie utilisée : MCP4725 d'Adafruit, disposible dans l'IDE Arduino (source : https://github.com/adafruit/Adafruit_MCP4725)
*/
const float tensionVoulueEnSortie = 4.75; // Tension souhaitée en sortie du MCP4725, exprimée en volts (entre 0 et 5V)
#define memorisationTensionDeSortie false // Si "false", le DAC ajustera sa tension de sortie sans la mémoriser ; si "true", cette valeur sera dispo, même après coupure de courant
#define adresseI2CduMCP4725 0x60 // Adresse de votre MCP4725, sur le bus I2C (à ajuster, au besoin)
// Remarques, concernant les adresses possibles du MCP4725
// *******************************************************
// Pour un MCP4725 "standard" (MCP4725A0), les adresses I2C possibles sont 0x60 (si broche A0 mise à la masse) ou 0x61 (si broche A0 reliée à Vcc)
// Pour un MCP4725 "personnalisé" (MCP4725A1), les adresses I2C possibles sont 0x62 (si broche A0 mise à la masse) ou 0x63 (si broche A0 reliée à Vcc)
// Pour un MCP4725 "personnalisé" (MCP4725A2), les adresses I2C possibles sont 0x64 (si broche A0 mise à la masse) ou 0x65 (si broche A0 reliée à Vcc)
// Pour un MCP4725 "personnalisé" (MCP4725A3), les adresses I2C possibles sont 0x66 (si broche A0 mise à la masse) ou 0x67 (si broche A0 reliée à Vcc)
#include <Adafruit_MCP4725.h>
Adafruit_MCP4725 dac;
// ========================
// Initialisation programme
// ========================
void setup() {
// Initialise la liaison série (arduino nano -> PC)
Serial.begin(9600);
Serial.println(F("================================================================================"));
Serial.println(F("PRG1 - Demande au MCP4725 de sortir une tension particulière, sur sa sortie Vout"));
Serial.println(F("================================================================================"));
Serial.println("");
// Démarrage du DAC externe (le MCP 4725)
dac.begin(adresseI2CduMCP4725);
// Définition de la valeur correspondante à la tension voulue en sortie du DAC
int valeurAenvoyerAuMCP2547 = tensionVoulueEnSortie * 4095 / 5;
// Nota : la tension souhaitée ici est exprimée en volts (entre 0 et 5V), mais la valeur à transmettre
// pour ce faire doit être comprise entre 0 et 4095. D'où la nécessité de faire une conversion avant tout.
// Demande au DAC de changer sa tension de sortie, suivant cette nouvelle consigne
dac.setVoltage(valeurAenvoyerAuMCP2547, memorisationTensionDeSortie);
// Rappel de la tension entrée dans ce programme, sur le moniteur série
Serial.print(F("Tension souhaitée en sortie du DAC = "));
Serial.print(tensionVoulueEnSortie);
Serial.print(F(" volts"));
Serial.println("");
}
// =================
// Boucle principale
// =================
void loop() {
// Rien ici, car tout se passe dans la fonction setup !
}
Ici, j’ai utilisé la librairie MCP4725 de Adafruit. Au final, la fonction setVoltage() fait « tout le travail » ! Car c’est elle qui commande le convertisseur numérique analogique (DAC), afin que celui-ci ajuste sa tension de sortie à la valeur demandée.
Au passage, je vous ai laissé quelques lignes de débogage série, que j’avais utilisé lors de mes essais, et permettant d’avoir un retour PC (via le moniteur série de l’IDE Arduino). En fait, cela ne fait qu’afficher la tension demandée en sortie. D’ailleurs, voici un exemple en image, lorsqu’on demande « 4,75 volts » en tension de consigne, dans le programme :
À présent, je vous ai mis de côté 3 mesures que j’ai pu réaliser, avec ce montage. Celles-ci ont été faites à :
- 0 volt
- 4,75 volts
- Et 5 volts
Pour chacune d’entres elles, je vous ai fait des photos. Car elles mettent en évidence plusieurs choses intéressantes, et qu’il faut bien que vous compreniez. Mais avant cela, sachez que tout mon montage était alimenté avec une alimentation stabilisée 5V, fournissant « exactement » 4,946 volts. Et vous verrez pourquoi, par la suite, cette valeur est importante à souligner, ici.
Alors, lorsque j’ai demandé « 0 volt » dans le programme, voici ce que j’ai obtenu :
Ensuite, lorsque j’ai demandé « 4,75 volts », voici ce que j’ai pu obtenir :
Enfin, lorsque j’ai spécifié la valeur « 5 volts » dans le code arduino, voici ce qui se passe :
Matériel utilisé :
– 1 x Arduino Nano
– 1 x module MCP4725
– 1 x alim USB portable
– 1 x voltmètre Kaiweets KM601
– 1 x paire de sondes à pinces croco
En fait, comme vous avez dû le remarquer, on constate un petit écart entre la valeur demandée, et celle mesurée. Outre la précision du multimètre, il y a 3 choses qui expliquent cela :
- Premièrement, le convertisseur numérique analogique MCP4725 n’est pas parfait ; en effet, un « offset » peut être présent, et c’est d’ailleurs ce que l’on constate ici, lorsqu’on demande 0V (il y a 3 mV de différence).
- Deuxièmement, l’alimentation qui était censée être de 5V fait en fait 4,946 volts. Ce qui fait donc une perte potentielle de 54 mV en sortie. C’est d’ailleurs le principal défaut de ce MCP4725, selon moi, qui a pour tension de référence sa propre alimentation, et non une source de tension indépendante, plus précise et mieux stabilisée.
- Troisièmement, la « formule de calcul interne » du MCP4725 ne permet pas d’atteindre Vcc, car Vout = Vcc * N / 4096, avec « N » la valeur de consigne pouvant aller de 0 à 4095 ; du coup, la valeur maximale que nous pouvons atteindre ici vaut « seulement » : Vout = 5 * 4095 / 4096 = 4,998 volts. Ce qui induit forcément une erreur d’échelle en sortie, particulièrement nette lorsqu’on atteint les plus hautes valeurs.
Du coup, dites-vous bien que si la tension d’alimentation du MCP 4725 varie, alors la tension de sortie variera aussi. D’où « l’extrême » nécessité de travailler avec une alimentation la plus stabilisée et la plus filtrée qui soit, afin d’avoir un maximum de précision en sortie du DAC. Sinon, ce convertisseur numérique/analogique vous fournira une tension manquant de précision, malgré ce qu’on aurait pu en attendre !
Enfin, retenez bien le fait que ce DAC ne peut atteindre la valeur maximale supérieure en sortie, du fait que ses 4096 niveaux ne vont que de 0 à 4095. Du coup, la sortie Vout, telle que spécifiée par le fabricant, vaudra au maximum : Vout = 4095/4096 * Vcc (avec Vcc, la tension d’alimentation du MCP 4725).
Du reste, voici quelques valeurs remarquables, pour encore mieux vous représenter les choses :
- Consigne = 0000 0000 0000 → Vout = 0
- Consigne = 0000 0000 0001 → Vout = 1/4096 * Vcc (soit un « LSB », c’est-à-dire la plus petite portion possible)
- Consigne = 0000 0000 0010 → Vout = 2/4096 * Vcc (soit 2 LSB)
- …
- Consigne = 1000 0000 0000 → Vout = 2048/4096 * Vcc (soit Vout = 0,5 * Vcc)
- …
- Consigne = 1111 1111 1110 → Vout = 4094/4096 * Vcc (soit Vcc – 2 LSB)
- Consigne = 1111 1111 1111 → Vout = 4095/4096 * Vcc (soit Vcc – 1 LSB) → donc pas entièrement Vcc, même au maximum !
Par ailleurs, même si ce n’est pas le cas ici, souvenez-vous bien également que la charge que vous mettrez en sortie du MCP4725 influencera également cette tension Vout. En effet, plus vous tirerez de courant, et plus cette tension baissera !
Code arduino #2 : exemple de boucle de vérification du DAC
Voici un autre exemple intéressant à réaliser, et qui vous permettra de voir comment changer la valeur du DAC « à la volée » ! Qui plus est, celui-ci vous permettra de visualiser l’écart « entre théorie et pratique », ou plus exactement, entre tension demandée, et tension réellement obtenue !
Pour faire cela, nous allons simplement créer une boucle, dans laquelle :
- L’arduino demandera au MCP4725 de générer une tension donnée
- Puis l’arduino mesurera la tension effectivement générée par le DAC, par le biais d’une de ses entrées analogiques
Et ceci, pour toutes les valeurs possibles du MCP 4725, c’est-à-dire de 0 volt à quasi Vcc (puisque la valeur max que l’on pourra atteindre ici, sera de 4095/4096*Vcc). Ainsi, nous pourrons observer l’écart entre ces valeurs (tension demandée / tension obtenue), et voir comment expliquer les différences, lorsqu’il y en a !
Comme support à cet exemple, je vous propose le schéma suivant :
Matériel utilisé :
– 1 x Arduino Nano
– 1 x module MCP4725
– 1 x alim USB portable
En fait, c’est quasiment un copier/coller de l’exemple précédent ! À ceci près que cette fois-ci, la sortie Vout ne sera pas « libre », mais raccordée en retour à l’Arduino Nano. Ainsi, on pourra effectuer des mesures au travers de l’ADC de l’arduino, afin de pouvoir comparer les tensions demandées et mesurées en pratique.
Nota : attention à ne pas vous emmêler les pinceaux ici, entre les terminologies « ADC » (analog to numeric converter), qui est un convertisseur analogique vers numérique, et « DAC » (digital to analog converter), qui est un convertisseur numérique vers analogique. Car l’un fait exactement l’opposé de l’autre ! Et il est facile de les confondre au début, du fait de leurs lettres quasi identiques ! Alors prenez garde 😉
Au niveau du code arduino, voici le programme arduino permettant de réaliser les opérations de parcours des valeurs, avec mesures et comparaisons dans la foulée (et affichage sur le moniteur série de l’IDE arduino) :
/*
______ _ _///_ _ _ _
/ _ \ (_) | ___| | | | (_)
| [_| |__ ___ ___ _ ___ _ __ | |__ | | ___ ___| |_ _ __ ___ _ __ _ ___ _ _ ___
| ___/ _ \| __|| __| |/ _ \| '_ \_____| __|| |/ _ \/ _| _| '__/ \| '_ \| |/ \| | | |/ _ \
| | | ( ) |__ ||__ | | ( ) | | | |____| |__ | | __/| (_| |_| | | (_) | | | | | (_) | |_| | __/
\__| \__,_|___||___|_|\___/|_| [_| \____/|_|\___|\____\__\_| \___/|_| |_|_|\__ |\__,_|\___|
| |
\_|
Fichier : prgTestMCP4725-2-BoucleDeControle.ino
Description : Programme permettant de comparer la tension générée en sortie du MCP4725 (sur sa broche Vout),
par rapport à la consigne qui lui a été envoyée, par un Arduino Nano
Auteur : Jérôme TOMSKI (https://passionelectronique.fr/)
Créé le : 15.12.2021
Librairie utilisée : MCP4725 de Rob Tillaart, disposible dans l'IDE Arduino (source : https://github.com/RobTillaart/MCP4725)
*/
#include <MCP4725.h>
// Constantes
#define adresseI2CduMCP4725 0x60 // Adresse i2c du module MCP4725 (à ajuster, au besoin)
#define entreeDeMesureAnalogique A0 // Entrée de l'arduino nano qui sera reliée à la sortie Vout du MCP4725,
// pour pouvoir mesurer cette tension effective, et pouvoir la comparer à la valeur attendue
// Variables
int valeurEnvoyeeAuDacMcp4725; // Valeur 12 bits (0..4095) permettant de balayer les 4096 niveaux de tension possible du DAC
int valeurMesureeSurAdcArduino; // Valeur 10 bits (0..1023) représentant la tension mesurée par l'Arduino, sur son entrée analogique
float tensionAttendueEnSortie; // Tension calculée d'après la valeur envoyée au DAC, exprimant en volts la tension attendue sur la sortie Vout
float tensionMesureeParArduino; // Tension calculée d'après la valeur mesurée sur l'entrée analogique ADC de l'Arduino
// Instanciation de la librairie
MCP4725 dac(adresseI2CduMCP4725);
// ========================
// Initialisation programme
// ========================
void setup() {
// Initialise la liaison série (Arduino Nano -> PC)
Serial.begin(9600);
Serial.println(F("================================================================="));
Serial.println(F("PRG2 - Boucle de contrôle Arduino Nano -> MCP4725 -> Arduino Nano"));
Serial.println(F("================================================================="));
Serial.println("");
// Teste la présence du convertisseur numérique analogique MCP4725, depuis l'arduino
if (dac.begin() == false) {
Serial.print("Connexion au module MCP4725 impossible, à l'adresse [0x");
Serial.print(adresseI2CduMCP4725, HEX);
Serial.println("]");
Serial.println("Arrêt du programme.");
while(1);
} else {
Serial.println("Connexion au MCP4725 réussie !");
Serial.println("");
}
// Lance le parcours de toutes les tensions possibles (de 0 à +Vcc), au niveau du DAC,
// et compare cela avec la tension lue en retour par l'Arduino Nano
parcoursToutesLesTensions();
}
// =================
// Boucle principale
// =================
void loop() {
// Nada (tout se passe dans la fonction setup !)
}
// ======================================
// Fonction : parcoursToutesLesTensions()
// ======================================
void parcoursToutesLesTensions() {
// Boucle de parcour des 4096 valeurs possibles du DAC (fractions de sa tension de référence Vcc, envoyé sur sa sortie Vout)
for(valeurEnvoyeeAuDacMcp4725 = 0 ; valeurEnvoyeeAuDacMcp4725 < 4096 ; valeurEnvoyeeAuDacMcp4725++)
{
// Envoi de la valeur au DAC
if(!dac.isConnected()) { // Vérifie que la connexion au DAC est toujours active
Serial.println("Connexion au DAC perdue…");
Serial.println("Arrêt.");
while(1);
} else {
dac.writeDAC(valeurEnvoyeeAuDacMcp4725, false); // Transmet la valeur souhaitée, de l'Arduino vers le MCP 4725 (la valeur "false" indique de ne pas sauvegarder cette valeur en EEPROM)
}
tensionAttendueEnSortie = valeurEnvoyeeAuDacMcp4725 * 5.0 / 4096.0;
delay(50); // Petite pause de 200ms, pour attendre la stabilisation de la tension en sortie (se fait généralement en 6µs environ, d'après le fabricant)
// Lecture de cette valeur
valeurMesureeSurAdcArduino = analogRead(entreeDeMesureAnalogique);
tensionMesureeParArduino = valeurMesureeSurAdcArduino * 5.0 / 1024.0;
// Affichage de toutes les infos sur le moniteur série de l'IDE Arduino
Serial.print("Valeur consigne [DAC mcp4725] = ");
Serial.print(valeurEnvoyeeAuDacMcp4725);
Serial.print("/4095");
Serial.print(" (soit ");
Serial.print(tensionAttendueEnSortie, 3); // Affiche 3 chiffres après la virgule
Serial.print("V)\n");
Serial.print("Valeur mesurée [ADC arduino] = ");
Serial.print(valeurMesureeSurAdcArduino);
Serial.print("/1023");
Serial.print(" (soit ");
Serial.print(tensionMesureeParArduino, 3); // Affiche 3 chiffres après la virgule
Serial.print("V)\n");
// Et rebouclage, après une petite pause de quelques millisecondes !
Serial.println("");
delay(200);
}
}
Ici, j’ai utilisé la librairie MCP4725 de Rob Tillaart, histoire de pouvoir communiquer plus profondément avec le DAC, et pour vous montrer une autre façon de faire les choses. Ainsi, vous pourrez choisir la méthode qui vous conviendra le mieux 😉
Dans le cas où il y aurait un problème de communication entre le MCP4725 et l’Arduino, le message suivant devrait s’afficher sur votre moniteur série :
Et si tout se passe bien :
À noter que le temps d’exécution de ce programme est particulièrement long, en l’état. Libre à vous de diminuer le délai entre chaque valeur testée (fixée à 200 ms, dans le programme), ou de simuler seulement 1 valeur sur 2, sur 3, ou sur plus, au lieu de toutes les parcourir, une à une. Ici, je vous donne juste une version « complète », pour observer des valeurs particulières, au besoin.
D’ailleurs, voici quelques valeurs remarquables :
- Valeur consigne [DAC mcp4725] = 0/4095 (soit 0.000V)
Valeur mesurée [ADC arduino] = 0/1023 (soit 0.000V)
→ pas d’écart - Valeur consigne [DAC mcp4725] = 819/4095 (soit 1.000V)
Valeur mesurée [ADC arduino] = 203/1023 (soit 0.991V)
→ écart de 9 mV - Valeur consigne [DAC mcp4725] = 2048/4095 (soit 2.500V)
Valeur mesurée [ADC arduino] = 509/1023 (soit 2.485V)
→ écart de 15 mV - Valeur consigne [DAC mcp4725] = 2867/4095 (soit 3.500V)
Valeur mesurée [ADC arduino] = 713/1023 (soit 3.481V)
→ écart de 19 mV - Valeur consigne [DAC mcp4725] = 4095/4095 (soit 4.999V)
Valeur mesurée [ADC arduino] = 1017/1023 (soit 4.966V)
→ écart de 33 mV
Comme vous pouvez le constater, plus on monte en tension, et plus l’écart est important. Mais attention à ne pas mal interpréter ces résultats, car il est facile de se mettre soi-même en erreur. En effet, ces écarts ont beaucoup d’explications possibles :
- Pour commencer, l’alimentation 5V de l’arduino n’est jamais vraiment de 5 volts, tout pile. Or, le MCP4725 « envoie » en sortie une fraction de cette tension d’alimentation, qui lui sert de référence. Du coup, en pratique, on aura forcément une petite erreur en sortie, à partir du moment où cette tension de référence n’est pas exactement à la valeur souhaitée. Par exemple : si l’alim est de 4,97 volts et non de 5,00 volts, alors la tension max théorique en sortie du DAC ne sera que de 4,97*4095/4096, soit 4,968 volts (donc 32 mV en dessous de 5V).
- Ensuite, dans l’exemple que je vous propose ici, il y a nativement une erreur de précision qui s’opère. Car le DAC du MCP475 opère sur 12 bits (4096 niveaux de tension, donc), tandis que l’ADC de l’arduino opère sur 10 bits (1024 niveaux de tension). Donc forcément, les échelles n’étant pas les mêmes, il y aura inévitablement une erreur de conversion induite, à ce niveau.
- Enfin, le MCP 4725 en lui-même n’est pas parfait, et génère lui aussi des imprécisions de conversion ; ce sont en fait des erreurs d’offset et de gain, notamment dues à son ampli op de sortie, interne.
Du coup, ne tirez jamais de conclusions trop hâtives, et n’incriminez pas telle ou telle chose, avant d’avoir tout bien considéré 😉
Module | Description | Lien |
---|---|---|
Module MCP4725 monté sur PCB (avec sélection d'adresse i2c possible) |
Code arduino #3 : exemple de génération d’onde sinusoïdale (sine wave), avec le MCP4725
Au travers de ce troisième exemple, je vais vous montrer comment générer une onde sinusoïdale avec votre Arduino, à l’aide d’un MCP4725. Pour ce faire, nous allons utiliser une librairie générique de l’IDE Arduino, qui s’appelle « Wire » (celle-ci est très utilisée, pour les communications I2C, entre autres). Et comme support à ceci, nous allons reprendre le circuit que nous avions réalisé au début, à l’occasion du premier exemple (avec simplement un oscilloscope branché sur la sortie Vout, à la place d’un simple voltmètre) :
Côté logiciel, je vous propose un programme arduino qui :
- Au démarrage : remplira un tableau de valeurs, qui représenteront une onde sinusoïdale complète (une période entière, donc) ; avec des valeurs allant de 0 à 4095 (soit 12 bits), pour être en adéquation avec les 12 bits du MCP4725.
- Dans sa boucle perpétuelle :
- Parcourra les valeurs de ce tableau, une à une
- Et enverra ces valeurs, l’une après l’autre, au MCP 4725
Cela permettra de générer un signal périodique en sortie du MCP 4725, avec pour « forme de signal », l’image des valeurs parcourues dans le tableau.
En langage arduino, voici comment tout cela se traduit, ici :
/*
______ _ _///_ _ _ _
/ _ \ (_) | ___| | | | (_)
| [_| |__ ___ ___ _ ___ _ __ | |__ | | ___ ___| |_ _ __ ___ _ __ _ ___ _ _ ___
| ___/ _ \| __|| __| |/ _ \| '_ \_____| __|| |/ _ \/ _| _| '__/ \| '_ \| |/ \| | | |/ _ \
| | | ( ) |__ ||__ | | ( ) | | | |____| |__ | | __/| (_| |_| | | (_) | | | | | (_) | |_| | __/
\__| \__,_|___||___|_|\___/|_| [_| \____/|_|\___|\____\__\_| \___/|_| |_|_|\__ |\__,_|\___|
| |
\_|
Fichier : prgTestMCP4725-3-SineWave.ino
Description : Programme permettant de générer dynamiquement une onde sinusoïdale sur la sortie Vout du MCP4725,
ce dernier étant piloté par un Arduino Nano, via le bus I2C (monté à 400 kHz, pour l'occasion !).
Auteur : Jérôme TOMSKI (https://passionelectronique.fr/)
Créé le : 16.12.2021
*/
#include <Wire.h>
// Constantes
const int adresseI2CduMCP4725 = 0x60; // Adresse du module MCP4725 sur le bus I2C (à ajuster, au besoin)
const int nombreDePasDuSignalSinusoidal = 120; // Indique le nombre de pas qui compose le signal sinusoïdal à générer (nota : plus il sera grand, plus le signal sera "précis")
// Variables
int ondeSinusoidale[nombreDePasDuSignalSinusoidal]; // Tableau qui contiendra toutes les valeurs représentant une onde sinusoïdale complète
int pointeurTableauOndeSinusoidal; // Pointeur qui permettra de parcourir ce tableau, pas à pas
int echantillonOndeSinusoidale; // Contiendra une fraction de l'onde sinusoïdale (un "pas")
// ========================
// Initialisation programme
// ========================
void setup() {
// Initialise la liaison série (Arduino Nano -> PC)
Serial.begin(9600);
Serial.println(F("==============================================================================="));
Serial.println(F("PRG3 - Génération d'une onde sinusoïdale en sortie du MCP4725 (amplitude 0->5V)"));
Serial.println(F("==============================================================================="));
Serial.println("");
// Initialise la liaison I2C (Arduino Nano -> MCP4725)
Wire.begin();
Wire.setClock(400000); // Passage de la fréquence 100 kHz par défaut (Standard Mode) à 400 kHz (Fast Mode), pour la ligne SCL (serial clock)
// Remplissage de la table "ondeSinusoidale", représentant une onde sinusoïdale complète, d'amplitude 0 à 4095, décomposée en un nombre de pas défini
for (int pointeurTableauOndeSinusoidal = 0 ; pointeurTableauOndeSinusoidal < nombreDePasDuSignalSinusoidal ; pointeurTableauOndeSinusoidal++) {
// Pour cela, on va utiliser la fonction sin(), qui prend en argument une valeur exprimée en radian (et pour rappel : 1 "tour complet" = 2*Pi radians)
ondeSinusoidale[pointeurTableauOndeSinusoidal] = 2047 + round(2047 * sin(2 * PI * pointeurTableauOndeSinusoidal / (nombreDePasDuSignalSinusoidal-1)));
// Nota 1 : variera entre 0 et 4094 (en fait : initialement de -2047 à 2047, mais décalé vers le haut de 2047 pts, pour travailler en tension positive)
// Nota 2 : ce valeur se chiffre donc sur 12 bits (que l'on devra scinder en 2 parties plus tard, pour la transmission I2C)
}
}
// =================
// Boucle principale
// =================
void loop() {
// ******************************************************************************************************
// Parcours du tableau représentant une onde sinusoïdale complète, divisée en un certain nbre de morceaux
// ******************************************************************************************************
for(pointeurTableauOndeSinusoidal = 0 ; pointeurTableauOndeSinusoidal < nombreDePasDuSignalSinusoidal ; pointeurTableauOndeSinusoidal++) {
// Lit un échantillon de cette onde
echantillonOndeSinusoidale = ondeSinusoidale[pointeurTableauOndeSinusoidal];
// Initie la communication avec le MCP4725
Wire.beginTransmission(adresseI2CduMCP4725);
// Envoi d'une commande en "Fast Mode Write" (C2=0 et C1=0), et en "Normal Mode" (PD1=0 et PD0=0) au niveau des power-down bits, sur 2 octets
// Remarque : quand on parle de "Fast Mode" ici, il s'agit en fait du nom d'opération spécifiée par le fabricant du MCP4725,
// et non du "Fast Mode" I2C, qui concerne la vitesse de communication sur le bus i2c de l'arduino. C'est subtil, certes, mais pas du tout la même chose !
// ============================================
// Premier octet à envoyer
// ============================================
// Numéros de bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0
// Désignation du bit | C2 | C1 | PD1 | PD0 | D11 | D10 | D9 | D8
// Valeur des bits | 0 | 0 | 0 | 0 | x | x | x | x (les "x" étant à remplacer à la volée, par les 4 bits de poids fort de la valeur à envoyer
// ============================================
// En résumé : les 4 premiers bits vaudront "0000", et les 4 suivants seront les 4 premiers bits de la valeur 12 bits à envoyer (d'où le décalage de 12-4 = 8 bits)
Wire.write((0b0000 << 4) | ((echantillonOndeSinusoidale & 0b111100000000) >> 8));
// ============================================
// Deuxième octet à envoyer
// ============================================
// Numéros de bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0
// Désignation du bit | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0
// Valeur des bits | x | x | x | x | x | x | x | x (les "x" étant à remplacer à la volée, par les 8 bits de poids faible de la valeur à envoyer
// ============================================
// En résumé : ces 8 bits correspondent aux 8 derniers bits de la valeur 12 bits à envoyer
Wire.write(echantillonOndeSinusoidale & 0b000011111111);
// Stoppe cet envoi à destination du MCP4725, concernant cet échantillon d'onde sinusoïdale
Wire.endTransmission();
// PAS DE PAUSE ICI. En fait, on boucle directement à la valeur suivante. Cela nous permettra de voir quelle fréquence on pourra atteindre
// avec cet arduino, sachant que notre sinusoïde a été divisée en un certain nombre de pas (qu'on pourra réduire pour aller plus vite,
// mais en dégradant la qualité du signal de sortie au passage).
}
}
À noter que, pour l’occasion, j’ai « boosté » le débit de communication i2c, en la faisant passer de 100 kHz (standard), à 400 kHz (Fast I2C). Cela permettra d’obtenir un signal sinusoïdal plus rapide, car comme vous le verrez, il en aura bien besoin !
D’ailleurs, au sujet de la fréquence théorique estimée de cette sinusoïde, on peut grossièrement la calculer, de la manière suivante :
- Si la fréquence d’horloge du bus I2C (SCL) est à 400.000 Hz
- Si on scinde le signal sinusoïdal en 120 pas (taille du tableau, contenant l’image de l’onde sinusoïdale)
- Et si on considère qu’il faut grosso modo 40 coups d’horloge I2C (3 octets plus ACK, …) pour ordonner au MCP4725 de changer de valeur courante
- Alors le signal de sortie devrait avoir une fréquence égale à : 400.000 / 120 / 40 = 83 hertz
Bien sûr, ce n’est ici qu’une estimation très approximative, et assez sommaire. Mais cela nous servira de comparatif avec la valeur obtenue en pratique, pour voir si le résultat est cohérent. D’ailleurs, voici ce que j’ai pu obtenir de mon côté, en branchant un oscillo sur la sortie Vout du DAC :
Comme vous pouvez le constater, on obtient une « belle » onde sinusoïdale, de fréquence égale à 73 Hz (ce qui n’est pas très loin des 83 Hz théoriques estimés, donc tout semble correct !).
Matériel utilisé :
– 1 x Arduino Nano
– 1 x module MCP4725
– 1 x oscilloscope portable
– 1 x alim USB portable
Au passage, vous constatez que nous obtenons un signal particulièrement « lent », malgré le fait que nous fonctionnons à vitesse « maximale », du côté de l’Arduino. Ce qui réduit à néant nos rêves de transformation d’Arduino en « générateur de signaux », avec cette méthode-là ! Mais ce n’est pas si grave, car il y a bien d’autres façon de faire 😉
Module | Description | Lien |
---|---|---|
Module MCP4725 monté sur PCB (avec sélection d'adresse i2c possible) |
Tutorial MCP4725 : conclusion !
Voilà ! Je pense que nous avons fait le tour du MCP4725, du moins, dans les grandes lignes ! Vous en savez donc suffisamment à présent sur ce petit composant, pour faire vos premiers pas avec 😉
Alors amusez-vous bien avec ! Et à bientôt !
Jérôme.
À découvrir aussi : un convertisseur analogique numérique (ADC) à 15 bits (l’ADS1115), pour compléter ce DAC !
(*) Mis à jour le 18/01/2024
Merci pour ce tuto qui peut rendre bien des services, très précis et très complet comme à votre habitude ! Bonnes fêtes.
Merci, merci ! Et bonnes fêtes à toi aussi 😉
Merci pour cette belle documentation. Toujours d’excellence.
Bonnes fêtes de fin d’année.
Un lecteur.
De rien, et excellentes fêtes de fin d’année à toi aussi !
Tuto très bien documenté
Félicitations
Un autodidacte
Merci beaucoup, Marc !
Félicitations pour ce tuto, d’une grande précision technique et opérationnel ! Bravo !!!
Merci beaucoup ! Et au plaisir 😉
Jérôme.
Très bon Tuto qui nous permet immédiatement d’activer le 4725.
J’ai une question : est-il possible de stocker et de relire dans l’EEprom une autre valeur que la valeur de la tension ?
Salut Joseph !
En théorie, oui. On peut parfaitement stocker une valeur 12 bits quelconque dans cette EEPROM, pour la relire ensuite. Par contre, il faut bien comprendre que cela va inévitablement impacter la sortie du 4725, à sa prochaine mise sous tension (en modifiant sa tension de sortie Vout). Car le MCP4725, lui, va interpréter cette valeur comme étant une consigne de tension (quand bien ce ne serait pas souhaité), et régler sa tension de sortie en conséquence.
C’est pourquoi, en pratique, il ne vaut mieux jamais faire cela.
Mais il y a d’autres solutions, si ce qui t’intéresse est simplement de stocker des valeurs en mémoire EEProm, pour les relire ensuite. Par exemple, tu pourrais très bien écrire tes données dans la mémoire EEPROM de ton arduino, et ainsi pouvoir y accéder ensuite (cf. l’article que j’avais fait dessus : https://passionelectronique.fr/eeprom-arduino-lire-ecrire/). Du coup, cela te permettrait de ne pas toucher à la mémoire interne du MCP 4725, tout en pouvant mémoriser des valeurs en mémoire non volatile.
Voila ! Bonne journée à toi 😉
Jérôme.
Merci pour ces informations très utiles pour moi.
J ai une question : comment je peux générer une tension variant de -5 v à +5 v ?
Merci d’avance.
Salut !
Malheureusement, le MCP 4725 ne te permettra pas de faire cela (sans faire de la bidouille, j’entends).
Car il s’agit d’un modèle « Single-Supply », dont la tension d’alimentation doit être comprise entre 2,7 et 5,5 volts. Il ne couvre donc pas nativement les 10 volts d’amplitude qu’il y a entre -5 et +5V, que tu recherches. Sans parler du fait qu’il faudrait ensuite adapter le signal, en fonction de la charge.
Désolé,
Jérôme.
Bonjour, merci pour ce tuto.
Vous dîtes qu’il n’y a pas de sortie ANA sur Arduino, mais pourtant il y a les sorties PWM !
Quels sont les avantages et les inconvénients entre PWM et MCP4725 ?
Salut Jean-Paul !
Attention : il ne faut pas confondre un signal PWM, qui est un signal numérique « tout ou rien » (0 ou +Vcc, si tu préfères), avec un signal analogique (qui peut par exemple varier et prendre toutes les valeurs entre 0 et +Vcc).
L’arduino, nativement, ne possède aucune sortie analogique. Il n’a que des sorties numériques. Bien que ces sorties numériques puissent générer des signaux PWM (et donc une tension moyenne paramétrable), il n’en reste pas moins que tu ne pourras jamais sortir une onde sinusoïdale analogique, par exemple, en l’état ; mais seulement un signal carré, de fréquence et/ou rapport cyclique adaptable.
Du coup, le MCP4725 est justement là pour ça ; en fait, il s’agit d’un exemple de module, parmi tant d’autres, qui permet d’ajouter à l’Arduino la possibilité de sortir un véritable signal analogique (chose qu’il est incapable de faire, nativement).
Voilà !
Excellente journée à toi,
Jérôme.
Afin de filtrer au maximum les messages de type "spam" ou "inappropriés", chaque commentaire est soumis à modération, et validé manuellement. Du coup, il se peut que certains commentaires ne soient pas publiés, ou sinon, avec un peu de retard. Par ailleurs, j'ai malheureusement plus de messages à traiter que de temps pour y répondre ; c'est pourquoi je ne pourrais pas répondre à tout le monde. Désolé …