Incrementar resolución del DAC MCP4725

Muy buenas!

Tengo un proyecto donde uso un MCP-4725 para crear sonido. El caso és que creo que el DAC MCP4725 no tiene rendimiento suficiente (resolución) como para ejecutar el programa a la velocidad requerida. Lo sé porque cada vez que añado cosas al código, la señal de salida empeora.

Sabéis si hay alguna forma de augmentar la resolución al DAC? O bien otro modelo que sea de alto rendimiento?

Grácias :slight_smile: :slight_smile: :slight_smile:

Hi,
Aqui creo que tu tienes la contentacion a tu problema cuando dices que a medida que aumentas el program esto hace que el micro le tome mas tiempo al ejecutar el programa. No dices que micros usas pues posiblemente vaz a necesitar un micro mas rapido o posiblemente optimizar tu programa.

Si! mi microchip és el DAC MCP 4725, sabéis de alguno que tenga más rendimiento. O si quizás hay alguna configuración mediante código que le haga trabajar a mayor resolución?

Gracias

Vas a Google y pones DAC MCP 4725 y encuentras que es de 12 bits
Luego dos caminos, o miras en Microchip o miras en alguna casa de componentes.

la otra opcion de Digikey es
MCP4728 Evaluation Board de 12 bits tambien.

Google: Adafruit Dac arroja este resultado 0 No hay nada mejor que 12 bits

Un búsqueda mas amplia

AD5667 2 canales 16 bits

16 bits AD9164 aunque es carisimo porque luce como para aplicaciones muy avanzadas.

Texas tiene este DAC7742

Hi,
Sugerencia porque en vez de usar el MCP4772 usas el MCP4821 que es tambien 12 bits. Yo uso este para regular el voltaje de los rails de mi amplificador con buenos resultado. Creo que tiene menos delay en salida.

@tauro: Porque el titulo dice: "Incrementar resolución del DAC MCP4725" cosa que no se puede.
Lo que si puede es comprar otro de mas bits.

Hi,
Segun lo que el explica

rendimiento suficiente (resolución) como para ejecutar el programa a la velocidad requerida.

esto quiere decir que el micro processor no tiene velocidad suficiente para reproducir el sonido con fidelidad. El puede usar uno de 16 bits pero el problema va hacer el mismo. Para mi la velocidad es la rapidez que se puede reproducir el sonido.El aumentar los bits no te va disminuir la distorcion del sonido. Para que veas adjunto dos link que usan el ATTINY micro para reproducir sonido y solamente usan 8bits.

Si se expresa mal es un problema no te parece?

Resolución es cantidad de bits.
Velocidad de respuesta o ancho de banda es otro tema.

En ese punto tienes razón @tauro0221, pero mirando la hoja de datos del MCP4725 me encuentro con esto

High-Speed (3.4 Mbps) Modes

Si soporta altas velocidades de datos de 3.4Mbps entonces, la conclusión es obvia, mal código.

Por favor, coloca el código que esas usando para evaluar mejor tu problema.

Tu DAC tiene capacidad de al menos transferir sea en 100Kbps (Modo standard), 400 Kbps o hasta 3.6Mbps en High Speed Mode.

Hi,
Ya yo le sugeri eso que posiblemente necesita optimizar la programacion para aumentar la velocidad del micro. Si vez en los link ellos usan PWM para mandar la senal de sonido pero el no esplica como lo esta haciendo. Estoy de acuerdo contigo que debe de bajar el sketch que el usa para poder darle consejo que pasos a tomar para mejor la velocidad. Espero que no use delays. Otra cosa es que el puede convertir PWM a analoga usando una resistencia y un condesador. Esto convierte la senal digital a analoga.

Muy buenas!

Muchíssimas gracias por sus respuestas. Soy muy nuevo en esto y me disculpo de antemano si no me expresso con claridad. Leyendo todas sus respuestas, e visto que se puede configurar el MCP4725 en "High Speed Mode". Alguien sabria decirme como realizar esta configuración? He buscado por todas partes y lo único que he encontrado es la función Wire.setClock() y cuando la pongo en el código no me funciona.

