Archive for June 2015

AD5791 20Bit DAC Evaluation board with Arduino

FullSizeRender(2)

I’ve been playing with a 20bit DAC today. This is a precision DAC from Analog devices which is interfaced over SPI. There’s some non-Arduino example code on github here.

I hacked around with this so I could test the board on an Arduino. The code is below. Gotchas included: forgetting to connect the Voltage Reference!, not toggling LDAC to correctly, and not setting SPI MODE1. Lots of fun and games but it works now. The code below sweeps though all the output voltages. On my device this is going from +10 to -14V. I’ve still some debugging to do, it seems to sit at -14V for a while and I’m not sure why.
[UPDATE: this bug appears do be something to do with the negative voltage reference, selecting AGND on LNK9 (which uses AGND for the negative voltage reference) seems to sweep between 10 and -10 correctly).

#include <SPI.h>

const int reset = A0;
const int clr   = A1;
const int ldac  = A2;
const int sync  = A3;

void setup() {
  Serial.begin(9600);

  SPI.begin();
 

  pinMode(reset, OUTPUT);
  pinMode(clr  , OUTPUT);
  pinMode(ldac , OUTPUT);
  pinMode(sync , OUTPUT);  
}

#define AD5791_NOP 0 // No operation (NOP).
#define AD5791_REG_DAC 1 // DAC register.
#define AD5791_REG_CTRL 2 // Control register.
#define AD5791_REG_CLR_CODE 3 // Clearcode register.
#define AD5791_CMD_WR_SOFT_CTRL 4 // Software control register(Write only).

typedef enum {
ID_AD5760,
ID_AD5780,
ID_AD5781,
ID_AD5790,
ID_AD5791,
} AD5791_type;

struct ad5791_chip_info {
unsigned int resolution;
};
static const struct ad5791_chip_info ad5791_chip_info[] = {
[ID_AD5760] = {
.resolution = 16,
},
[ID_AD5780] = {
.resolution = 18,
},
[ID_AD5781] = {
.resolution = 18,
},
[ID_AD5790] = {
.resolution = 20,
},
[ID_AD5791] = {
.resolution = 20,
}
};

AD5791_type act_device;

/* Maximum resolution */
#define MAX_RESOLUTION 20
/* Register Map */
#define AD5791_NOP 0 // No operation (NOP).
#define AD5791_REG_DAC 1 // DAC register.
#define AD5791_REG_CTRL 2 // Control register.
#define AD5791_REG_CLR_CODE 3 // Clearcode register.
#define AD5791_CMD_WR_SOFT_CTRL 4 // Software control register(Write only).
/* Input Shift Register bit definition. */
#define AD5791_READ (1ul << 23)
#define AD5791_WRITE (0ul << 23)
#define AD5791_ADDR_REG(x) (((unsigned long)(x) & 0x7) << 20)
/* Control Register bit Definition */
#define AD5791_CTRL_LINCOMP(x) (((x) & 0xF) << 6) // Linearity error compensation.
#define AD5791_CTRL_SDODIS (1 << 5) // SDO pin enable/disable control.
#define AD5791_CTRL_BIN2SC (1 << 4) // DAC register coding selection.
#define AD5791_CTRL_DACTRI (1 << 3) // DAC tristate control.
#define AD5791_CTRL_OPGND (1 << 2) // Output ground clamp control.
#define AD5791_CTRL_RBUF (1 << 1) // Output amplifier configuration control.
/* Software Control Register bit definition */
#define AD5791_SOFT_CTRL_RESET (1 << 2) // RESET function.
#define AD5791_SOFT_CTRL_CLR (1 << 1) // CLR function.
#define AD5791_SOFT_CTRL_LDAC (1 << 0) // LDAC function.
/* DAC OUTPUT STATES */
#define AD5791_OUT_NORMAL 0x0
#define AD5791_OUT_CLAMPED_6K 0x1
#define AD5791_OUT_TRISTATE 0x2

