Fast 16bit ADC using Arduino Due and AD977

Hello
The code below works at adjustable speeds, sending 16bit voltages to a PC terminal (Realterm, etc.)

  //AD977DueOne program written by R H Clarke 8/April/2013 to upload data to PC via Arduino Due

  //***********************************************************************/
  //Include SPI library functionns
  //*********************************************************************/
  #include "SPI.h"
  //*************************************************************************/
void setup() 
{
  // Set up Serial link (via USB) to PC
  Serial.begin(57600); // Standard "baud rate" for USB link is 115200
  // initialise SPI bus on Due for a device on pin 10
  SPI.begin(10); //initialise SPI bus for a device with its SS input on pin 10
  SPI.setBitOrder(10, MSBFIRST);  //parameters are SS pin and bit order
  SPI.setClockDivider(10, 255); //parameters are SS pin and a number from 1 to 255: clock divider
  SPI.setDataMode(10, SPI_MODE0); // i.e. clock low when "resting", and collect bit on rising clock edge
}
//***************************************************************/
//Main program starts
//*****************************************************************/
void loop() 
{
  byte response1 = SPI.transfer(10,0x00, SPI_CONTINUE);
  byte response2 = SPI.transfer(10,0x00);
  // Calculation below may be dodgy: it is based upon a 0-4.980V analog input with the lower 8 bits
 // giving zero to 0.01953V, max,
  //and the upper 8 bits the rest
  int volts=((4980470*response1)/255)+((19530*response2)/255);
  Serial.print(volts,5);
  Serial.println();
}

The Due (see photo) is covered by a proto shield with an AD977, a hex invertor (to correct the barSS polarity), and a 5 to 3.3V level shifter for the DATA output. A crude input is provided by a pot. An additional image shows the barSS and DATA waveforms at 255 clock divider and 57600 baud (Realterm's default), together with the Realterm output.

I would greatly appreciate any comments about the code, especially the use of integer maths without the use of the fancy way for combining MSB/LSB data which, frankly, I don't understand.

waveform and Volts x 1000,000.JPG

Oops: the print data line should read "Serial.print(volts);", not "Serial.print(volts,6);", The line delivers voltsx1,000,000 to the terminal.
R

The relevant line is probably this one:

  int volts=((4980470*response1)/255)+((19530*response2)/255);

Hint: call variables so that the content is best described. If it contains microvolts, don't call it volts.

Then use the correct shifting:

  int microvolts = (((uint32_t) response1) << 8 | response2) * 76;

First we create a 16bit value from the two bytes, resulting in a value in the range 0 - 65535. The upper limit multiplied by 76 gives 4980660, which should be sufficiently accurate for your usage.

Pylon
Thanks for your help. I'm amazed how none of my C books have anything about data collection; it's all boring stuff about databases, and the web. R

sadlerochit,

could you post the schematics of the shield? I would like to try to build it, and the schematics would help me a lot. Thank you!

Ventu

I'm afraid I don't have a schematic. It just grew. The circuit is based upon recommended circuits in the AD977 datasheet, specifically fig 13 centre, left, for the analogue side, and fig 29 for the digital side. Note the need for a level shifter (MC14504B) and an invertor (MC14584) on the digital side or you will break the Due. Wire up using the circuits in their respective datasheets. You can leave the parts you don't use unconnected. The input can be improved with a simple impedance matching/low pass filter op amp circuit but works without. The connections to the Due are: pin 10 - inverted R/C, pin 12 - CLK, pin 13 - level shifted data out from AD 977. I don't use pin 11.
I hope this helps
Richard

Hi sadlerochit, I was trying to do same, good job!
Just a question, why are you using

SPI.transfer(10,0x00,..

and then the inverter to get barSS, instead of just using

SPI.transfer(10,0xFF,..

Hello Goan

The SPI.transfer() function library description says that the "(10,0x00)" parameters start the byte transfer by pulling pin 10 low from its normal high state. Using "(10,0xFF)" would not do this and would mess up the SPI timing. Therefore the invertor is necessary.

Incidentally, I have written a fast VB.NET terminal for my PC and have tested all the available baud rates. Not all the fast ones work but 460800 does! This is great because it allows a wider bandwidth, giving a Nyquist frequency of a few KHz. I think this is because the Due's 460800 rate may closer to the PC's than the others but am not sure. I understand that baud rates are often only approximations to the specified SPI rates). The Due's 460800 baud rate does not work with my off-the shelf terminal (Realterm).

Does anybody know how to combine a VB.NET terminal with a good realtime graphics display package, maybe "Processing"??

Richard

The SPI.transfer() function library description says that the "(10,0x00)" parameters start the byte transfer by pulling pin 10 low from its normal high state. Using "(10,0xFF)" would not do this and would mess up the SPI timing. Therefore the invertor is necessary.

Both versions do pull SS low but the first version sends a null byte to the chip while the later sends only one bits. SPI is capable (it even enforces) of sending and receiving concurrently, so if you want to receive a byte, you have to also send one. Usually a null byte is sent while you expect a receiving byte.

I understand that baud rates are often only approximations to the specified SPI rates

You mix things up. The baud rates you specify are for the UART communication and not SPI. You don't communicate with the PC by SPI.

Karma
I don't understand your first point, which doesn't appear to be consistent with what the library says and with what the following code actually does:-

while(count<lines) // collect "lines" number of data lines
{
byte response1 = SPI.transfer(10,0x00, SPI_CONTINUE); //upload MSbits from SPI/ADC
byte response2 = SPI.transfer(10,0x00); // upload LSbits from SPI/ADC (Due only)
int signal = (((uint32_t) response1) << 8 | response2); //code by "pylon" on Arduino forum: thanks.
Serial.print(signal); // full resolution integer number
Serial.print('\n'); // for some reason this works better than "Serial.println()"
count++; // increment lines counter
}

As I understand it each "SPI.transfer()" line uploads first the MSbits then the LSbits, not a "null byte".

Thanks for pointing out my accidental confusion of the UART and SPI rates.

Richard

As I understand it each "SPI.transfer()" line uploads first the MSbits then the LSbits, not a "null byte".

SPI.transfer(10,0x00);

transfers a null byte (a byte consisting of 8 zero bits). And yes, usually it transfers it MSB first but that's configurable. I don't know what your "upload" means, but every SPI transfer sends out one byte of information while also receiving one byte at the same time (there are two signal lines, one for outgoing and one for incoming information). This is done by shift out/in registers.

In your code you have two SPI.transfer() calls, the first one sends (and also receives, in your case this is the relevant operation) the higher byte while the second one does the same for the lower byte. The SPI_CONTINUE option is to inform the SPI layer to hold SS low between transfer calls, you ommit that option for the call to transfer() which end that data block.

Karma
Thanks for clarifying your point. I find the library texts a little sparse! R