También les dejo aquí como me an dicho mi código, aunque creo que no está muy cargado, pero quizás podéis ayudarme. Muchísimas gracias comunidad Arduino :grinning:

#include <Wire.h>
#include <Adafruit_MCP4725.h>
#include "avr/pgmspace.h"
#include <avr/interrupt.h>
#include <avr/io.h>

#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))


Adafruit_MCP4725 dac;

int sineVal = 0;
int tono[ ] = {261, 277, 294, 311, 330, 349, 370, 392, 415, 440,466};
                   // mid C C# D D# E F F# G G# A
int p = 0;
int n = 0;
int ref1 = 0;
int ref2 = 0;
int ledPin = 13;


// table of 256 sine values / one sine period / stored in flash memory
const PROGMEM byte  sine256[]  = {
 127,130,133,136,139,143,146,149,152,155,158,161,164,167,170,173,176,178,181,184,187,190,192,195,198,200,203,205,208,210,212,215,217,219,221,223,225,227,229,231,233,234,236,238,239,240,
 242,243,244,245,247,248,249,249,250,251,252,252,253,253,253,254,254,254,254,254,254,254,253,253,253,252,252,251,250,249,249,248,247,245,244,243,242,240,239,238,236,234,233,231,229,227,225,223,
 221,219,217,215,212,210,208,205,203,200,198,195,192,190,187,184,181,178,176,173,170,167,164,161,158,155,152,149,146,143,139,136,133,130,127,124,121,118,115,111,108,105,102,99,96,93,90,87,84,81,78,
 76,73,70,67,64,62,59,56,54,51,49,46,44,42,39,37,35,33,31,29,27,25,23,21,20,18,16,15,14,12,11,10,9,7,6,5,5,4,3,2,2,1,1,1,0,0,0,0,0,0,0,1,1,1,2,2,3,4,5,5,6,7,9,10,11,12,14,15,16,18,20,21,23,25,27,29,31,
 33,35,37,39,42,44,46,49,51,54,56,59,62,64,67,70,73,76,78,81,84,87,90,93,96,99,102,105,108,111,115,118,121,124

};


double dfreq;
// const double refclk=31372.549;  // =16MHz / 510
const double refclk=31376.6;      // measured

// variables used inside interrupt service declared as voilatile
volatile byte icnt;              // var inside interrupt


volatile unsigned long phaccu;   // pahse accumulator
volatile unsigned long tword_m;  // dds tuning word m
volatile int timerTick = 0;

void setup(void) {
  
  pinMode(ledPin, OUTPUT);
  Serial.begin(9600);
  
   // For Adafruit MCP4725A1 the address is 0x62 (default) or 0x63 (ADDR pin tied to VCC)
  // For MCP4725A0 the address is 0x60 or 0x61
  // For MCP4725A2 the address is 0x64 or 0x65
  
  dac.begin(0x60);
   
 
 Setup_timer2();

 // disable interrupts to avoid timing distortion
    cbi (TIMSK0,TOIE0);              // disable Timer0 !!! delay() is now not available
    sbi (TIMSK2,TOIE2);              // enable Timer2 Interrupt
    
}


void loop(void) 
{
      while ((ref1 == 0) and (ref2 == 0))
      {
      
        digitalWrite(ledPin, HIGH);   // turn the LED on (HIGH is the voltage level)
          ref1 = analogRead(A0);
          Serial.print (ref1);
        delay(700000);                       // wait for a second
      
        digitalWrite(ledPin, LOW);    // turn the LED off by making the voltage LOW
        delay(500000); 

        digitalWrite(ledPin, HIGH);
          ref2 = analogRead(A0);
          Serial.print (ref2);
        delay(500000);
       } 
      
      if  (ref2 != 0)
      {
           p = analogRead(A0);
           n = map (p, ref1, ref2, 0, 11) ; // Ell array solo tiene 12 notas
           
double ValorFr= tono[n]*0.4;

dfreq=ValorFr;                    // initial output frequency = 1000.o Hz
 tword_m=pow(2,32)*dfreq/refclk;  // calulate DDS new tuning word 

}

if (timerTick = 1)
     {         


 dac.setVoltage(sineVal, false);
  
 timerTick = 0;
     }

        
}



