Go Down

Topic: Speed up arduino (Read 6374 times) previous topic - next topic

Nick Gammon

The ADC converter takes 13 ADC clock cycles where each one is 1/125000 seconds, so it has to take 104 uS, as AWOL said. My measured time of 112 uS is in fact only 8 uS more than that.

According to the datasheet:

Quote
By default, the successive approximation circuitry requires an input clock frequency between 50 kHz and 200 kHz to get maximum resolution. If a lower resolution than 10 bits is needed, the input clock frequency to the ADC can be higher than 200 kHz to get a higher sample rate.


So you could change the prescaler if you don't mind a lower resolution. But I would be looking elsewhere, since there is a big difference between being able to do analogReads at a rate of 8.92 kHz and your reported throughput of 120 Hz.
Please post technical questions on the forum, not by personal message. Thanks!

More info:
http://www.gammon.com.au/electronics

hong89

Thanks a lot for the information.

Now I have frequency of 1.2kHz with three analog input and two digital input.

In before I notice that just 120Hz is because I had put "Serial.print" in the program. After take out the serial print to the serial monitor, it getting faster.

In the other case, I have to connect an ethernet shield to my mega 2560, with that I worry that in the end the sampling rate for the system will go below 1kHz.This is the reason I would like to use ADC in my project.

PaulS

Quote
In the other case, I have to connect an ethernet shield to my mega 2560, with that I worry that in the end the sampling rate for the system will go below 1kHz.This is the reason I would like to use ADC in my project.

If the goal is to send the data to another computer, send raw data and let the other computer do the number crunching. Presumably, you won't be sending data to something slower than an Arduino.

Nick Gammon

#33
Apr 17, 2012, 11:17 pm Last Edit: Apr 17, 2012, 11:24 pm by Nick Gammon Reason: 1
What you can do is combine some of the ideas presented here. First, do the analog reading with an interrupt, that way you can be processing the data and sending the result while you are taking the next one.

Code: [Select]
const byte adcPin = 0;
volatile int adcReading;
volatile boolean adcDone;
boolean adcStarted;

void setup ()
{
 Serial.begin (115200);
 // set the analog reference (high two bits of ADMUX) and select the
 // channel (low 4 bits).  this also sets ADLAR (left-adjust result)
 // to 0 (the default).
 ADMUX = _BV (REFS0) | (adcPin & 0x07);

}  // end of setup

// ADC complete ISR
ISR (ADC_vect)
 {
 byte low, high;
 
 // we have to read ADCL first; doing so locks both ADCL
 // and ADCH until ADCH is read.  reading ADCL second would
 // cause the results of each conversion to be discarded,
 // as ADCL and ADCH would be locked when it completed.
 low = ADCL;
 high = ADCH;

 adcReading = (high << 8) | low;
 adcDone = true;  
 }  // end of ADC_vect
 
void loop ()
{
 // if last reading finished, process it
 if (adcDone)
   {
   adcStarted = false;
   Serial.write (highByte (adcReading));
   Serial.write (lowByte (adcReading));
   adcDone = false;
   }
   
 // if we aren't taking a reading, start a new one
 if (!adcStarted)
   {
   adcStarted = true;
   // start the conversion
   ADCSRA |= _BV (ADSC) | _BV (ADIE);
   }    
 
}  // end of loop


This code was run on a Uno, not a Mega2560.

The above sketch does a non-blocking analog conversion (Pin A0 in this case). When complete it uses Serial.write to send the two bytes (high byte, low byte) to the computer.

As you can see here:



The bytes are being sent as fast as they can (once every 170 uS). Since 115200 baud takes 1/11520 seconds per byte (86.8 uS) then this is what you expect. The analog conversions take 104 uS each, so they can fit inside the time needed to send 16 bits to the computer.

Now as PaulS suggested, do all your calculations on the computer (not the Arduino) so you don't slow down the sampling rate. You can see that you would have a maximum of about 66 uS to do calculations before you started to slow it down.