long AD5791_SetRegisterValue(unsigned char registerAddress, unsigned long registerValue) {
  unsigned char writeCommand[3] = {0, 0, 0};
  unsigned long spiWord = 0;
  char status = 0;
  spiWord = AD5791_WRITE | AD5791_ADDR_REG(registerAddress) | (registerValue & 0xFFFFF);
  writeCommand[0] = (spiWord >> 16) & 0x0000FF;
  writeCommand[1] = (spiWord >> 8 ) & 0x0000FF;
  writeCommand[2] = (spiWord >> 0 ) & 0x0000FF;
  
  digitalWrite(sync,LOW);
  status = SPI.transfer(writeCommand[0]);
  status = SPI.transfer(writeCommand[1]);
  status = SPI.transfer(writeCommand[2]);
  digitalWrite(sync,HIGH);

  return 0;
}

long AD5791_GetRegisterValue(unsigned char registerAddress) {
  unsigned char registerWord[3] = {0, 0, 0};
  unsigned long dataRead = 0x0;
  char status = 0;
  registerWord[0] = (AD5791_READ | AD5791_ADDR_REG(registerAddress)) >> 16;

  digitalWrite(sync,LOW);

  status = SPI.transfer(registerWord[0]);
  status = SPI.transfer(registerWord[1]);
  status = SPI.transfer(registerWord[2]);
  digitalWrite(sync,HIGH);


  registerWord[0] = 0x00;
  registerWord[1] = 0x00;
  registerWord[2] = 0x00;
    digitalWrite(sync,LOW);
  registerWord[0] = SPI.transfer(0x00);
  registerWord[1] = SPI.transfer(0x00);
  registerWord[2] = SPI.transfer(0x00);
    digitalWrite(sync,HIGH);
  dataRead = ((long)registerWord[0] << 16) |
             ((long)registerWord[1] << 8) |
             ((long)registerWord[2] << 0);
  return dataRead;
}

void loop() {
  
  // setup
  digitalWrite(ldac,HIGH);
  digitalWrite(reset,HIGH);
  digitalWrite(clr,HIGH);
  digitalWrite(sync,HIGH);
  SPI.beginTransaction(SPISettings(1000000, MSBFIRST, SPI_MODE1));
    
  long status = AD5791_GetRegisterValue(AD5791_REG_CTRL);
  
  status = AD5791_GetRegisterValue(AD5791_REG_CTRL);
  
  Serial.print(status,BIN);
  Serial.println();

  unsigned long oldCtrl = status;
  oldCtrl = oldCtrl & ~(AD5791_CTRL_LINCOMP(-1) | AD5791_CTRL_SDODIS | AD5791_CTRL_BIN2SC | AD5791_CTRL_RBUF | AD5791_CTRL_OPGND);

  status = AD5791_SetRegisterValue(AD5791_REG_CTRL, oldCtrl);
  
  status = AD5791_GetRegisterValue(AD5791_REG_CTRL);
  
  Serial.print(status,BIN);
  Serial.println();
  
  long d=100;
  for(long n=0;;n+=d) {
    // write DAC value
    //long int value = n;
    //value = value << (MAX_RESOLUTION - 20); // change for other devices in range
  
    digitalWrite(ldac,LOW);
    long v = n;
    status = AD5791_SetRegisterValue(AD5791_REG_DAC, v);

    //digitalWrite(ldac,HIGH);
    //Serial.print(n,BIN);
    //Serial.println();
    delay(1);
   if(n>= 524287) d=-100;
   if(n<=-524287) d=100;
   if(n%10000 == 0) Serial.println(n);
  }

}

LTC1859CG Evaluation board with an Arduino

arduino_ltc1859

I’ve been playing with a LTC1859 Evaluation board. Linear supply code for their Linduino platform (which costs a lot) and a cable to connect to it. However the Linduino is really just a standard Arduino with a special connector. I wired up the SPI interface to the board as shown above and it works fine (a couple of the ground connections are not connected above, I needed to connect them all before the board was stable). The code adapted from the Linduino library and their LTC1859 example is in the tarball below:

DC682A.tar

DC682A.ino can be simplified to the following to give a continuous single channel voltage reading:

#include <Arduino.h>
#include "Linduino.h"
#include "LT_SPI.h"
#include "LT_I2C.h"
#include "LTC1859.h"
#include <SPI.h>
#include <Wire.h>                         

void setup()
{
  uint16_t adc_code; 
  
  quikeval_SPI_init();           // Configure the spi port for 4MHz SCK
  quikeval_SPI_connect();        // Connect SPI to main data port
  Serial.begin(115200);          // Initialize the serial port to the PC
}

