MCP4725 DAC Module (Part 1)

One of the limits of the Arduino Uno is that it doesn't have a DAC. The external MCP4725 module is thus an interesting extension.

MCP4725 DAC Module

The MCP4725 is a 12-bit DAC from Microchip that can be connected to the Arduino using I2C. A digital-to-analog converter (DAC) allows to output an analog signal with a microcontroller. It does basically the opposite of an analog-to-digital converter, which allows to measure an analog voltage and convert it to a digital number. The MCP4725 features a single analog output. Its big cousin, the MCP4728, has four of them.

There are different methods available to convert a digital value into an analog voltage. The MCP4725 is a resistive string DAC. It contains a string of 4096 identical resistors which form a huge voltage divider. The voltage divider permanently generates all the 4096 possible output voltages. A digitally controllable switch matrix allows us to select one of the 4096 voltage outputs of the voltage divider and connect it with the DACs output stage. With 5V supply voltage it is thus possible to select output voltages between 0 V and 4.9988 V in 0.0012 V steps. In the Arduino program the output voltage is represented using an integer between 0 and 4095. The conversion between this value and the actual voltage is possible using the following equation:
$$V_{DAC} = {{value \cdot V_{cc}} \over 4096}$$

Resistive string DACs allow for fast voltage changes and feature a clean output signal with low noise. However, DACs using this technique usually have a relatively small resolution. To increase the resolution, e.g. to 16-bit (65536 steps), a huge amount of resistors and switches would be necessary and the cost for such a DAC would explode.

The MCP4725 provides the following features:

• 12-bit resolution (0.0012 V @ 5V)
• I2C Interface
• 2.7 - 5.5 V supply voltage range
• 6 us settling time
• EEPROM to store a power up value

The more interesting question is, however, what can we do with it? What are the possible applications for such a DAC?

The most common application for DACs is the generation of analog signals like audio signals. While the MCP4725 can be used for signal generation, it is not designed for classical audio applications. For audio applications a 16-bit accuracy is the de facto standard and some audio DACs even feature 24-bit accuracy. For this type of applications other types of DACs are used. Additionally, the Arduino Uno would not be able to output the audio samples fast enough, anyway, as it can only drive the I2C bus at a maximum speed of 400 kHz. As we need to transmit at least 2 bytes (16-bit) to set the 12-bit output value, we can only achieve a sampling rate of 25 kHz. In practice the sampling rate is a lot lower, as we need to account for the time the Arduino needs to prepare the I2C package and send the device address over the bus, as well. We would need at least 40 kHz to play music or voice recordings without distortions.

Audio DACs typically use I2S instead of I2C. I2S uses faster data rates by default. It is not supported by the Arduino Uno, however. The Arduino Uno is not suited for such an application anyway, as it does not have enough storage to store the audio data. The only thing we can do with the Arduino Uno itself, is creating simple tones. For projects with more complicated sounds or music, we need to use external modules that can store and play sound files. They can then be triggered by the Arduino.

An example for what we can do with the MCP4725 and the Arduino Uno, is creating simple signals like sine or triangular waves. With the Arduino Uno itself we can only generate square waves e.g. PWM signals. The DAC extends our past possibilities.

In general, DACs are not designed to be used as adjustable voltage source to power other components. The MCP4725 is rated for a maximum of 25 mA of short circuit current, however, that does not mean that we can load it with 25 mA. Even at only a few microamperes the DAC isn't able to keep the output voltage stable. For a stable output voltage the current should be below 1 mA. We thus can't connect the DAC directly to an LED. The LED's brightness couldn't be properly controlled by changing the voltage anyway because of its highly non-linear I-V curve.

DACs are almost always used in conjunction with external circuits. In case of audio or signal generation this could be an amplifier circuit that is powerful enough to e.g. drive a speaker. However, there are way more applications for DACs.

The MCP4725 can e.g. be used to programmatically set threshold or offset values for other analog circuits. A good example for this are the threshold modules that I presented in earlier tutorials. They all featured a potentiometer for setting a threshold value. These modules also feature a LM393 comparator that then compares the sensor value to the threshold value and the toggles the digital output of the threshold module respectively. As an alternative to the potentiometer, one could use a DAC to set this threshold value programmatically.

In this part of the tutorial, we will take a quick look at signal generation. In the next part of this tutorial, I want to show you a more complex circuit in which we use the MCP4725 to realize an Arduino controlled current source.

MCP4725 Arduino Library

To use the MCP4725 with the Arduino we need a library. For the purpose of this tutorial we will use the one provided by Adafruit. You can install it by using the library manager under Tools > Manage Libraries ....

The library allows us to set the output voltage using the setVoltage(int value, bool writeEEPROM) method. To use it we need to create an instance of Adafruit_MCP4725 and configure the address of the DAC in setup. The MCP4725's address is normally either 0x60 or 0x61. However, if you use the Adafruit MCP4725 module the address is either 0x62 or 0x63.

Here is a short example that shows how a DAC instance can be created and configured in code:

#include <Adafruit_MCP4725.h>

void setup() {
}

After that we can call the setVoltagemethod of our dac object to send the desired output value to the MCP4725:

dac.setVoltage(value, false);

To store the value as power up value, you can set the writeEEPROM parameter to true. Setting the power up value is slower than just setting the output value. As the EEPROM supports only a limited number of writes, you should only update the startup value if you need to do it.

Signal generation

As a simple signal generation example, we are going to generate a sawtooth wave like shown in the image below. To keep it simple we won't specify an output frequency.

Our first step is to connect the MCP4725 module to the Arduino. The wiring is straight forward. Connect VCC and GND to 5V and GND on the Arduino Uno and hook up the I2C data and clock lines SDA and SCL to the identically labeled pins of the Arduino. The generated signal is available on the OUT pin. This module also comes with an extra GND pin which simplifies the wiring. To visualize the outputted signal we can just hook up OUT and GND to an oscilloscope. Not all MCP4725 modules have this extra GND pin. If yours doesn't have it, you can use one of the Arduino's GND pins instead. It you don't have a scope, you can instead connect the output to a multimeter and reduce the frequency of the sawtooth signal by adding a delay after the setVoltagecall. A sufficiently long delay will allow you to keep track of the voltage changes on a multimeter.

The code is quite simple as we only need to increase the voltage until we reach the maximum and then start again at 0 V. For this, we just use a for-loop with a counter in which we send the counter value to the DAC in each iteration. The counter is increased by one until we reach the DAC's maximum value of 4095.

Here is how this looks like in code:

#include <Adafruit_MCP4725.h>

void setup() {
}

void loop() {
// Generate a sawtooth wave
for(int i = 0; i < 4096; i++) {
dac.setVoltage(i, false);
}
}

Once the code is uploaded to the Arduino it will start to generate the sawtooth wave. It takes quite some time to iterate through all 4096 steps and send each value to the DAC. A full cycle takes roughly 650 ms or in other words we get a signal with a frequency of roughly 1.5 Hz. If we wanted to use a higher frequency, we would need to skip some output values. For 3 Hz we could for example increment the counter by 2 in each iteration.

There would be much more to say about signal generation. Especially, if the goal is to generate a signal with a specific frequency. For the purpose of this tutorial, however, I like to keep the example as simple as possible.