This gives you a sampling frequency of 5.88 kHz, which is faster than your requirements (1/.000170).
Please post technical questions on the forum, not by personal message. Thanks!

More info:
http://www.gammon.com.au/electronics

hong89

Hi Nick Gammon,

Sorry that I'm not understand about the code

Here is my original code,
Code: [Select]

int fsrPin = 0;     // the FSR and 10K pulldown are connected to a0
int fsrReading;     // the analog reading from the FSR resistor divider
int fsrVoltage;     // the analog reading converted to voltage
unsigned long fsrResistance;  // The voltage converted to resistance, can be very big so make "long"
unsigned long fsrConductance;
long fsrForce;       // Finally, the resistance converted to force

void setup(void) {
  Serial.begin(9600);   // We'll send debugging information via the Serial monitor
}

void loop(void) {
  fsrReading = analogRead(fsrPin); 
  Serial.print("Analog reading = ");
  Serial.println(fsrReading);

  // analog voltage reading ranges from about 0 to 1023 which maps to 0V to 5V (= 5000mV)
  fsrVoltage = map(fsrReading, 0, 1023, 0, 5000);
  Serial.print("Voltage reading in mV = ");
  Serial.println(fsrVoltage); 

  if (fsrVoltage == 0) {
    Serial.println("No pressure"); 
  } else {
    // The voltage = Vcc * R / (R + FSR) where R = 10K and Vcc = 5V
    // so FSR = ((Vcc - V) * R) / V        yay math!
    fsrResistance = 5000 - fsrVoltage;     // fsrVoltage is in millivolts so 5V = 5000mV
    fsrResistance *= 10000;                // 10K resistor
    fsrResistance /= fsrVoltage;
    Serial.print("FSR resistance in ohms = ");
    Serial.println(fsrResistance);

    fsrConductance = 1000000 / fsrResistance;           // we measure in micromhos so
    Serial.print("Conductance in microMhos: ");
    Serial.println(fsrConductance);

    // Use the two FSR guide graphs to approximate the force
    if (fsrConductance <= 100)                  //range 0-2N
    {
      fsrForce = fsrConductance / 20;
      Serial.print("Force in Newtons: ");
      Serial.println(fsrForce);     
    }
   
    else
    {
      fsrForce = fsrConductance / 50;                //range 80N-100N
      Serial.print("Force in Newtons: ");
      Serial.println(fsrForce);           
    }
   
 
  }
  Serial.println("--------------------");
  delay(1000);
}


How can I combine the code as given by you?

AWOL

There's very little point speeding-up that code; you're spending far longer transmitting serial characters than you are converting analogue inputs. (One bit at 9600 bps takes about the same as a single A/D conversion)
"Pete, it's a fool looks for logic in the chambers of the human heart." Ulysses Everett McGill.
Do not send technical questions via personal messaging - they will be ignored.

hong89

Quote
Now as PaulS suggested, do all your calculations on the computer (not the Arduino) so you don't slow down the sampling rate. You can see that you would have a maximum of about 66 uS to do calculations before you started to slow it down.


I'm just using arduino to deal with the calculation without computer help