//******************************************************************
// Timer2 Interrupt Service at 31372,550 KHz = 32uSec
// this is the timebase REFCLOCK for the DDS generator
// FOUT = (M (REFCLK)) / (2 exp 32)
// runtime : 8 microseconds ( inclusive push and pop)
// 125 ticks = 4 milliseconds
ISR(TIMER2_OVF_vect) 
 {


  phaccu=phaccu+tword_m; // soft DDS, phase accu with 32 bits
icnt=phaccu >> 24;     // use upper 8 bits for phase accu as frequency information

  sineVal = (pgm_read_byte_near(sine256 + icnt))<<4;


     timerTick = 1;


    
    
 }

//******************************************************************
// timer2 setup
// set prscaler to 1, PWM mode to phase correct PWM,  16000000/510 = 31372.55 Hz clock
void Setup_timer2() {
  
// Timer1 Clock Prescaler to : 1
 sbi (TCCR2B, CS20);
cbi (TCCR2B, CS21);
 cbi (TCCR2B, CS22);

 // Timer1 PWM Mode set to Phase Correct PWM
cbi (TCCR2A, COM2A0);  // clear Compare Match
 cbi (TCCR2A, COM2A1);

 cbi (TCCR2A, WGM20);  // Mode 1  / Phase Correct PWM
 cbi (TCCR2A, WGM21);
 cbi (TCCR2B, WGM22);
}
/**************************************************************************/

/**************************************************************************/
  1. Observación menor.
    Entiendo que esto se usa para programar tu DDS pero 500 seg o 700 segundos?
        delay(500000);
  1. pow
    Tu código tiene cosas para mejorar
tword_m=pow(2,32)*dfreq/refclk;  // calulate DDS new tuning word

pow es un algoritmo lento.. si realmente necesitas la potencia 32 del 2 solo escribela pero no la calculas cada vez!

pow(base, exponent)
pow(2,32) = 4294967296

Ahora este número es el máximo de un unsigned long de modo que la cuenta tiene desborde

tword_m=pow(2,32)*dfreq/refclk; // calulate DDS new tuning word
En tu cuenta involucras dos double como dfreq y refclk y un numero al limite sin hacer typecast
mejor sería algo asi
pow(2,32) = 4294967296
4294967296/refclk = 13688,44 eliminamos los decimales porque con operaciones enteras no tienen sentido

tword_m = (unsigned long) (13688 * dfreq);

Eso es mas rápido que lo que usas.

  1. Acá un tema importante
    Esto se ejecuta siempre que estes en el loop porque no hay potenciómetro que te entregue 0 Voltios.
if  (ref2 != 0) {
		p = analogRead(A0);
		n = map (p, ref1, ref2, 0, 11) ; // Ell array solo tiene 12 notas

		double ValorFr= tono[n]*0.4;

		dfreq = ValorFr;                    // initial output frequency = 1000.o Hz
		tword_m = pow(2,32)*dfreq/refclk;  // calulate DDS new tuning word 
	}

Esto te esta generando una demora de tiempo importante.
Olvida este método que usas para reprogramar tu DDS e incluye un par de pulsadores.

Solo para que veas, comenta esta parte y coloca valores fijos, recompila y mira como se comporta el sistema.

@surbyte eres un crack!! Muchas gracias por la aportación. Y siento no poder contestar antes

Sobre el punto 1, realmente són 5 i 7 segundos, aunque nose porué me hace ponerlo de esta forma (500000 i 700000) en vez de 5000 i 7000. En todo caso són delay demasiado altos, los he bajado a un segundo.

Sobre el punto 2, muchas gracias, realmente puedo ahorrarme el calculo repetitivo y hacer-lo como me dijiste.

Y sobre el punto 3, no havia caído en que realmente puedo hacerlo por pulsadores, ya que la lectura analógica constante crea esta demora que empeora la señal de salida. Lo he probado usando el led de la placa como en la fase anterior para configurar el DDS. Creeis que es la mejor forma? Realmente cuando coge bien el valor, la señal tiene mucha más calidad que antes, el problema es que a la que varia la lectura analógica a éste le cuesta procesar-lo. No se si es culpa de los delay() o de cuando el pin Led esta apagado. Es decir cuando mejor funciona es cuando coje un valor fijo en la lectura analógica.

