Digital Analog Converter MCP4922

Hi to everyone,

I need to give an output between 0 and 4V with my Arduino DUE.

I have a Digital to Analog Converter MCP4922 but to use it I should change the SPI library because the stuff I was using for the Arduino UNO, on the DUE is not working because The sketch is written for an ATmega based Arduino. On a Due I can't use the direct port manipulation commands of the ATmega platform, so I have to rewrite the sketch to use digitalWrite() instead of the port manipulations and the SPI library instead of the register manipulations in the sketch.

I know that on the DUE there is already a DAC integrated, but the output can be only between 0 and 3.3V, actually they give an output between 0.549V and 2.775V.

I want an output between 0 and 4V. How can I have it?

I was thinking that there are mainly 2 ways:

  1. Add an Operational Amplifier
  2. Change the SPI library to let it work with the DUE.

What do you think is the best way? or what do you suggest to me and why?

Thanks a lot,
Fab.

If you go with SPI look at this thread. SPI is different on the ARM chip
http://forum.arduino.cc//index.php?topic=157203.msg1424519#msg1424519
I know its more about slave mode but plenty of tips for DUE SPI in there.

If you can use the Due 12bit DAC all the better.

Can you not use a pair of resistors to do a voltage divide and then a bit of math on the output to scale it to what you want.

Just noticed this post
http://forum.arduino.cc//index.php?topic=194694.0

Puts a damper on the idea of using the Due DAC.

See page 23-24.
You bring CS low, send out 2 bytes via SPI.transfer(x); bring CS high.
You really need a library for that?
Due does not support SPI.transfer( ) command?

Stephan_Duino:
Just noticed this post
Horrific DAC noise on DUE board - Arduino Due - Arduino Forum

Puts a damper on the idea of using the Due DAC.

Thanks for the reply but that page is not really useful for me because people are searching for a solution to connect the DUE as slave and it also seemed that they didn´t find a solution . By the way my problem is another one.

I want to use a DAC MCP4922 on the Arduino DUE

CrossRoads:
http://ww1.microchip.com/downloads/en/DeviceDoc/22250A.pdf

See page 23-24.
You bring CS low, send out 2 bytes via SPI.transfer(x); bring CS high.
You really need a library for that?
Due does not support SPI.transfer( ) command?

Thanks for the reply ,

I will look into it.

Fab.

Ok. I looked a little bit more into it and what I have to send is the one in the picture attached.

So now I know what message I have to send but, in your opinion can I send it with just an SPI.write(msg) command?

Thanks a lot,
fab.

I think I can do it in this way:

#include <SPI.h>

void setup() {
  // put your setup code here, to run once:
SPI.begin(10);
}

void loop() {
  // put your main code here, to run repeatedly: 
  
  // The entire message to send a 4095 number will be:
  // 0011 | 111111111111
  
  msg= ...
  SPI.transfer(10,msg)
}

What is msg?

The problem now is how to create a message of 2 bytes on the Arduino DUE, because the integers are of 32 bits.

Thanks for your help,
Fab.

I tried with this:

short msg=0011111111111111;
  
  SPI.transfer(10,msg);

and with this:

byte msg1=00111111;
byte msg2=11111111;
SPI.transfer(10,msg1,SPI_CONTINUE);
SPI.transfer(10,msg2);

But nothing is working :frowning: .

Please help

I figured out that the library SPI.h is only for 8 bit data values. So I can´t use the library to address my MCP4922 DAC.

Assumed that this is true what I have to do now is to port the following code, that is working on my Arduino UNO, to the DUE:

/*
  MCP4922 test
  outputs steadily rising voltages through both DACs
  
  Steve Woodward, 2010
  most code borrowed from
  http://mrbook.org/blog/2008/11/22/controlling-a-gakken-sx-150-synth-with-arduino/
  
  connections
  ====================================================
  
  +5v           > 4922 pin 1
  Ard pin 10    > 4922 pin 3   (SS - slave select)
  Ard pin 13    > 4922 pin 4   (SCK - clock)
  Ard pin 11    > 4922 pin 5   (MOSI - data out)
  Ground        > 4922 pin 8   (LDAC)
  +5v           > 4922 pin 11  (voltage ref DAC B)
  Ground        > 4922 pin 12
  +5v           > 4922 pin 13  (voltage ref DAC A)
 
 
  4922 pin 14 DAC A > 1k resistor > synth CV in
  
 */ 
 
