Cet article est amené à évoluer. J’expliquerai plus en détail le code des programmes en guise d’introduction à la compréhension de la programmation Arduino, de même que j’ajouterai de nombreux oscillogrammes dès que j’aurai un oscilloscope à disposition. Je détaillerai également les différents schéma du montage décrit dans cet article. N’hésitez pas à revenir sur cette page !

Dans cet article, nous verrons comment réaliser de manière très simple un DAC avec seulement quelques résistances, et une carte Arduino. Un vrai rêve d’enfant. Vous serez étonné par la qualité de restitution que l’on peut obtenir avec ces quelques composants passifs. L’expression « DAC » signifie « Digital to Analog Converter ». Mais ça, vous le saviez déjà. Les sorties analogiques des premières cartes Arduino ne permettent pas de générer des signaux sinusoïdaux, c’est pourquoi nous devons utiliser un DAC pour convertir des données numériques (typiquement une liste de bits, ou des valeurs numériques) en un signal analogique. La méthode détaillée dans cet article est appelée « R-2R Resistor Ladder ». Par ailleurs, la récente carte Arduino Due dispose de deux DAC. J’eûs aimé tester cette carte aux caractéristiques prometteuses, mais mon budget ne me le permet pas.

Le principe de fonctionnement de ce DAC est très simple. Mais pas moins élégant. Ce circuit est un réseau de résistance qui permet de convertir une liste de bits en une tension analogique comprise entre deux valeurs de référence. Les bits sont délivrés par la carte Arduino. De manière plus simple, vous pouvez assimiler ce circuit à une succession de ponts diviseurs de tension qui pondèrent la valeur de la tension délivrée par le montage. Pour des informations plus détaillées, je vous renvoies à la page Wikipédia dédiée au sujet (en Anglais).

R2R_Resistor_Ladder_Upsilon_Audio

Un réseau de résistances du type R-2R constitué de N Bits (Source: Wikipedia)

Le montage détaillée dans cet article constitue un DAC 8-Bit, ce qui signifie que la tension analogique délivrée par le circuit peut prendre 256 valeurs différentes. Le choix des résistances, et l’utilisation d’une carte Arduino impose que ces 256 valeurs de tension soient également réparties entre 0V et 5V. On est loin des 24-Bits offerts par les équipements audio Hi-Fi ! Ce qui représente tout de même 16777216 valeurs distinctes. Toutefois, les 8-Bits de notre montage sont amplement suffisants pour comprendre le principe de fonctionnement des DACs. Sans plus attendre, voici le schéma du montage dont il est question.


Upsilon_Audio_DAC_R2R_Schéma

Le schéma du montage.  Réalisé à la plage. Pour cela, j’ai utilisé le logiciel Fritzing. Un logiciel que j’apprécie particulièrement, et que je vous recommande !

Intéressons nous désormais au programme. Les programmes Arduino suivant reposent tous sur l’emploi de la commande PORT. Il s’agit d’une manipulation sur les ports du microcontrôleur. Cette commande est tout à fait adaptée au montage détaillé dans cet article. En effet elle permet à l’utilisateur de modifier simultanément le niveau de sortie (état haut ou bas) des broches appartenant à un même port. Celle-ci est donc bien plus efficace que la commande digitalWrite() qui ne permet pas de changer l’état de plusieurs broches de manière simultanée. Pour plus d’informations sur la commande PORT, je vous renvoies à cet article du site officiel Arduino. Le schéma exact de ce montage dépendra de la carte que vous utiliserez. Référez vous au pinout de votre carte pour connaître le routage des ports du microcontrôleur. Par exemple, les ports 0 à 7 correspondent au port D pour un Arduino Uno tandis que les ports A0 à A7 correspondent au port F pour un Arduino Mega. Vous remarquerez que j’ai adjoint quelques commentaires au code des programmes. Par ailleurs, j’ai testé les programmes suivants avec les cartes Arduino Uno et Arduino Mega. Que cela ne vous empêche pas de m’offrir une carte Arduino Due !

Ce premier programme est très simple: celui-ci permet de générer un signal carré.

//Signal Carré
//Par Pierre Pelé ([email protected])
//http://upsilonaudio.com/arduino-dac-r-2r-ladder/
//Septembre 2013

void setup()
{
  for (int i=0;i<8;i++) //Définir les broches 0 à 7 en tant que sorties 
  {
    pinMode(i,OUTPUT);
  }
}

void loop()
{
    PORTD = 255;
    delayMicroseconds(50); //Pause de 50 microsecondes
    PORTD = 0;
    delayMicroseconds(50); //Pause de 50 microsecondes
}

