Bandwidth of Uno+MCP4725 DAC

I'm trying to make a simple feed-forward circuit using an Uno and MCP4725 DAC breakout board from Sparkfun. I got it up and running, then discovered that putting in a signal (sine or square wave) at more than a few Hz, caused the output to quickly degrade. For the sine wave this was a loss of smoothness as the signal became digitized, and for the square wave, this was jitter in the output frequency.

I was hoping to achieve a bandwidth of about a KHz. I simplified my code as much as possible by not storing anything in program memory and only having two values in the lookup table, but this did not increase the speed of the response. The code it below.

Is this typical behavior, and I've overlooked a fundamental obstacle?

#include <Wire.h>  //Include the Wire library to talk I2C
//#include <avr/pgmspace.h> //Include this to enable writing to PROGMEM

#define MCP4725_ADDR 0x60//This is the I2C Address of the MCP4725 (Sparkfun MCP4725A0), by default (A0 pulled to GND).
int DACLookup[2] =
{
  0,
  2500 //Careful you have 4096 values but one is zero
};
//--------------------------------------------------------------------------

void setup()
{
  Serial.begin(9600);//Serial output for debugging

  Wire.begin();
  // Set A2 and A3 of the Arduino as Outputs to make them our GND and Vcc,
  //which will power the MCP4725
  pinMode(A2, OUTPUT);
  pinMode(A3, OUTPUT);
  digitalWrite(A2, LOW);//Set A2 as GND
  digitalWrite(A3, HIGH);//Set A3 as Vcc
}
//--------------------------------------------------------------------------

void loop()
{
  //ANALOG INPUT
  //-------
  int   analogIn = analogRead(A0); // Reads in a value (0-1023) depending on the voltage on A0 (0-5V).
  boolean inputState;

  if (analogIn <= 512)
  {
    inputState = false;
  }
  else
  {
    inputState = true;
  }


  //DAC OUTPUT
  //-------
  Wire.beginTransmission(MCP4725_ADDR);
  Wire.write(64); // cmd to update the DAC (0x60 in hex = 60 in binary = The start bit for the DAC)


  int analogOut = DACLookup[inputState]; //

  Wire.write(analogOut >> 4); // Grab the 8 most significant bits first.  (D11.D10.D9.D8.D7.D6.D5.D4)
  Wire.write((analogOut & 15) << 4); // the 4 least significant bits. (D3.D2.D1.D0.x.x.x.x)
  Wire.endTransmission();
  Serial.println(analogOut);

}

Adafruit has example code here. Check the trianglewave example and test image. They set the I2C frequency to 400kHz, and run the full 12bit code up and down (8192). The test image shows frequency of 283.03mHz. So 8192/0.28303sec = 28.944 kHz (bandwidth).

If you're generating a sinewave (or any waveform) with 8192 data points per cycle, then the max frequency would be about 3.5Hz with the I2C clock at 400kHz.

Also, I wouldn't use serial.print after writing each DAC value as this will drastically slow things down.

I see. So bandwidth is not the word I should be using to describe what I want, which is how fast can I ramp the input and the output still follows faithfully.

I already tried limiting the output to two points to try and speed it up, with no noticeable improvement, though I would expect one. I'll get rid of the serial printout, but it looks like the I2C communication speed is a fundamental bottleneck for what I want to do.

Perhaps an SPI DAC chip?

Yes, SPI is much better suited. However, its still quite easy to hit a bottleneck when generating sinewaves and there's lots to consider.

The performance of the MCU.
What output voltage range do you need 0-5, 0-10, ±5, ±10?
Does the output need to be ground referenced?
Accuracy, resolution, how many data points per cycle?

Note there are some Arduino and compatible boards that have DAC built in.
The Due has 12bit DAC and outputs a signal in the range (1/6)3.3V to (5/6)3.3V.
The Teensy3.X and TeensyLC has one DAC

An example using a low cost external SPI:
SPI_DAC MCP4921

Thanks. Removing the serial print and increasing the I2C speed to 400KHz have me about an order of magnitude improvement. I'm going to look into SPI DACs for the final implementation though.

Looking here: Gammon Forum : Electronics : Microprocessors : Comparison of transfer protocols

It seems that at most I'd get an order of magnitude increase in speed by switching to SPI. This would put me at ~500Hz, and I'm looking to get about 1KHz. Perhaps there is some sort of parallel implementation possibility.

For the Uno, with SPI at the default 4MHz clock rate, and sending 2 bytes per transaction (16-bits), this calculates to 250K transactions/second.

For other boards, take a look at SPIperf.txt and examples here

Arduino Due SPI with DMA @ 42MHz transfers at 41.45 million bits/second.
With transfer size set to 12 bits, this is transferring data at 3,454,166 transactions/second.

Another example here:

As an update, the Due works significantly better. With a full 12-bit lookup table in RAM, I can mirror an input sign wave up to about 1.5KHz with little degradation in the signal.

dlloyd:
Adafruit has example code here. Check the trianglewave example and test image. They set the I2C frequency to 400kHz, and run the full 12bit code up and down (8192). The test image shows frequency of 283.03mHz. So 8192/0.28303sec = 28.944 kHz (bandwidth).

If you're generating a sinewave (or any waveform) with 8192 data points per cycle, then the max frequency would be about 3.5Hz with the I2C clock at 400kHz.

Can you explain to me how do I calculate the frequency of the sinewave generated with the MCP4725?

akuibida:
Can you explain to me how do I calculate the frequency of the sinewave generated with the MCP4725?

This is quite an old thread.

Is your question relating to determining maximum possible frequency for a constructed/reconstructed sinewave from a MCP4725?

To calculate the output frequency, time how long it takes to write 360 degrees of the output waveform. The reciprocal of that time is the frequency.

If you don’t know how to accurately time events with Arduino, have a look at the “blink without delay” example sketch. This method can be used with both millis() and micros(), depending upon the resolution you need.

To approach it from the other end, for a given output frequent in Hz:

1 / desired frequency / number of DAC bits in output * 1000

Would be the number of milliseconds to wait between each bit output write.