Go Down

Topic: serial port control of SPI DAC (Read 2745 times) previous topic - next topic


I thought it was finally time for me to put something back onto the bulletin boards, as I've benefitted again and again from the material that is here.  
Recently I've been experimenting with interfacing a serial peripheral interface (SPI) protocol DAC to the arduino.  I found some useful stuff here regarding interfacing Microchip MCP 4921 DACS, which I basically tweaked slightly from its original author, and I added a very rudimentary serial port interface to it which allows the user to set DAC output values via hyperterminal or any serial terminal program.

It's a good introduction to SPI interfacing, as well as a handy user interface to control the DAC output, rather than having the DAC just follow some pre-programmed output cycle.

I've tried to comment the code sufficiently to make the key steps as clear as possible and geared to those who aren't card carrying programmers (as I most certainly am not!)

In my searching for example code, I looked for two things in particular:
1) SPI DAC interfacing examples, and 2) serial input to number conversion examples.

For the SPI examples, I found a very nice example of code which interfaced two of these very same DAC's to an x-y plotter, and my code is basically derived from that.  It has been scaled back to a single DAC control, commented up a bit and one slight change was made to the write subroutine to optimize the internal gain coeficient of the DAC to maximize the resolution of the output signal.

For the serial input interface, I found quite a large variety of examples (many of which didn't work so well either), which were all based on an ascii to integer function "atoi()" which supposedly makes the task of building a serial interface to control the DAC a breeze.  After spending a couple hours twisting my brain in a knot trying to understand the finer nuances of pointers and null terminated string arrays, and another couple hours banging my head against a wall with less than 100% bug free example code, I basically threw my arms up in disgust and proceeded to write a VERY simple serial interface which looks for setpoints to be entered in from a terminal, converts them to a number and spits it out to the DAC on the SPI interface.  Like I said... I'm not a programmer.  The hope is that because of that, this may appeal or at least be somewhat understandable to newer folks to the Arduino.

Enough said... here's the code below:

Code: [Select]
/* This code reads serial data and converts to long integer.
Then it writes the value (if valid 0-4095) to an SPI DAC

This is built upon several examples of SPI code and serial to integer conversion which
have been shared with the community. You gotta love the open source concept!
#define DATAOUT 11//MOSI - serial data input
#define DATAIN 12//MISO - not used, but part of builtin SPI
#define SPICLOCK  13//sck - serial clock input
#define SLAVESELECT0 10//ss for 1st DAC - chip select input for 1st DAC
//#define SLAVESELECT1 9//ss for 2nd DAC  - chip select input for 2nd DAC
long setpoint = 0;
char inString[100];
int value = 0;
int dir = 1;
int maxValue = 4000; //max DAC output
int minValue = 1200;  //min DAC output

void setup()
 Serial.begin(9600);        // connect to the serial port
 byte clr;
 pinMode(DATAIN, INPUT);
 digitalWrite(SLAVESELECT0,HIGH); //disable device

 SPCR = (1<<SPE)|(1<<MSTR);  //set up SPI control register

 Serial.println("SPI MCP4921 DAC  Serial Setpoint Control");
 Serial.println("enter # between 0-4096 (preceeded by s with no space)");
 Serial.println("EG:  s1234");
 Serial.println("0 maps to 0V, and 4095 maps to +5V output");

void loop () {
if (Serial.available()>0){                 //when there's no activity on the serial port...
 setpoint =SerialLook();                  // go look for something from the port
 if ((setpoint >= 0) && (setpoint <= 4095)) {     // if true, valid setpoint data was recieved (12bit DAC)
     Serial.print("received setpoint ");  //acknowledges valid setpoint recieved.
     Serial.println(setpoint);            // echoes (in integer format) setpoint data to serial port
     write_value(int(setpoint));          // Write this value to DAC (converted to 2 byte integer from long)
  else {                                  // valid set point data was NOT received
     Serial.println("received invalid string");  //notifies that non valid entry was received

//****************************  functions and subroutines ... ****************************

long SerialLook() {      // gets serial data into long integer
 char inByte;
 char firstchar; //first character of line.  this allows for a variety of different actions depending on first character
 long number = 500000; // return number (initially set to an invalid value)
 firstchar = Serial.read();  
 if (firstchar =='s'){          // if first character is "s" then this is a setpoint command
while(inByte != 13){            // 13 is carriage return-linefeed character.  (gather and process characters until user hits enter)
  inByte = Serial.read();

  // test to make sure characters are valid numbers...
  if ((inByte >= '0') && (inByte <= '9')){
    number= 10*number+long(inByte-'0');
  Serial.flush(); // flush serial buffer
  return  number;

//--- communicate  value to DAC
void write_value(int sample)
 // splits int sample in to two bytes
 byte dacSPI0 = 0;
 byte dacSPI1 = 0;
 dacSPI0 = (sample >> 8)|B00100000 & 0x00FF; //byte0 takes bit 15 - 8 and B00100000 "or"ed with data sets gain to 1
 dacSPI0 |= 0x10;
 dacSPI1 = sample & 0x00FF; //byte1 takes bit 7 - 0
 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

Go Up