Quad 12-bit DAC MCP4728 libary

I write a library for mcp4728 because I used it High Power RGB LED Shield that I will post later.

It is basically 4 channel version of mcp4725 ( you can find the break out board from sparkfun). If you want to have mcp4725 library, let me know. I will write a library.

Basic spec of mcp4728.

  • Quad 12-bit voltage output Digital-to-Analog Convertor (DAC)

  • I2C communication with programmable address.

  • Internal or External voltage reference

  • EEPROM for default values.


I did document the source code. Example files have description for functions.

Here is a part of example file. full example pde code http://code.google.com/p/neuroelec/source/browse/trunk/mcp4728/examples/BasicUse/BasicUse.pde

    #include <Wire.h>
    #include “mcp4728.h”

    void setup()
    Serial.begin(9600);  // initialize serial interface for print()
    dac.begin();  // initialize i2c interface

    void loop()
    Basic analog writing to DAC

    dac.analogWrite(500,500,500,500); // write to input register of DAC four channel (channel 0-3) together. Value 0-4095
    dac.analogWrite(0,700); // write to input register of a DAC. Channel 0-3, Value 0-4095
    int value = dac.getValue(0); // get current input register value of channel 0
    Serial.print(“input register value of channel 0 = “);
    Serial.println(value, DEC); // serial print of value

    dac.voutWrite(1.8, 1.8, 1.8, 1.8) // write to input register of DAC. Value (V < VDD)
    dac.voutWrite(2, 1.6) // write to input register of DAC. Channel 0 – 3, Value (V < VDD)
    float vout = dac.getVout(1); // get current voltage out of channel 1
    Serial.print(“Voltage out of channel 1 = “);
    Serial.println(vout, DEC); // serial print of value

Source code




Some remarks :