Les adjunto mi código y un par de fotos de la salida en el osciloscopio.

Muchisimas gracias comunidad arduino!!

#include <Wire.h>
#include <Adafruit_MCP4725.h>
#include "avr/pgmspace.h"
#include <avr/interrupt.h>
#include <avr/io.h>

#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))


Adafruit_MCP4725 dac;

int sineVal = 0;
int tono[ ] = {261, 277, 294, 311, 330, 349, 370, 392, 415, 440,466};
                   // mid C C# D D# E F F# G G# A
int p = 0;
int n = 0;
int ref1 = 0;
int ref2 = 0;
int ledPin = 13;


// table of 256 sine values / one sine period / stored in flash memory
const PROGMEM byte  sine256[]  = {
 127,130,133,136,139,143,146,149,152,155,158,161,164,167,170,173,176,178,181,184,187,190,192,195,198,200,203,205,208,210,212,215,217,219,221,223,225,227,229,231,233,234,236,238,239,240,
 242,243,244,245,247,248,249,249,250,251,252,252,253,253,253,254,254,254,254,254,254,254,253,253,253,252,252,251,250,249,249,248,247,245,244,243,242,240,239,238,236,234,233,231,229,227,225,223,
 221,219,217,215,212,210,208,205,203,200,198,195,192,190,187,184,181,178,176,173,170,167,164,161,158,155,152,149,146,143,139,136,133,130,127,124,121,118,115,111,108,105,102,99,96,93,90,87,84,81,78,
 76,73,70,67,64,62,59,56,54,51,49,46,44,42,39,37,35,33,31,29,27,25,23,21,20,18,16,15,14,12,11,10,9,7,6,5,5,4,3,2,2,1,1,1,0,0,0,0,0,0,0,1,1,1,2,2,3,4,5,5,6,7,9,10,11,12,14,15,16,18,20,21,23,25,27,29,31,
 33,35,37,39,42,44,46,49,51,54,56,59,62,64,67,70,73,76,78,81,84,87,90,93,96,99,102,105,108,111,115,118,121,124

};


double dfreq;
// const double refclk=31372.549;  // =16MHz / 510
const double refclk=31376.6;      // measured

// variables used inside interrupt service declared as voilatile
volatile byte icnt;              // var inside interrupt


volatile unsigned long phaccu;   // pahse accumulator
volatile unsigned long tword_m;  // dds tuning word m
volatile int timerTick = 0;

void setup(void) {
  
  pinMode(ledPin, OUTPUT);
  Serial.begin(9600);
  
     // For Adafruit MCP4725A1 the address is 0x62 (default) or 0x63 (ADDR pin tied to VCC)
  // For MCP4725A0 the address is 0x60 or 0x61
  // For MCP4725A2 the address is 0x64 or 0x65
  
  dac.begin(0x60);
  
   
 
 Setup_timer2();

 // disable interrupts to avoid timing distortion
    cbi (TIMSK0,TOIE0);              // disable Timer0 !!! delay() is now not available
    sbi (TIMSK2,TOIE2);              // enable Timer2 Interrupt
    
}


void loop(void) 
{
      while ((ref1 == 0) and (ref2 == 0))
      {
      
        digitalWrite(ledPin, HIGH);   // turn the LED on (HIGH is the voltage level)
          ref1 = analogRead(A0);
          Serial.print (ref1);
        delay(100000);                       // wait for a second
      
        digitalWrite(ledPin, LOW);    // turn the LED off by making the voltage LOW
        delay(100000); 

        digitalWrite(ledPin, HIGH);
          ref2 = analogRead(A0)+ 20;
          Serial.print (ref2);
        delay(100000);
       } 
      
    
        digitalWrite(ledPin, HIGH);   // turn the LED on (HIGH is the voltage level)
          
          p = analogRead(A0);
          n = map (p, ref1, ref2, 0, 11) ; // Ell array solo tiene 12 notas          
          double ValorFr= tono[n]*0.4;

          dfreq=ValorFr;                    // initial output frequency = 1000.o Hz
          tword_m=(136884*dfreq);  // calulate DDS new tuning word 
            
         delay (1000);

        digitalWrite(ledPin, LOW);    // turn the LED off by making the voltage LOW

         tword_m=(136884*dfreq);  // calulate DDS new tuning word  

          
        delay(1000);
        
                  
     
if (timerTick = 1)
     {         


 dac.setVoltage(sineVal, false);
  
 timerTick = 0;
     }

        
}



