SPI_DAC

This is a pretty simple project that is not an original.  This project reads in an analog value and then uses a Microchip MCP4921 Digital to Analog Converter ($2.36 per chip) to regenerate the signal.  The challenge for some people is understanding how to use the SPI bus on the Arduino.  This project contains everything you need in one file.  No downloading extra libraries or layers of abstraction to further confuse you.  The bonus about this chip is its high temperature operating range, -40°C to +125°C.  That coupled with the Atmel ATMEGA328P-PN operating temperature of  -40°C to +105°C leaves you with a device that can do things in more extreme conditions if you build your own board.

ADC2DACcloseup

The parts you need…

    •  MCP4921
    • generic potentiometer ( 1kohm or greater)
    • some wire
    • breadboard / protoboard

How to wire it up…

MCP4921 pin —> Arduino UNO
1 Vdd               —> 5 volt power supply – can come from Arduino
2 CS                 —> Arduino pin 10
3 SCK              —> Arduino pin 13
4 SDI               —> Arduino pin 11
5 LDAC           —> Ground
6 Vrefa            —> 5 volt power supply – for reference use only
7 AVss             —> Ground
8 Vouta           —> Analog voltage output signal (DAC)

For the potentiometer, hook up one lead to ground, the other to the Arduino 5V, and the variable output to A0 on the Arduino.

Code without any comments…

  #include <SPI>
  const int chipSelectPin = 10;
  const int analogInPin = A0;
  int sensorValue = 0;
  int outputValue = 0;
  byte outputValueByte0 = 0;
  byte outputValueByte1 = 0;

  void setup()
{
     SPI.begin();
     SPI.setBitOrder(MSBFIRST);
     SPI.setClockDivider(SPI_CLOCK_DIV4);
     pinMode(chipSelectPin, OUTPUT);
     digitalWrite(chipSelectPin, HIGH);
  }

  void loop()
  {
     sensorValue = analogRead(analogInPin);
     outputValue = map(sensorValue, 0, 1023, 0, 4095);
     outputValueByte0 = byte(outputValue);
     outputValue = outputValue >> 8;
     outputValueByte1 = byte(outputValue | 0b00110000);

     digitalWrite(chipSelectPin, LOW);
     SPI.transfer(outputValueByte1);
     SPI.transfer(outputValueByte0);
     digitalWrite(chipSelectPin, HIGH);
  }

The result is a device that takes in an analog signal ( max 6.5V limited by Vdd, reference can not exceed Vdd ) and sends out the same analog signal at about a rate of 6kHz.  From here you can take in an analog signal and regenerate it in any way you want with a few more if/else statements and more maps.  The Arduino reference page and knowing how to count in binary are useful for understanding how this is working.

How does it work…

Start by downloading the code here with all the comments to explain each line.  As I’m writing this up, this is only the second SPI device that I’ve written any code to work with.   If you don’t have an oscilloscope, I would really recommend trying to get one somehow if you are doing anything with SPI that isn’t a copy and paste project.  Check out my suggestions here.  If you did have an oscilliscope you would see something similar to what is described in Figure 5-1 from the MCP4921 datasheet:

SPI_MCP4921

In my case, with the clock rate slowed down as slow as it could be( SPI.setClockDivider(SPI_CLOCK_DIV128);), and my scope as fast as it can go (50uSec/division), this is what I see (with a little photo-chopping to include a 3rd signal on a 2 channel scope):

ADC2DACTimeDelay_WithDigitalWrite128

Left to right there are 16 bits of data sent and this is how it happens.  The first 4 do some configuring and stay the same.  The last 12 are the data.  The SPI transfer command can only send 8 bits at a time, so you need two lines in the code to send all 16.  When its done right, you will be able to measure the analog voltage you are looking for from pin 8 of the MCP4921. On my oscilloscope the output looks rough, but that is only because of the low resolution of my USB scope.  With a high speed scope it would look much cleaner.

Leave a Reply