[Solved] Using a DAC (MCP4911) instead of a low-pass filter

CrossRoads:
You are right CS as CS, vs LDAC.

Yes, these 2 lines use the SPI hardware to shift out two bytes:

SPI.transfer ( highByte( your_data ) );
SPI.transfer ( lowByte( your_data ) );

So in total I have 4 pins - CS, LDAC, SDI (MOSI) and SCK. I found and edited a library for a similar DAC to mine a couple of days ago, which uses SPI.transfer, so I think it could work. Originally you only had to specify the CS and LDAC pins, but because I was unsure about the remaining pins, I edited it so I could choose the MOSI and SCK pins.

I will be using a Mega with my Ethernet shield, so there will be free connections for hardware SPI. Given the relatively low rate that I will be sending data to the DAC at, would it be okay to just use the same MOSI/SCK pins?

Thanks :smiley:

.cpp:

// ----------------------------------------------------------------------------
// MCP4911.cpp
//
// Provides an SPI based interface to the MCP4911 single voltage output digital 
// to analog converter.
//
// Author: Will Dickson, IO Rodeo Inc., edited by plusminus_
// ----------------------------------------------------------------------------

#include "Arduino.h"
#include "SPI.h"
#include "MCP4911.h"

#define ACTIVE      0b0001000000000000
#define SHUTDOWN    0b0000000000000000
#define GAIN_1X     0b0010000000000000
#define DAC_A_WRITE 0b0000000000000000

MCP4911::MCP4911() {
}

// ----------------------------------------------------------------------------
// MCP4911::MCP4911
//
// Constructor
// ----------------------------------------------------------------------------
MCP4911::MCP4911(int csPin, int ldacPin, int clkPin, int sdiPin) {
    // Configure chip select and latch pins
	// I think MISO can be ignored
    cs = csPin;
    ldac = ldacPin;
	clk = clkPin;
	sdi = sdiPin;
}

// ----------------------------------------------------------------------------
// MCP4911::MCP4911
//
// initialization function
// ----------------------------------------------------------------------------
void MCP4911::begin() {
	pinMode(cs,OUTPUT);
    pinMode(ldac,OUTPUT);
	pinMode(clk,OUTPUT);
	pinMode(sdi,OUTPUT);
	
    digitalWrite(cs,HIGH);
    digitalWrite(ldac,HIGH);
	digitalWrite(clk,LOW);
	digitalWrite(sdi,LOW);
	
    // Set to default configuration
    setGain1X_A();
}

// ---------------------------------------------------------------------------
// MCP4911::getCmdWrd
//
// Gets command word for writing value to given channel
// ---------------------------------------------------------------------------
int MCP4911::getCmdWrd(int dac, int value) {
    int cmdWrd = 0;
    switch (dac) {
        case MCP4911_DAC_A:
            cmdWrd = DAC_A_WRITE;
            break;
        default:
            return 0;
    } 
    cmdWrd |= gain[dac];
    cmdWrd |= ACTIVE;
    cmdWrd |= (0b0000111111111111 & value);
    return cmdWrd;
}

// ----------------------------------------------------------------------------
// MCP4911::setValue
//
// Set the output value of the given dac channel
// ----------------------------------------------------------------------------
void MCP4911::setValue(int dac, int value) {
    int cmdWrd;
    uint8_t byte0;
    uint8_t byte1;
    
    // Enable SPI communications 
    digitalWrite(cs,LOW);
    // Send command word
    cmdWrd = getCmdWrd(dac,value);
    byte0 = cmdWrd >> 8;
    byte1 = cmdWrd & 0b0000000011111111;
    SPI.transfer(byte0);
    SPI.transfer(byte1);
    // Disable SPI communications
    digitalWrite(cs,HIGH);
    // Latch value
    digitalWrite(ldac,LOW);
    digitalWrite(ldac,HIGH);
}

// ---------------------------------------------------------------------------
// MCP4911::setValue_A
//
// Set the output value of dac A to the given value
// ---------------------------------------------------------------------------
void MCP4911::setValue_A(int value) {
    setValue(MCP4911_DAC_A, value);
}

// ---------------------------------------------------------------------------
// MCP4911::setGain1X
//
// Set the gain of the given channel to 1X
// ----------------------------------------------------------------------------
void MCP4911::setGain1X(int dac) {
    if (dac == MCP4911_DAC_A) {
        gain[dac] = GAIN_1X;
    }
}

// ----------------------------------------------------------------------------
// MCP4911::setGain1X_A
//
// Set the gain of dac A to 1X
// ----------------------------------------------------------------------------
void MCP4911::setGain1X_A() {
    setGain1X(MCP4911_DAC_A);
}

// ----------------------------------------------------------------------------
// MCP4911::off
//
// Turn off the given dac
// ----------------------------------------------------------------------------
void MCP4911::off(int dac) {
    int cmdWrd = 0;
    uint8_t byte0;
    uint8_t byte1;
    
    // Create shutdown cmd word. 
    switch (dac) {
        case MCP4911_DAC_A:
            cmdWrd = DAC_A_WRITE;
            break;
        default:
            return;
    } 
    cmdWrd |= SHUTDOWN;
    // Enable SPI communications 
    digitalWrite(cs,LOW);
    // Send command word
    byte0 = cmdWrd >> 8;
    byte1 = cmdWrd & 0b0000000011111111;
    SPI.transfer(byte0);
    SPI.transfer(byte1);
    // Disable SPI communications
    digitalWrite(cs,HIGH);
}

// ----------------------------------------------------------------------------
// MCP4911::off_A
//
// Turn off dac A
// ----------------------------------------------------------------------------
void MCP4911::off_A() {
    off(MCP4911_DAC_A);
}

.h:

// ----------------------------------------------------------------------------
// mcp4911.h
//
// Provides an SPI based interface to the MCP4911 single voltage output digital 
// to analog to converter.
//
// Author: Will Dickson, IO Rodeo Inc., edited by plusminus_
// ----------------------------------------------------------------------------
#ifndef _MCP4911_H_
#define _MCP4911_H_

#define MCP4911_NUMCHAN 1

enum MCP4911_DAC_CHAN {MCP4911_DAC_A};

class MCP4911 {
private:
    int cs;
    int ldac;
	int clkPin;
	int sdiPin;
    int gain[MCP4911_NUMCHAN];
    int getCmdWrd(int dac, int value);
public:
    MCP4911();
    MCP4911(int csPin, int ldacPin, int clkPin, int sdiPin);
    void begin();
    void setValue(int dac, int value);
    void setValue_A(int value);
    void setGain1X(int dac);
    void setGain1X_A();
    void off_A();
    void off(int dac);
};
#endif

example sketch:

#include <SPI.h>
#include "MCP4911.h"

#define AOUT_CS 5
#define AOUT_LDAC 6
#define AOUT_CLK 7
#define AOUT_SDI 8
// For example, these will change

MCP4911 analogOut = MCP4911(AOUT_CS, AOUT_LDAC, AOUT_CLK, AOUT_SDI);

void setup() {
    // Setup serial and SPI communications
    Serial.begin(115200);
    analogOut.begin();
    // SPI.setDataMode(SPI_MODE0); - default
    // SPI.setBitOrder(MSBFIRST); - default
    // SPI.setClockDivider(SPI_CLOCK_DIV8); - half default speed
    SPI.begin();

    // Configure analog outputs
    analogOut.setGain1X_A();
}


void loop() {
    static int cnt=0;
    analogOut.setValue_A(cnt);
    cnt += 20;
    if (cnt > 1023) {
        cnt = 0;
    }
}