Vous pouvez télécharger ce dernier programme (DAC_R2R_Carre) en suivant ce lien. (410 Octets)

Ce deuxième programme permet de générer un signal en forme de rampe. Pour ce faire, rien de plus simple: on met en oeuvre une boucle for.

//Signal Triangulaire
//Par Pierre Pelé ([email protected])
//http://upsilonaudio.com/arduino-dac-r-2r-ladder/
//Septembre 2013

void setup()
{
  for (int i=0;i<8;i++) //Définir les broches 0 à 7 en tant que sorties 
  {
    pinMode(i,OUTPUT);
  }
}

void loop()
{
  for (int a=0;a<256;a++)
  {
    PORTD = a;
    delayMicroseconds(50); //Pause de 50 microsecondes
  }
}

Vous pouvez télécharger ce dernier programme (DAC_R2R_Rampe) en suivant ce lien. (378 Octets)

Ce programme permet de générer un signal triangulaire. Le principe de fonctionnement de ce programme est identique à celui du programme précédent.

//Signal Triangulaire
//Par Pierre Pelé ([email protected])
//http://upsilonaudio.com/arduino-dac-r-2r-ladder/
//Septembre 2013

void setup()
{
  for (int i=0;i<8;i++) //Définir les broches 0 à 7 en tant que sorties 
  {
    pinMode(i,OUTPUT);
  }
}

void loop()
{
  for (int a=0;a<256;a++)
  {
    PORTD = a;
    delayMicroseconds(50); //Pause de 50 microsecondes
  }
   for (int a=1;a<255;a++)
  {
    PORTD = 255-a;
    delayMicroseconds(50); //Pause de 50 microsecondes
  }
}

Vous pouvez télécharger ce dernier programme (DAC_R2R_Triangulaire) en suivant ce lien. (487 Octets)

Ce programme permet de générer un signal sinusoïdal. Son principe de fonctionnement est un peu différent de celui des autres programmes décrits ci-dessus, puisqu’il met en oeuvre une série de calculs faisant appel à des nombres décimaux.

//Signal Sinusoidal
//Par Pierre Pelé ([email protected])
//http://upsilonaudio.com/arduino-dac-r-2r-ladder/
//Septembre 2013

int N = 256;

void setup()
{
  for (int i=0;i<8;i++) //Définir les broches 0 à 7 en tant que sorties
  {
    pinMode(i,OUTPUT);
  }
}

void loop()
{
  for (int t=0;t<256;t++)
  {
    PORTD = 127+127*sin(2*3.14159*(t/256));
    delayMicroseconds(50); //Pause de 50 microsecondes
  }
}

Vous pouvez télécharger ce dernier programme (DAC_R2R_Sinus) en suivant ce lien. (418 Octets)

Ce programme fonctionne, mais il n’est pas exempts de défauts. Le principal problème de ce dernier programme est lié à cet instruction:

PORTD = 127+127*sin(2*3.14159*(t/256));
Manipulation de port avec Arduino

Cette dernière instruction représente à elle seule trop de calculs. Cela incombe (en partie) à l’utilisation combinée des nombres décimaux avec des fonctions trigonométriques. Pour palier à ce problème, une solution consiste à calculer toutes les valeurs associée à la fonction sin() en amont de la boucle loop() puis de stocker ces valeurs dans la mémoire de la carte Arduino. Lors de l’exécution du programme, le microcontroleur fera simplement appel à ces valeurs, plutôt que de les calculer. Une opération très rapide pour la carte Arduino. Pour générer les 256 valeurs nécessaires à l’exécution du programme, j’ai simplement fait appel à la procédure Maple ci-après (le paramètre N représente le nombre de valeurs à générer). Vous pouvez bien évidemment faire appel au logiciel/langage de votre choix pour calculer ces valeurs.

DAC := proc (N) local A, i; A := [127]; for i to N-1 do A := [op(A), floor(evalf(127+127*sin(2*Pi*i/N)))] end do; A end proc
Procédure Maple

Vous pouvez télécharger le fichier Maple associé à cette procédure en suivant ce lien. (27,37 Koctets)

J’ai stocké ces valeurs dans un tableau nommé sine. Vous remarquerez que j’ai utilisé le type de donné byte. En effet, les valeurs générées par la procédure Maple sont entières et comprises entre 0 et 255. Ce type de donné présente l’avantage de ne consommer qu’un seul octets de mémoire, contre deux octets pour le type de donné int. Il n’y a pas de petites économies ! Pour plus d’informations sur les types de données atomiques du langage C je vous renvoies sur cette page dédiée au sujet. Voici le programme dont il est désormais question.