Why not using milliVolts iso Volts. Then you can use integer (long) math instead of float math. Makes the lib a bit faster and the footprint smaller. (but don't know if the type (unsigned) long is long enough with all intermediate results esp in getVout()

Think the print command should not be built into the class because it only prints to Serial. It could be provided as an example function using your class.

The default constructor does not set the _deviceID. you can easily merge the two constructors:

mcp4728::mcp4728(uint8_t deviceID = 0x00)
  _deviceID = deviceID;
  _dev_address = (BASE_ADDR | _deviceID);
  _vdd = defaultVDD;

There are three small functions [reset wake update] that use almost the small code. Could be replaced by a call to a simple Command that takes the byte as param e.g.

uint8_t mcp4728::reset() 
  • AnalogWrite() copies the values twice, direct copy in the _values array is faster and use less memory.
uint8_t mcp4728::analogWrite(uint16_t value1, uint16_t value2, uint16_t value3, uint16_t value4) 
  _values[0] = value1;
  _values[1] = value2;
  _values[2] = value3;
  _values[3] = value4;
  return fastWrite();
  • eepromWrite same remark:
uint8_t mcp4728::eepromWrite(uint16_t value1, uint16_t value2, uint16_t value3, uint16_t value4)
  _valuesEp[0] = _values[0] = value1; 
  _valuesEp[1] = _values[1] = value2; 
  _valuesEp[2] = _values[2] = value3; 
  _valuesEp[3] = _values[3] = value4; 
  return seqWrite();
  • setVref(), setgain(), setPowerDown() , .... same remark;

You might consider to creat a struct Channel and have an array[4] of them. The struct would contain intVref, gain, value etc.

sofar my 2 cents, Rob


Thanks for reviewing the library. I adopt most of your suggestions. I will update the library soon.

You're welcome, if a new version is available let us know.

Some additional remarks (new day, new insights :)

  • As the mcp4728 is essentially a 4 channel version of the mcp4725, maybe you could rewrite the lib in such a way that it supports both? dont know if the diffs are big.

  • optionally rename mcp4278.getValue(channel) ==> mcp4278.analogRead(channel) to be more in line with Arduino namegiving?

  • use #defines for typical values e.g. Voltage Reference Settings and Powerdown like

    define VREF_INTERNAL 1

    define VREF_EXTERNAL 0

    define GAIN_X1 0

    define GAIN_X2 1

    define GAIN_IGNORE 0 ????





define RESET 0B00000110


  • maybe it is an idea to merge the setting of vref and gain into one call as they are coupled in normal use... mcp.setVref(channel, VREF_INTERNAL, GAIN_X2);

  • is there room for an eepromRead() to reset all or a channel ?

Finally, please take some time to write a playground article how the lib can be used when it is stable.

I just updated the library based on robtillaart suggestions. thanks again. You can find the changes here http://code.google.com/p/neuroelec/source/detail?r=6

  • serial output for debug moved to sample pde.
  • use 3 bytes integer for calculation instead of float
  • direct copy of array instead of for loop
  • cosmetic #defines
  • and some minor fixes and cleanup

any suggestion for the library is welcome.

@robtillaart for mcp4725 library, it use slightly different command sequence. I think it is better to be separate. Once I get the chip that I ordered, I will write the library. It should be trivial.

I think analogRead() naming is confusing, since it is not reading anything from pins. it is just returning current internal values. Are you suggesting factory reset like function with eepromRead() ?

My application of this chip is controlling led driver to set maximum current as I posted http://arduino.cc/forum/index.php/topic,51887.0.html That application would be too chip specific. If I have time later, I will try to use this chip as wave file player and write playground article.

Are you suggesting factory reset like function with eepromRead() ?

100% right, makes sense or?


wrt new lib, looks good, one remark

static const uint8_t BASE_ADDR = 0x60; => could be a #define too?


I wrote a little background on how I implement programmable I2C address feature of mcp4728 using software I2C library. If anyone interest, check it out. http://neuroelec.com/2011/02/soft-i2c-and-programmable-i2c-address/

@robtillaart I will update library as you suggested including eepromReset(). thanks


Can you tell me that if I want to use the internal reference voltage instead of VDD, what would I do in set up to set the gain to 2 and the ref voltage to the internal ref voltage.

Thank you.

Never mind. I found a routine you had built - setVref().

Great job on this library.

@neurostar You might add a link in the library to this thread for discussions. And maybe a small article on the playground how to use ?

Updated to Version 1.2

Download : http://neuroelec.googlecode.com/files/mcp4728_library-v1.2.zip



  • eepromReset() : reset all setting to factory default
  • All library functions are fully documented.
  • add keyword.txt
  • add reference to this thread for discussion and feedback

@robtillaart I think the example code has enough details on how to use the library. setVref() was explained in the example code. I added a link to this thread for discussion.


Just curious if you are planning to upgrade this library to Arduino 1.0 ?

Regards James

saw there is an 1.3 version available although not 1.0 compliant yet - http://code.google.com/p/neuroelec/downloads/detail?name=mcp4728_library-v1.3.zip

Is it possible to have a 0-10v output with this IC?

datasheet - http://ww1.microchip.com/downloads/en/DeviceDoc/22187a.pdf -

page 1:
Output Voltage Range:
- Using Internal VREF (2.048V):
0.000V to 2.048V with Gain Setting = 1
0.000V to 4.096V with Gain Setting = 2
- Using External VREF (VDD): 0.000V to VDD

Page 3:
Absolute Maximum Ratings†

So 10V is not possible, you need to add some heavy duty transistor or so… (no electrical guru)

thank you for the answer

Data sheets are very usefull, and 99% of the time correct ...

WanaGo: Hi,

Just curious if you are planning to upgrade this library to Arduino 1.0 ?

Regards James

Arduino 1.0 compatible library is available. http://code.google.com/p/neuroelec/downloads/detail?name=mcp4728_library_v1.3_for_Arduino_1.0.zip

I'm newbie and i need help..

i use ide 1.04..but this library is not compatible. how to match library with ide 1.04

please help me. thx..