I had combine some code form ethernet shield and add in the code from AWOL from another discussion(http://arduino.cc/forum/index.php/topic,101718.msg763256.html#msg763256),  when without signal read in, the sampling rate is 888Hz, with the signal read in the sampling rate drop to 780Hz.

So if I use another ADC to deal with the analog signal and using only the digitalRead() of the arduino, this will increase the sampling rate right?
(but I'm not familiar on how to give the clock input for the ADC)



Many thanks~~~

PaulS

You are still concentrating on the wrong parts of your code. You can speed up communications, making it 12 times faster, by changing the 9600 baud rate to 115200. Your maximum sampling rate is going to be impacted far more by the stupid delay(1000) in the code than by the few nanoseconds the ADC takes.

AWOL

Quote
Your maximum sampling rate is going to be impacted far more by the stupid delay(1000) in the code than by the few nanoseconds the ADC takes.

I think, to be fair, that's a hundred thousand or so nanoseconds
"Pete, it's a fool looks for logic in the chambers of the human heart." Ulysses Everett McGill.
Do not send technical questions via personal messaging - they will be ignored.

PaulS

#39
Apr 18, 2012, 12:49 pm Last Edit: Apr 18, 2012, 12:52 pm by PaulS Reason: 1
Quote
I think, to be fair, that's a hundred thousand or so nanoseconds

True. but OP wants to shave time off that, while keeping the slow baud rate and humongous delays.

It's like cutting the handle off a toothbrush before putting it in the backpack next to the cast iron dutch oven and canned beans.

hong89

Quote
when without signal read in, the sampling rate is 888Hz, with the signal read in the sampling rate drop to 780Hz.


I had change the baud rate to 115200, and take out all delay and serial.print and come up with the frequency as above.

Nick Gammon


It's like cutting the handle off a toothbrush before putting it in the backpack next to the cast iron dutch oven and canned beans.


You know, I think we are all on a roll today. Must be something in the air. ;)
Please post technical questions on the forum, not by personal message. Thanks!

More info:
http://www.gammon.com.au/electronics

Nick Gammon


Hi Nick Gammon,

Sorry that I'm not understand about the code
...
How can I combine the code as given by you?


I'm not sure I understand the question. Given the figures we've been discussing what is your objective? You can't argue with the laws of physics. Well, you can, but you won't win.
Please post technical questions on the forum, not by personal message. Thanks!

More info:
http://www.gammon.com.au/electronics

AWOL

Quote
You can't argue with the laws of physics.

In my head, I'm hearing James Doohan...
"Pete, it's a fool looks for logic in the chambers of the human heart." Ulysses Everett McGill.
Do not send technical questions via personal messaging - they will be ignored.

GoForSmoke


Code: [Select]
const byte adcPin = 0;
volatile int adcReading;
volatile boolean adcDone;
boolean adcStarted;

void setup ()
{
 Serial.begin (115200);
 // set the analog reference (high two bits of ADMUX) and select the
 // channel (low 4 bits).  this also sets ADLAR (left-adjust result)
 // to 0 (the default).
 ADMUX = _BV (REFS0) | (adcPin & 0x07);

}  // end of setup

// ADC complete ISR
ISR (ADC_vect)
 {
 byte low, high;
 
 // we have to read ADCL first; doing so locks both ADCL
 // and ADCH until ADCH is read.  reading ADCL second would
 // cause the results of each conversion to be discarded,
 // as ADCL and ADCH would be locked when it completed.
 low = ADCL;
 high = ADCH;

 adcReading = (high << 8) | low;
 adcDone = true;  
 }  // end of ADC_vect
 
void loop ()
{
 // if last reading finished, process it
 if (adcDone)
   {
   adcStarted = false;
   Serial.write (highByte (adcReading));
   Serial.write (lowByte (adcReading));
   adcDone = false;
   }
   
 // if we aren't taking a reading, start a new one
 if (!adcStarted)
   {
   adcStarted = true;
   // start the conversion
   ADCSRA |= _BV (ADSC) | _BV (ADIE);
   }    
 
}  // end of loop




I look and I look and I see here's something else I hadn't seen before. Until now, to me, interrupts need attachInterrupt() to work... which is not correct.
So I dig and see that when I get back  later I have some reading to do:
http://www.nongnu.org/avr-libc/user-manual/group__avr__interrupts.html

Just one tiny inconsequential nit-pick because it -might- have a tiny chance to speed your code;
why combine the low and high bytes in the IRQ() when you're only going to split them out to print bytes in loop()?

Quote
You can't argue with the laws of physics.

Then science really isn't a matter of opinion?  ]:D
Nick Gammon on multitasking Arduinos:
1) http://gammon.com.au/blink
2) http://gammon.com.au/serial
3) http://gammon.com.au/interrupts

Go Up