2

So I have a project where I will be using an Arduino Leonardo and an external ADC (AD7980) to run a 16-bit (18bit) conversion and transmit it to the Arduino and then out on USB as fast as possible.

The AD7980 can run up to 1 MSPS, and I know I cannot reach that with the ATmega32U4 - but I want to know how to optimize my code for running it as fast as possible and how to measure it.

Right now my code is:

#include <SPI.h>

int CNV= 11; //Convert on pin 11
int Slaveselect=8;
int adcValue;
byte highByte;
byte lowByte;

void setup() {
  Serial.begin(115200);
  SPI.begin();
  SPI.setClockDivider(SPI_CLOCK_DIV2 ); // 8 MHz rate
  SPI.setBitOrder(MSBFIRST);
  pinMode(Slaveselect, OUTPUT);
  pinMode(CNV, OUTPUT);
}

void loop() {
  digitalWrite(Slaveselect,HIGH);
  // SS always high to use 3 Wire mode without busy indicator
  digitalWrite(CNV,LOW);
  digitalWrite(13,LOW);
  digitalWrite(CNV,HIGH);//starts conversion
  digitalWrite(13, HIGH);
  digitalWrite(CNV,LOW); //starts to transmit MSB on data line
  digitalWrite(13,LOW);
  highByte = SPI.transfer(0x00);
  lowByte = SPI.transfer(0x00);
  adcValue = highByte << 8;
  adcValue = adcValue | lowByte;
  Serial.print("analog value =");
  Serial.println(adcValue);
  Serial.print('\n');  
}

The datasheet for the ADC can be found here: Datasheet

dda
  • 1,553
  • 1
  • 12
  • 17
Martin.E
  • 23
  • 4

2 Answers2

1
  1. Don't use digitalWrite().
  2. Don't use delay().
  3. Don't flood your Serial connection with useless junk ("analog value =")

You may also consider using lower level SPI operations instead of the SPI library, which is completely blocking and slows you down. At least consider the use of SPI.transfer16(...) which is more efficient than two SPI.transfer(...) calls.

Majenko
  • 103,876
  • 5
  • 75
  • 133
  • "1. Don't use digitalWrite()" What should i use then? – Martin.E Jun 15 '17 at 09:52
  • @Martin.E Direct port manipulation. https://www.arduino.cc/en/Reference/PortManipulation – Majenko Jun 15 '17 at 10:00
  • Either write on GPIO registers directly (https://www.arduino.cc/en/Reference/PortManipulation, http://stackabuse.com/speeding-up-arduino/) or use at least https://github.com/NicksonYap/digitalWriteFast . – Maximilian Gerhardt Jun 15 '17 at 10:01
  • Also - changed my code to not use delay() and changed to SPI.transfer16 - now I have a sample periode of 44 micros - #Edit - With digital write fast, i am now down to 8 Micros – Martin.E Jun 15 '17 at 10:01
  • But by useing this, It looks like i lost some precision - Before this change, my max ADC value was 32000 - now its around 19000, and my minimum was 0 now it is 10000 – Martin.E Jun 15 '17 at 10:09
  • You are probably violating some of the timing specifications (p.7f of the PDF). Conversion time is at least 500ns to at max 710ns, and the SPI.transfer might kick in before that. Time between conversions is at least 1000ns, acquisition time 290ns. FastDigitialWrite needs ~125ns on a 16MHz Uno. You should probably get an oscilloscope / logic analyzer to debug this thing now on these speeds. "Waiting" can also be done by burning cycles using the "NOP" instruction (https://forum.arduino.cc/index.php?topic=433372.0) – Maximilian Gerhardt Jun 15 '17 at 10:22
0

For fast possible speed, use spi transmission buffer and spi interrupt. The concept is that once there is at least free space in the buffer, it interrupts and you load the next byte into the buffer.

That's assuming that dma isn't available.

Short of that, don't use arduino GPIO functions - they are like 100x slower than that's possible. And use a usartt isr to manage transmission.

dannyf
  • 2,730
  • 9
  • 13