//Signal Sinusoidal Amélioré
//Par Pierre Pelé ([email protected])
//http://upsilonaudio.com/arduino-dac-r-2r-ladder/
//Septembre 2013

byte sine[] = {127, 130, 133, 136, 139, 142, 145, 148, 151, 154, 157, 160, 163, 166, 169, 172, 175, 178, 181, 184, 186, 189, 192, 194, 197, 200, 202, 205, 207, 209, 212, 214, 216, 218, 221, 223, 225, 227, 229, 230, 232, 234, 235, 237, 239, 240, 241, 243, 244, 245, 246, 247, 248, 249, 250, 250, 251, 252, 252, 253, 253, 253, 253, 253, 254, 253, 253, 253, 253, 253, 252, 252, 251, 250, 250, 249, 248, 247, 246, 245, 244, 243, 241, 240, 239, 237, 235, 234, 232, 230, 229, 227, 225, 223, 221, 218, 216, 214, 212, 209, 207, 205, 202, 200, 197, 194, 192, 189, 186, 184, 181, 178, 175, 172, 169, 166, 163, 160, 157, 154, 151, 148, 145, 142, 139, 136, 133, 130, 127, 123, 120, 117, 114, 111, 108, 105, 102, 99, 96, 93, 90, 87, 84, 81, 78, 75, 72, 69, 67, 64, 61, 59, 56, 53, 51, 48, 46, 44, 41, 39, 37, 35, 32, 30, 28, 26, 24, 23, 21, 19, 18, 16, 14, 13, 12, 10, 9, 8, 7, 6, 5, 4, 3, 3, 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 3, 3, 4, 5, 6, 7, 8, 9, 10, 12, 13, 14, 16, 18, 19, 21, 23, 24, 26, 28, 30, 32, 35, 37, 39, 41, 44, 46, 48, 51, 53, 56, 59, 61, 64, 67, 69, 72, 75, 78, 81, 84, 87, 90, 93, 96, 99, 102, 105, 108, 111, 114, 117, 120, 123};

void setup()
{
  for (int i=0;i<8;i++) //Définir les broches 0 à 7 en tant que sorties
  {
    pinMode(i,OUTPUT);
  }
}

void loop()
{
  for (int t=0;t<256;t++)
  {
    PORTD = sine[t];
    delayMicroseconds(50); //Pause de 50 microsecondes
  }
}

Vous pouvez télécharger ce dernier programme (DAC_R2R_Sinus_Ameliore) en suivant ce lien. (1,51 Koctets)

Ce montage peux encore être amélioré. Dans une prochaine mise à jour de cet article, je publierai quelques améliorations. Je pense notamment supprimer la composante continue au moyen d’un simple filtre passif et ajouter un étage tampon (Buffer en Anglais). Cette protection est indispensable afin de protéger notre DAC des distorsions qui résultent d’une mauvaise adaptation d’impédance, au moyen d’une impédance d’entrée très élevée et d’une faible impédance de sortie. N’hésitez pas à me faire part de vos suggestions pour améliorer ce circuit !

Par ailleurs, j’envisage de rassembler toutes les fonctions (carré, rampe, triangulaire, et sinusoïdal) au sein d’un même programme Arduino. J’ajouterai au montage quelques boutons afin de changer rapidement de fonction. Je pense notamment utiliser une ou plusieurs interruptions au sein du programme pour gérer ces changements. À moins que vous n’ayez d’autres idées ?

Ce DAC est fonctionnel, mais celui-ci n’est qu’une première étape. En effet, je réfléchis à la réalisation d’une carte d’extension afin de transformer une carte Arduino en un véritable générateur de fonctions bon marché. Pour cela, j’ai l’intention d’utiliser un circuit intégré dédié afin de générer les signaux. La carte Arduino permet alors de piloter ce dernier circuit intégré (Analog Devices propose différents circuits adaptés à cette tâche). Vous pouvez suivre l’avancement de ce projet en vous rendant sur cette page. Affaire à suivre …

Vous avez aimé cet article ?! Suivez moi sur TwitterPierre Pelé

Mise à jour 16/10/2013: Réécriture des programmes avec l’indentation.
Mise à jour 24/10/2013: Ajout de précisions sur la manipulation des ports du microcontrôleur.
Mise à jour 12/12/2013: Ajout des programmes sur GitHub !