//******************************************************************
// Timer2 Interrupt Service at 31372,550 KHz = 32uSec
// this is the timebase REFCLOCK for the DDS generator
// FOUT = (M (REFCLK)) / (2 exp 32)
// runtime : 8 microseconds ( inclusive push and pop)
// 125 ticks = 4 milliseconds
ISR(TIMER2_OVF_vect) 
 {


  phaccu=phaccu+tword_m; // soft DDS, phase accu with 32 bits
icnt=phaccu >> 24;     // use upper 8 bits for phase accu as frequency information

  sineVal = (pgm_read_byte_near(sine256 + icnt))<<4;


     timerTick = 1;


    
    
 }

//******************************************************************
// timer2 setup
// set prscaler to 1, PWM mode to phase correct PWM,  16000000/510 = 31372.55 Hz clock
void Setup_timer2() {
  
// Timer1 Clock Prescaler to : 1
 sbi (TCCR2B, CS20);
cbi (TCCR2B, CS21);
 cbi (TCCR2B, CS22);

 // Timer1 PWM Mode set to Phase Correct PWM
cbi (TCCR2A, COM2A0);  // clear Compare Match
 cbi (TCCR2A, COM2A1);

 cbi (TCCR2A, WGM20);  // Mode 1  / Phase Correct PWM
 cbi (TCCR2A, WGM21);
 cbi (TCCR2B, WGM22);
}
/**************************************************************************/

/**************************************************************************/

Hi,
Para tu consideracion adjunto un link AD9850 DDS usando un modulo para producir DDS.

Link:https://telecnatron.com/articles/Variable-Gain-Amplifier-For-AD9850-DDS/index.html

Tu debes tener una rutina de generación de señales libre de cambios.
Cuando necesites un cambio lo haces y luego al terminar el sistema debe volve a generar sin nada que lo perturbe.
Por eso te sugerí pulsadores porque solo debes ver si se ha pulsado para prestar atención a la rutina de ajuste de la frecuencia deseada.

Gracias por sus respuestas, el problema que tengo @surbyte es que no puedo introducir unos pulsadores físicos al sistema, entonces deveria simular-los con algun especie de contador, pero no consigo lograr-lo. Se os ocurre si existe alguna función que vaya por tiempo? es decir que cada X tiempo canvie de variable? Un especie de pulsador pero simulado.

Y la otra pregunta que queria hacer es si alguien sabe como configurar el DAC MCP4725, para que funcione a 400mbps o al High Speed mode? Para el 400mbps he visto que existe el TWBR=12 pero lo pongo en el Setup del código y no funciona, la verdad és que no sé exactamente como deve ir.

A continuación os pongo el código que estoy usando, es igual que el anterior pero mas simplificado. Y efectivamente @surbyte cuando comenté la parte de la lectura analógica y entro los datos manualmente el sistema se comporta mucho mejor.

Gracias por su ayuda.

/**************************************************************************/
#include <Wire.h>
#include "Adafruit_MCP4725.h"
#include "avr/pgmspace.h"
#include <avr/interrupt.h>
#include <avr/io.h>

#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))


Adafruit_MCP4725 dac;

