Internal vs. External I2C/SPI; Fastest performance?

Hello all,

I'm working on a RC remote, and would like to make the transmitter as fast as possible to limit latency. I'm simply retrofitting an older RC remote with the existing potentiometers, adding in a wireless link between Arduinos (a Pro Mini in the transmitter, and a Mega in the receiver).

Here is the question. To minimize latency, which would be faster with respect to processing time, using the built-in ADCs, or go to an external I2C or SPI chip?

My thoughts are that either way, I will have processing cycles to run the commands: analogRead(x), or some function to grab the data from the external ADC.

I know SPI is faster than I2C, but I don't know how fast the libraries for either of those compare to the built-in analogRead() function. I know each of the commands has a slew of machine code buried behind it, but I'm not proficient enough to know which would be faster.

Thanks in advance to any pointers.

internal ADC takes about 110 millisecond, and analogRead() is blocking (in theory).

In practice you can split the call in three parts making it asynchronuously.
discussed here - -

This allows you to process a value while the ADC takes its next sample.

So, if I understand your other post, if I:


//do other stuff, maybe process the last analog reading, perform other math, etc.

analog0 = AR_Value());

I could eliminate some of the waiting time while I wait for the chip to take a reading?

This is interesting stuff! That would certainly allow me to process what the last ADC reading was, or assemble the packet to send, etc. And, if I understand correctly, this would eliminate some of the 'sitting on my hands' time that I'm trying to avoid.

Thank you!

internal ADC takes about 110 millisecond

110 microSeconds, not milliSeconds.

Even so, an external SPI ADC can do it 10 times faster and with higher resolution.
Typically, for a single channel ADC, just 2 SPI.transfers:

PORTB = PORTB & 0b11111011; // D10 low, 2 clocks
upperByte = SPI.transfer(commandData_if_needed); // 34 clocks at 4 MHz
lowerByte = SPI.transfer(0); // 34 clocks at 4 MHz
PORTB = PORTB | 0b00000100; // D10 high // 2 clocks
result = (upperByte<<8) | lowerByte; // plus any additional manipulation needed. How many clocks? 10?
                                                           // perhaps clearing upper 4

At 4 MHz, takes about 82 clocks x 62.5nS = 5.1 to 5.2uS?

Pick a SPI ADC with 200K or better sampling rate:

(Why does the forum hose the weblinks? or maybe its IE doing it? Front of the link needs editing.)

indeed 110 microseconds (crossroads is right)

It only takes 125ns to read the internal A/D:

int value = ADC;

You either have to initiate a conversion periodically or set it in free-running mode. But the latter isn't feasible if you have more than one channel to read. There is only one internal A/D; to read a different channel requires switching the multiplexor and allowing it time to settle on the new voltage before initiating a conversion. You can do all of those things in the background.

Externally you can have parallel channels free-running, so it's simpler in that sense. But it's not obvious that it would save you any processor cycles. You'd have to compare the code. My guess is that it wouldn't.

Crossroads, excellent! This is what I was looking for. So besides any kind of processing related to math, which I would have to do on either source anyway, it looks like an external ADC is factors above the onboard.

Question, where did the 4MHz come from in your example? This comes from the SPI prescaler, correct? So does this mean that the SPI.transfer() occurs at 4MHz for 34 clocks or 8.5uS? Not that it even matters at that level, as it's still an improvement over the onboard.

I do like the fact that the onboard ADC simplifies wiring, and allows me to accomplish the same thing with fewer components, but this certainly answered my question. But, of course, going external the sky is the limit with the number of ADCs.

For the application, I'm not so worried about precision, since the receiver will be controlling servos anyway, and I'm not going to see anything better than 8-bit precision there.

You'll have to look very long for an external ADC that is faster than the internal one. Then multiply the cost by the number of signals to digitize - a MUX will slow down every ADC.

In it's simplest form, the internal A/D works like this:

ADMUX = bit(REFS0) | adcChannel; // select the channel (ref = Vcc)
sbi(ADSCRA, ADSC); // start the conversion

Then you go and do other things while it converts. Either you come back after you know enough time has elapsed or else you occasionally check the ADSC bit to see if the conversion has completed.

int result = ADC; // read the result

Even after accounting for the code to use millis() to determine when to initiate a conversion and read the result, it's unlikely that an external A/D will save much, if anything, in terms of processor cycles. I'll bet it costs you cycles.