//! Repeats Linduino loop
void loop()
{
  uint16_t user_command;
  uint16_t adc_command;         // The LTC1859 command byte    
  uint16_t adc_code = 0;    // The LTC1859 code
  float adc_voltage;
  uint8_t x, y, startcount, endcount;

  uint8_t uni_bipolar = LTC1859_BIPOLAR_MODE;
  uint8_t single_ended_differential = LTC1859_SINGLE_ENDED_MODE;
  uint8_t range_low_high = LTC1859_HIGH_RANGE_MODE;

  startcount=0;
  endcount=0;
  
  float LTC1859_vref = 10; 

  adc_command = LTC1859_CH0 | uni_bipolar | range_low_high;
  LTC1859_read(LTC1859_CS, adc_command, &adc_code);     // Throws out last reading and starts CH0 conversion
  adc_command = LTC1859_CH0 | uni_bipolar | range_low_high; // Send channel config for the NEXT conversion to take place
  LTC1859_read(LTC1859_CS, adc_command, &adc_code);   // Read previous channel conversion (x-1) and start next one (x)
  LTC1859_read(LTC1859_CS, adc_command, &adc_code);   // Read previous channel conversion (x-1) and start next one (x)
           
  adc_voltage = LTC1859_code_to_voltage(adc_code, LTC1859_vref, range_low_high, uni_bipolar);
 
  //Serial.println(adc_code, BIN);
  Serial.print(adc_voltage, 4);
  Serial.println();
  
}

Picomotor 8351 Internal Pics

pico8351_notes

I decided to have a poke around in one of the Picomotors I bought. The construction is fairly simple. I’ve annotated the above picture and it matches the description in the product documentation well:

slipstick

A nice neat little construction! More pics below:

pico8351_6

pico8351_1

pico8351_2

pico8351_3

pico8351_4

Playing with Picomotors

arduino

I’ve written about slip-stick motors before here and I’ve now purchased a couple from ebay to play with.

I acquired both 30nm and 100nm resolution Picomotors and a Picomotor 8701 driver. This post documents the process on getting them up and running.

This is the 30nm resolution motor:

picomotor

It’s kind of seen better days. The case is coming off and I wrapped teflon tape round it to hold it together. It does work however!

Slip-stick motors work using the difference in motion caused by static and dynamic friction. It’s rather neat and you can read more about it here. But it means they only require a single piezo stack give clockwise and anti-clockwise rotation. Giving you 30-100nm resolution over ~25mm travel.

slipstick

The 8701 driver didn’t come with a power supply, but luckily it’s quite well documented (local copy). It requires +12,-12 and 5V supplies. It also used an old DIN type connector. Unfortunately I didn’t have one knocking around so had to make do with board pins. Hopefully the one I’ve ordered from China will turn up in the new couple of weeks, same goes for the 15pin input signal connector.

func

For the moment I’m running it off a bunch of lab supplies, but will probably put together something more permanent at some point:
power

Driving the motor is pretty straight forward. There’s a 5v “step” input pin (which triggers on the negative edge), and a direction pin. The motor can be driven at upto 1KHz. Initially I drove it using a function generator:

control

Finally I knocked a quick Arduino Sketch together to drive to motor over serial (code below):

arduino


void setup()
{
  // start serial port at 9600 bps and wait for port to open:
  Serial.begin(9600);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for Leonardo only
  }

  pinMode(8, OUTPUT);
  pinMode(9, OUTPUT);
}

int cspeed = 1;
void do_steps(int dir,int dspeed) {

  digitalWrite(9,dir);
  for(int n=0;n<dspeed;n++) {
    digitalWrite(8,1);
    delay(1);
    digitalWrite(8,0);
    delay(1);
  }

}

void loop()
{
  // if we get a valid byte, read analog ins:
  if (Serial.available() > 0) {
    // get incoming byte:
    int in = Serial.read();
    
    if(in == 'W') {do_steps(1,cspeed); Serial.print("S");}
    if(in == 'S') {do_steps(0,cspeed); Serial.print("S");}
    if(in == 'P') {cspeed++; Serial.print(cspeed); Serial.print("\n");}
    if(in == 'L') {cspeed--; Serial.print(cspeed); Serial.print("\n");}
  }
}