int sineVal = 0;

 
// table of 256 sine values / one sine period / stored in flash memory
const PROGMEM byte  sine256[]  = {
 127,130,133,136,139,143,146,149,152,155,158,161,164,167,170,173,176,178,181,184,187,190,192,195,198,200,203,205,208,210,212,215,217,219,221,223,225,227,229,231,233,234,236,238,239,240,
 242,243,244,245,247,248,249,249,250,251,252,252,253,253,253,254,254,254,254,254,254,254,253,253,253,252,252,251,250,249,249,248,247,245,244,243,242,240,239,238,236,234,233,231,229,227,225,223,
 221,219,217,215,212,210,208,205,203,200,198,195,192,190,187,184,181,178,176,173,170,167,164,161,158,155,152,149,146,143,139,136,133,130,127,124,121,118,115,111,108,105,102,99,96,93,90,87,84,81,78,
 76,73,70,67,64,62,59,56,54,51,49,46,44,42,39,37,35,33,31,29,27,25,23,21,20,18,16,15,14,12,11,10,9,7,6,5,5,4,3,2,2,1,1,1,0,0,0,0,0,0,0,1,1,1,2,2,3,4,5,5,6,7,9,10,11,12,14,15,16,18,20,21,23,25,27,29,31,
 33,35,37,39,42,44,46,49,51,54,56,59,62,64,67,70,73,76,78,81,84,87,90,93,96,99,102,105,108,111,115,118,121,124

};



double dfreq;
// const double refclk=31372.549;  // =16MHz / 510
const double refclk=31376.6;      // measured

// variables used inside interrupt service declared as voilatile
volatile byte icnt;              // var inside interrupt


volatile unsigned long phaccu;   // pahse accumulator
volatile unsigned long tword_m;  // dds tuning word m
volatile int timerTick = 0;



void setup(void) {
  
    Serial.begin(9600);

  
   // For Adafruit MCP4725A1 the address is 0x62 (default) or 0x63 (ADDR pin tied to VCC)
  // For MCP4725A0 the address is 0x60 or 0x61
  // For MCP4725A2 the address is 0x64 or 0x65
  
  dac.begin(0x60);
      
 
 Setup_timer2();

 // disable interrupts to avoid timing distortion
    
    cbi (TIMSK0,TOIE0);              // disable Timer0 !!! delay() is now not available
    sbi (TIMSK2,TOIE2);              // enable Timer2 Interrupt
    
}


void loop(void) 
{
      
      int tono[ ] = {261, 277, 294, 311, 330, 349, 370, 392, 415, 440,466};
                   // mid C C# D D# E F F# G G# A

          int p = analogRead(A0);
          int n = map (p, 0,901, 0, 11) ; // Ell array solo tiene 12 notas
              
          
double ValorFr= tono[n]*0.4;

dfreq=ValorFr;                    // initial output frequency = 1000.o Hz
 tword_m=pow(2,32)*dfreq/refclk;  // calulate DDS new tuning word 


if (timerTick = 1)
     {         


 dac.setVoltage(sineVal, false);
  
 timerTick = 0;
     }

        
}



//******************************************************************
// Timer2 Interrupt Service at 31372,550 KHz = 32uSec
// this is the timebase REFCLOCK for the DDS generator
// FOUT = (M (REFCLK)) / (2 exp 32)
// runtime : 8 microseconds ( inclusive push and pop)
// 125 ticks = 4 milliseconds
ISR(TIMER2_OVF_vect) 
 {

  
  phaccu=phaccu+tword_m; // soft DDS, phase accu with 32 bits
icnt=phaccu >> 24;     // use upper 8 bits for phase accu as frequency information

  sineVal = (pgm_read_byte_near(sine256 + icnt))<<4;


     timerTick = 1;


    
    
 }

//******************************************************************
// timer2 setup
// set prscaler to 1, PWM mode to phase correct PWM,  16000000/510 = 31372.55 Hz clock
void Setup_timer2() {

  
// Timer1 Clock Prescaler to : 1
 sbi (TCCR2B, CS20);
cbi (TCCR2B, CS21);
 cbi (TCCR2B, CS22);

 // Timer1 PWM Mode set to Phase Correct PWM
cbi (TCCR2A, COM2A0);  // clear Compare Match
 cbi (TCCR2A, COM2A1);

 cbi (TCCR2A, WGM20);  // Mode 1  / Phase Correct PWM
 cbi (TCCR2A, WGM21);
 cbi (TCCR2B, WGM22);

}