// MCP4922 DAC out
#define DATAOUT 11//MOSI
#define DATAIN 12//MISO - not used, but part of builtin SPI
#define SPICLOCK  13//sck
#define SLAVESELECT0 10//ss
 
int i = 0;
 
void setup() {
  SPI_setup();
  Serial.begin(115200);
}
 
void loop() {
 i++;
 Serial.println(i);
 write_note(i);
 if(i>=4096) {
  i=0; 
 }
 delay(8);
}
 
void write_note(int i) {
  write_valueY(i);
  write_valueX(i);
}
 
// **************************************************
// SPI for DAC
 
void SPI_setup(){
 
  byte clr;
  pinMode(DATAOUT, OUTPUT);
  pinMode(SPICLOCK,OUTPUT);
  pinMode(SLAVESELECT0,OUTPUT);
  digitalWrite(SLAVESELECT0,HIGH); //disable device
 
  SPCR = (1<<SPE)|(1<<MSTR) | (0<<SPR1) | (0<<SPR0);
  clr=SPSR;
  clr=SPDR;
  delay(10);
}
 
// write out through DAC A
void write_valueX(int sample)
{
  // splits int sample in to two bytes
  byte dacSPI0 = 0;
  byte dacSPI1 = 0;
  dacSPI0 = (sample >> 8) & 0x00FF; //byte0 = takes bit 15 - 12
  dacSPI0 |= (1 << 7);// A/B: DACa or DACb - Forces 7th bit of x to be 1. all other bits left alone.
  dacSPI0 |= 0x10;
  dacSPI1 = sample & 0x00FF; //byte1 = takes bit 11 - 0
  dacSPI0 |= (1<<5);  // set gain of 1
  digitalWrite(SLAVESELECT0,LOW);
  SPDR = dacSPI0;			  // Start the transmission
  while (!(SPSR & (1<<SPIF)))     // Wait the end of the transmission
  {
  };
 
  SPDR = dacSPI1;
  while (!(SPSR & (1<<SPIF)))     // Wait the end of the transmission
  {
  };
  digitalWrite(SLAVESELECT0,HIGH);
  //delay(2);
}
 
// write out through DAC B
void write_valueY(int sample)
{
  // splits int sample in to two bytes
  byte dacSPI0 = 0;
  byte dacSPI1 = 0;
  dacSPI0 = (sample >> 8) & 0x00FF; //byte0 = takes bit 15 - 12
  dacSPI0 |= 0x10;
  
  dacSPI1 = sample & 0x00FF; //byte1 = takes bit 11 - 0
  dacSPI0 |= (1<<5);  // set gain of 1
  
  digitalWrite(SLAVESELECT0,LOW);
  SPDR = dacSPI0;			  // Start the transmission
  while (!(SPSR & (1<<SPIF)))     // Wait the end of the transmission
  {
  };
 
 
  SPDR = dacSPI1;
  while (!(SPSR & (1<<SPIF)))     // Wait the end of the transmission
  {
  };
  digitalWrite(SLAVESELECT0,HIGH);
  //delay(2);
}

I need some help for that please.

I figured out how to solve my problem. :slight_smile:

The library was useful , what I wrote in my previous post was a bullshit.

Here you can find a small tutorial to understand what I did:
http://schianorobotics.altervista.org/arduino.html

Hope it helps someone. If yes a "thanks" would be really appreciated !!!

Thanks so much for this, really helped me out.

I know how to send data across one channel, still not sure how to switch between channels?

thanks

manewc

CrossRoads:
http://ww1.microchip.com/downloads/en/DeviceDoc/22250A.pdf

See page 23-24.
You bring CS low, send out 2 bytes via SPI.transfer(x); bring CS high.
You really need a library for that?
Due does not support SPI.transfer( ) command?

Won't work on the Due - the CS line is handled in hardware and you must not
drive it directly. See the extensions to SPI for the Due, its all mentioned on the
Due product page I believe. SPI.transfer() brings CS low and takes it high again
automagically on every call.

Note on the Due you are limited to 3 or 4 particular CS pins (4 and 10 and some others). However you can use other pins for CS, but have to use one of the hardware pins as well
since the SPI hardware needs to drive one of them automatically IIRC.