Arduino and MCP4706 DAC - Page 1
Forum section: electronics / projects

author: PioB
author_rank: Regular Contributor
timestamp: July 23, 2013, 07:58:18 pm
content: Hi All,I am looking to use one of these small Microchip 8 bit DACs (MCP4706) with an arduino to provide a control voltage for a motor that takes a 0-5 Volt DC voltage on its control inputs. Adafruit has a Library for a 12 bit DAC (4725,https://github.com/adafruit/Adafruit_MCP4725) which I took and slightly modified in order to pass the correct data to my DAC.If I run the provided example to generate a triangle voltage, everything looks normal on the oscilloscope. However if I add a small delay (1 ms, 10 ms) between each output to the DAC the curve looks more like exponential decay/ logarithmic increase than a triangle with longer period. Also, the peak to peak Voltage decreases from about 4.8 V to 1.9 V to about .25 V. (The noise increases by a lot)At the moment I soldered it dead bug style to two headers and plugged it into a breadboard.(SDA, SCL via 10 kOhm Pullups to 5V to the Arduino, Vref to Vcc)The connections are correct, I put a .1 uF bypass capacitor between 5V and GND next to the headers. Vout is connected to the probe of the oscilloscope, it doesn't matter whether pulled down to ground via a 330 Ohm resistor (or bigger) or open circuit.The modified code is as follows:Code:/**************************************************************************//*!@file trianglewave.pde@author Adafruit Industries@license BSD (see license.txt)This example will generate a triangle wave with the MCP4725 DAC.This is an example sketch for the Adafruit MCP4725 breakout board----> https://www.adafruit.com/products/???Adafruit invests time and resources providing this open source code,please support Adafruit and open-source hardware by purchasingproducts from Adafruit!*//**************************************************************************/#include <Wire.h>#include <Adafruit_MCP4706.h>Adafruit_MCP4706 dac;void setup(void) {Serial.begin(9600);Serial.println("Hello!");// 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 0x65dac.begin(0x60);Serial.println("Generating a triangle wave");}void loop(void) {uint8_t counter;// Run through the full 12-bit scale for a triangle wavefor (counter = 0; counter < 255; counter++){dac.setVoltage(counter, false);delay(10);}for (counter = 255; counter > 0; counter--){dac.setVoltage(counter, false);delay(10);}}Header and .cpp file of modified library:Code:/**************************************************************************//*!@file Adafruit_MCP4706.h@author K. Townsend (Adafruit Industries)@license BSD (see license.txt)This is a library for the Adafruit MCP4706 breakout board----> https://www.adafruit.com/products/???Adafruit invests time and resources providing this open source code,please support Adafruit and open-source hardware by purchasingproducts from Adafruit!@section HISTORYv1.0 - First release*//**************************************************************************/#if ARDUINO >= 100#include "Arduino.h"#else#include "WProgram.h"#endif#include <Wire.h>#define MCP4726_CMD_WRITEDAC (0x40) // Writes data to the DAC#define MCP4726_CMD_WRITEDACEEPROM (0x60) // Writes data to the DAC and the EEPROM (persisting the assigned value after reset)class Adafruit_MCP4706{public:Adafruit_MCP4706();void begin(uint8_t a);void setVoltage( uint16_t output, bool writeEEPROM );private:uint8_t _i2caddr;};Code:/**************************************************************************//*!@file Adafruit_MCP4706.cpp@author K.Townsend (Adafruit Industries)@license BSD (see license.txt)I2C Driver for Microchip's MCP4706 I2C DACThis is a library for the Adafruit MCP4706 breakout----> https://www.adafruit.com/products/???Adafruit invests time and resources providing this open source code,please support Adafruit and open-source hardware by purchasingproducts from Adafruit!@section HISTORYv1.0 - First release*//**************************************************************************/#if ARDUINO >= 100#include "Arduino.h"#else#include "WProgram.h"#endif#include <Wire.h>#include "Adafruit_MCP4706.h"/**************************************************************************//*!@brief Instantiates a new MCP4706 class*//**************************************************************************/Adafruit_MCP4706::Adafruit_MCP4706() {}/**************************************************************************//*!@brief Setups the HW*//**************************************************************************/void Adafruit_MCP4706::begin(uint8_t addr) {_i2caddr = addr;Wire.begin();}/**************************************************************************//*!@brief Sets the output voltage to a fraction of source vref. (Valuecan be 0..255)@param outputThe 12-bit value representing the relationship betweenthe DAC's input voltage and its output voltage.@param writeEEPROMIf this value is true, 'output' will also be writtento the MCP4706's internal non-volatile memory, meaningthat the DAC will retain the current voltage outputafter power-down or reset.*//**************************************************************************/void Adafruit_MCP4706::setVoltage( uint16_t output, bool writeEEPROM ){uint8_t twbrback = TWBR;TWBR = 12; // 400 khzWire.beginTransmission(_i2caddr);if (writeEEPROM){Wire.write(MCP4726_CMD_WRITEDACEEPROM);}else{Wire.write(MCP4726_CMD_WRITEDAC);}// Wire.write(output); // Upper data bits (D11.D10.D9.D8.D7.D6.D5.D4)Wire.write(output ); // Upper data bits (D11.D10.D9.D8.D7.D6.D5.D4)Wire.write((output)); // Lower data bits (D3.D2.D1.D0.x.x.x.x)// Wire.write((output % 16) << 4); // Lower data bits (D3.D2.D1.D0.x.x.x.x)Wire.endTransmission();TWBR = twbrback;}the second Wire.write((output)); has to be there otherwise, nothing happens, but no bit shifting needed at all.I am not sure if I have a fundamental error in my circuit or in the code. I'd expect the output to still be linear and not increase/decrease exponentially even if the input is a bit slower.BottomTopTop with plastic for stabilityOscilloscope screen, no delayDelay 1 msDelay 10 msThank you very much for any ideas/hints you might have.

author: ve7xen
author_rank: Super Contributor
timestamp: July 24, 2013, 04:43:10 am
content: I'm surprised it does anything.I don't speak Arduino, but it looks like you're not following the datasheet with regard to the DAC register configuration. Per page 50, your written byte should be0 0 PD1 PD0 B11..B0While you are just writing your counter to both upper and lower bytes of the register (I assume Wire is I2C and write is 1 byte wide). The low bytes are DAC data, so I guess they are producing your output when you see it, but the high bytes include PD1 and PD2 which turn the DAC on and off. The slower you run it, the more chance the latent charge has to bleed off through the 1k, 100k or 500k resistor you are putting on it.What you need to do is properly construct the control word and send it. Since PD1 and PD0 will always have the same value (DAC on, which is 00), and your DAC is only 8 bits and expects the data to be right-aligned, you should be able to just send the top byte as 0 instead of output.

author: PioB
author_rank: Regular Contributor
timestamp: July 24, 2013, 08:35:08 am
content: Hi ve7xen,thank you very much for your help.The code should now work as intended.At first glance, however it looked the same on the oscilloscope for 1 and 10 ms delay.However, when I switched the probe to 10x for the 1 ms delay, that curve now also looks ok 5V p-p (with only a slight curvature).The 10 ms-delay curve still looks the same as before. Even attaching the probe directly to Vout (without going through the breadboard and a wire) to eliminate any capacitive influence doesn't help; the curve looks as bad as before.(Probe connected to output, to output and 330 Ohm resistor, 10 kOhm, 10 MOhm resistor to ground all yield the same. Also using another probe on the second channel of the oscilloscope gives the same result, so I can exclude a malfunction there )So I am not sure if my measuring influences the circuit and how I could remedy that.Thank you very much.Here the updated code and library:code:Code:/**************************************************************************//*!@file trianglewave.pde@author Adafruit Industries@license BSD (see license.txt)This example will generate a triangle wave with the MCP4706 DAC.This is an example sketch for the Adafruit MCP4725 breakout board----> https://www.adafruit.com/products/???Adafruit invests time and resources providing this open source code,please support Adafruit and open-source hardware by purchasingproducts from Adafruit!*//**************************************************************************/#include <Wire.h>#include <Adafruit_MCP4706.h>Adafruit_MCP4706 dac;void setup(void) {Serial.begin(9600);Serial.println("Hello!");// For MCP4706 the address is 0x60 (different types, verify)dac.begin(0x60);Serial.println("Generating a triangle wave");}void loop(void) {uint8_t counter;// Run through the full 8-bit scale for a triangle wavefor (counter = 0; counter < 255; counter++){dac.setVoltage(counter);delay(10);}for (counter = 255; counter > 0; counter--){dac.setVoltage(counter);delay(10);}}Library cppCode:/**************************************************************************//*!@file Adafruit_MCP4706.cpp@author K.Townsend (Adafruit Industries)@license BSD (see license.txt)I2C Driver for Microchip's MCP4706 I2C DACThis is a library for the Adafruit MCP4706 breakout----> https://www.adafruit.com/products/???Adafruit invests time and resources providing this open source code,please support Adafruit and open-source hardware by purchasingproducts from Adafruit!@section HISTORYv1.0 - First release*//**************************************************************************/#if ARDUINO >= 100#include "Arduino.h"#else#include "WProgram.h"#endif#include <Wire.h>#include "Adafruit_MCP4706.h"/**************************************************************************//*!@brief Instantiates a new MCP4706 class*//**************************************************************************/Adafruit_MCP4706::Adafruit_MCP4706() {}/**************************************************************************//*!@brief Setups the HW*//**************************************************************************/void Adafruit_MCP4706::begin(uint8_t addr) {_i2caddr = addr;Wire.begin();}/**************************************************************************//*!@brief Sets the output voltage to a fraction of source vref. (Valuecan be 0..255)@param outputThe 8-bit value representing the relationship betweenthe DAC's input voltage and its output voltage.*//**************************************************************************/void Adafruit_MCP4706::setVoltage( uint8_t output){uint8_t twbrback = TWBR;TWBR = 12; // 400 khzWire.beginTransmission(_i2caddr);Wire.write(B00000000); // Cx:Command bits, PDx:Power Down bits (0 0 for normal operation), X:Don't care (C2.C1.PD1.PD0.X.X.X.X)Wire.write(output); // Data bits (D7.D6.D5.D4.D3.D2.D1.D0)Wire.endTransmission();TWBR = twbrback;}Library .hCode:/**************************************************************************//*!@file Adafruit_MCP4706.h@author K. Townsend (Adafruit Industries)@license BSD (see license.txt)This is a library for the Adafruit MCP4706 breakout board----> https://www.adafruit.com/products/???Adafruit invests time and resources providing this open source code,please support Adafruit and open-source hardware by purchasingproducts from Adafruit!@section HISTORYv1.0 - First release*//**************************************************************************/#if ARDUINO >= 100#include "Arduino.h"#else#include "WProgram.h"#endif#include <Wire.h>class Adafruit_MCP4706{public:Adafruit_MCP4706();void begin(uint8_t a);void setVoltage( uint8_t output);private:uint8_t _i2caddr;};

author: ve7xen
author_rank: Super Contributor
timestamp: July 24, 2013, 09:10:51 am
content: Didn't catch that you were trying to run the I2C at 400khz. Slow it way down when troubleshooting. If you're only using 10k pull-ups these are probably too weak for that speed anyway(1.5-2.2k is usually recommended even at 100KHz), and strictly speaking the spec requires slew rate limiting at this clock rate.Code looks reasonable, but as I said, I don't speak Arduino and I'm just glancing at it. If you still don't have any success I'd get a capture of a few consecutive bus transfers and decode it manually to make sure the data on the wire looks right per the DAC datasheet. It's not clear to me what could be going on with the output, but I'd start with verifying that you're controlling it properly.

author: PioB
author_rank: Regular Contributor
timestamp: July 24, 2013, 11:54:11 am
content: Thank you very much for your answer.I shall try tonight to slow the clock down and put smaller pullup resistors on SDA and SCL.As I don't have a logic analyzer, I can't directly test/snoop bus traffic. I'll try to issue a read command (as per data sheet p55) and see if everything got written correctly in the serial monitor.I think it should read back correctly as I CAN get the triangle wave out with 5Vp_p, albeit only without any delays in the code.I'll report back tonight if I get around to working on it.

author: ve7xen
author_rank: Super Contributor
timestamp: July 24, 2013, 04:00:33 pm
content: Quote from: PioB on July 24, 2013, 11:54:11 amLooks like you've got a DSO? You only need two channels to monitor the bus.QuoteYeah the code looks right, but the output behaviour is extremely suspicious. It looks to me like the DAC is off most of the time and the circuit is behaving like a sample and hold with too high a load or something. Even the no delay waveform looks to be more nonlinear than I'd expect. Which is weird, because I'm not sure what could be causing it.

author: PioB
author_rank: Regular Contributor
timestamp: July 24, 2013, 08:52:09 pm
content: Quote from: ve7xen on July 24, 2013, 04:00:33 pmThank you very much for your continued help. I checked and replacing the 10 k Pullup resistors with 1.5k ones and lowering the speed to 100 kHz lead to much nicer waveforms of the I2C signal, but lead to no change for the better on the output.Code:// TWBR = 12; // 400 khzTWBR = 72; // 100 khzQuoteUnfortunately, the behavior on the output side did not change. Other than the oscilloscope (and the different resistors to Ground (which don't change the behavior) I have no load connected.( I also tried writing to the volatile memory, cf. p 51 and changing the Vref bits 00:Vdd unbuff, 10:Vref, Unbuff, 11 VRef, Buff, and adding the necessary trailing byte of 0s as there is some mention of high/low impedance on p36:Wire.write(B01011000);Wire.write(out);Wire.write(0); however nothing changed.Zoomed in on SDA (yellow) and SCL (red), signal should be good enough; 1.5 kOhm Pullups.Entire I2C SequenceHowever, even without delay, the output now looks no longer linear and Vp_p is now only about 4 V. (frequency ~1/4 of before) SCL: red, Output: yellowWith delay

author: ve7xen
author_rank: Super Contributor
timestamp: July 25, 2013, 03:37:43 am
content: I2C data looks okay at a glance. Not sure what else to suggest, sorry.

author: Strada916
author_rank: Frequent Contributor
timestamp: July 26, 2013, 12:18:26 am
content: If you examine the wave form in the first photo. You'll notice that they are in a exponent form already. By adding your 10ms delay you are expanding the wave. Does that make sense?

author: free_electron
author_rank: Super Contributor
timestamp: July 26, 2013, 01:38:44 am
content: you re not intialising the dac ! the eeprom is empty off factory. upon powerup it loads vref set to external ... you have never written the eeprom so you need a full dataframe to pull ut in the correct mode.try this :Wire.beginTransmission(_i2caddr);Wire.write(B01000001); // 010:Command bits, 00 : vref internal 00:Power Down bits , 1 gain bitWire.write(output); // Data bits (D7.D6.D5.D4.D3.D2.D1.D0)Wire.write(0); // Data bits all zero for 4706Wire.endTransmission();

author: PioB
author_rank: Regular Contributor
timestamp: July 26, 2013, 10:37:02 am
content: Hi ve7xen, Strada916 and free_electron.Thank you all very much for your hints and help.I think the I2c side of things should be working correctly, however I still do not observe the expected behavior on the oscilloscope.With the following code:Code:/**************************************************************************//*!@file trianglewave.pde@author Adafruit Industries@license BSD (see license.txt)This example will generate a triangle wave with the MCP4706 DAC.This is an example sketch for the Adafruit MCP4725 breakout board----> https://www.adafruit.com/products/???Adafruit invests time and resources providing this open source code,please support Adafruit and open-source hardware by purchasingproducts from Adafruit!*//**************************************************************************/#include <Wire.h>#include <Adafruit_MCP4706.h>Adafruit_MCP4706 dac;void setup(void) {Serial.begin(9600);Serial.println("Hello!");// For MCP4706 the address is 0x60 (different types, verify)dac.begin(0x60);Serial.println("Generating a triangle wave");}void loop(void) {uint8_t counter;// Run through the full 8-bit scale for a triangle waveSerial.println("up");for (counter = 0; counter < 255; counter++){dac.setVoltage(counter);Wire.requestFrom(0x60, 8); // request 6 bytes from slave device #2while(Wire.available()) // slave may send less than requested{byte c = Wire.read(); // receive a byte as characterSerial.print(c, BIN); // print the characterSerial.print(" ");}Serial.println();// delay(10);}Serial.println("down");for (counter = 255; counter > 0; counter--){dac.setVoltage(counter);Wire.requestFrom(0x60, 8); // request 6 bytes from slave device #2while(Wire.available()) // slave may send less than requested{byte c = Wire.read(); // receive a byte as characterSerial.print(c, BIN); // print the characterSerial.print(" ");}Serial.println();//delay(10);}}I get this output:Code:up11000001 0 11100000 0 11000001 0 11100000 011000001 1 11100000 0 11000001 1 11100000 011000001 10 11100000 0 11000001 10 11100000 011000001 11 11100000 0 11000001 11 11100000 011000001 100 11100000 0 11000001 100 11100000 011000001 101 11100000 0 11000001 101 11100000 011000001 110 11100000 0 11000001 110 11100000 011000001 111 11100000 0 11000001 111 11100000 011000001 1000 11100000 0 11000001 1000 11100000 011000001 1001 11100000 0 11000001 1001 11100000 011000001 1010 11100000 0 11000001 1010 11100000 011000001 1011 11100000 0 11000001 1011 11100000 011000001 1100 11100000 0 11000001 1100 11100000 011000001 1101 11100000 0 11000001 1101 11100000 011000001 1110 11100000 0 11000001 1110 11100000 011000001 1111 11100000 0 11000001 1111 11100000 011000001 10000 11100000 0 11000001 10000 11100000 011000001 10001 11100000 0 11000001 10001 11100000 011000001 10010 11100000 0 11000001 10010 11100000 011000001 10011 11100000 0 11000001 10011 11100000 011000001 10100 11100000 0 11000001 10100 11100000 011000001 10101 11100000 0 11000001 10101 11100000 011000001 10110 11100000 0 11000001 10110 11100000 011000001 10111 11100000 0 11000001 10111 11100000 011000001 11000 11100000 0 11000001 11000 11100000 011000001 11001 11100000 0 11000001 11001 11100000 011000001 11010 11100000 0 11000001 11010 11100000 011000001 11011 11100000 0 11000001 11011 11100000 011000001 11100 11100000 0 11000001 11100 11100000 011000001 11101 11100000 0 11000001 11101 11100000 011000001 11110 11100000 0 11000001 11110 11100000 011000001 11111 11100000 0 11000001 11111 11100000 011000001 100000 11100000 0 11000001 100000 11100000 0...down11000001 11111111 11100000 0 11000001 11111111 11100000 011000001 11111110 11100000 0 11000001 11111110 11100000 011000001 11111101 11100000 0 11000001 11111101 11100000 011000001 11111100 11100000 0 11000001 11111100 11100000 0...(Arduino omits leading zeroes.)I am a bit confused, as the address byte seems to be missing so the data byte is the second one.With the 100 kHz bus and all the communication, the waveform looks messed up even without any delay and Vpp is reduced to 4 volts.Vpp~4 V, forgot to switch oscilloscope display to x10 for probe)To get an idea as to what happens on a longer timeframe, I looped over 8 steps (*31 -> output to DAC).Code:/**************************************************************************//*!@file trianglewave.pde@author Adafruit Industries@license BSD (see license.txt)This example will generate a triangle wave with the MCP4706 DAC.This is an example sketch for the Adafruit MCP4725 breakout board----> https://www.adafruit.com/products/???Adafruit invests time and resources providing this open source code,please support Adafruit and open-source hardware by purchasingproducts from Adafruit!*//**************************************************************************/#include <Wire.h>#include <Adafruit_MCP4706.h>Adafruit_MCP4706 dac;void setup(void) {Serial.begin(9600);Serial.println("Hello!");// For MCP4706 the address is 0x60 (different types, verify)dac.begin(0x60);Serial.println("Generating a triangle wave");}void loop(void) {uint8_t counter;// Run through the full 8-bit scale for a triangle waveSerial.println("up");for (counter = 0; counter < 8; counter++){dac.setVoltage(counter*31);Wire.requestFrom(0x60, 8); // request 6 bytes from slave device #2while(Wire.available()) // slave may send less than requested{byte c = Wire.read(); // receive a byte as characterSerial.print(c, BIN); // print the characterSerial.print(" ");}Serial.println();delay(500);}Serial.println("down");for (counter = 8; counter > 0; counter--){dac.setVoltage(counter*31);Wire.requestFrom(0x60, 8); // request 6 bytes from slave device #2while(Wire.available()) // slave may send less than requested{byte c = Wire.read(); // receive a byte as characterSerial.print(c, BIN); // print the characterSerial.print(" ");}Serial.println();delay(500);}}This should lead to nice "stairsteps" and introducing a delay of 500 msec between the updates is that the voltage seems to be "pulled" toward the median (2.5 V) on either half of the curve. This is somewhat evident with the probe on x10 mode:(Vpp reduced to ~2 V)and really evident with the probe on x1 mode:Vpp=1.2 VMight it be that the output stage of the DAC is faulty?

author: amyk
author_rank: Super Contributor
timestamp: July 26, 2013, 10:44:45 am
content: Is your scope set to AC coupling? The curve shape isreminiscent of a capacitor charging and discharging.Why not connect a multimeter to it and watch the voltage.

author: PioB
author_rank: Regular Contributor
timestamp: July 26, 2013, 07:07:32 pm
content: Hi Amyk,thank you very much for your answer.I shall hang my head in shame.That was it. When I switched the input to DC, the waveform looked perfect, as expected.I would like to thank all the contributors and will go and read up on the correct use of the oscilloscope, which I just got recently as a birthday present, without any documentation unfortunately. (If anyone has a manual for a Gould DSO 500, let me know)I am sorry for the time I have wasted, but I have learned a lot, correct I2c use, debugging...Thank you very much!

author: amyk
author_rank: Super Contributor
timestamp: July 27, 2013, 09:27:18 am
content: This video may help:

author: free_electron
author_rank: Super Contributor
timestamp: July 27, 2013, 03:03:58 pm
content: Quote from: amyk on July 26, 2013, 10:44:45 amDamn it. I was just going to say : it looks like you have an interrupted ground lead, or a broken cable in the probe... This thing is coupled capacitively ..