Pages: 1 2 3 [4]   Go Down
Author Topic: Speed up arduino  (Read 5206 times)
0 Members and 1 Guest are viewing this topic.
Global Moderator
Offline Offline
Brattain Member
*****
Karma: 474
Posts: 18696
Lua rocks!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
I see here's something else I hadn't seen before. Until now, to me, interrupts need attachInterrupt() to work ...

Indeed not.

http://www.gammon.com.au/interrupts

Although since attachInterrupt already exists in the library, you need to use that for INT0_vect and INT1_vect or you are likely to get a duplicate symbol linker error.  Hmmm ... does a test ... maybe not.

Quote
Just one tiny inconsequential nit-pick because it -might- have a tiny chance to speed your code;
why combine the low and high bytes ...

Because of the comments in analogRead, which I reproduced.

Datasheet, page 251:

Quote
ADCL must be read first, then ADCH, to ensure that the content of the Data Registers belongs to the same conversion. Once ADCL is read, ADC access to Data Registers is blocked.
Logged

0
Offline Offline
Full Member
***
Karma: 2
Posts: 156
It was all digital
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hi Hong89

What are you making and why do you need to sample with 2kHz?

Let's look at your sampling and calculations. This is more or less a test run with 10000 samples:
Code:
int fsrPin = 0;
int fsrReading, fsrVoltage;
unsigned long fsrResistance, fsrConductance;
long fsrForce;

unsigned long time1, time2;
unsigned long loopNumber= 10000;

void setup(){
  Serial.begin(9600);
  time1 = micros();
  for(long i = 0; i < loopNumber; i++){
    fsrReading = analogRead(fsrPin);
    fsrVoltage = map(fsrReading, 0, 1023, 0, 5000);
 
    fsrResistance = 5000-fsrVoltage;
    fsrResistance *= 10000;
    fsrResistance /=fsrVoltage;
   
    fsrConductance = 1000000 / fsrResistance;                  // don't divide by 0 - add check!
   
    if (fsrConductance <= 100) fsrForce = fsrConductance / 20; // be aware some bad integer math can happen here
    if (fsrConductance >  100) fsrForce = fsrConductance / 50; // same same problem
  }
  time2=micros();
  Serial.println((time2-time1)*1.0/loopNumber);         // uS
  Serial.println(1000.0/((time2-time1)/loopNumber));    // kHz
}

void loop(){
}

Result:
281.33 uS
3.56 kHz


3.5 kHz is not that bad but there is problems in your code. This one:
Code:
fsrVoltage = map(fsrReading, 0, 1023, 0, 5000);
is draining time

There is a potential risk of divide by zero here:
Code:
fsrConductance = 1000000 / fsrResistance;
If you read 5V on the pin fsrResistance = 0.

You are loosing most of the accuracy of your reading and calculations here:
Code:
fsrForce = fsrConductance / 20;


First thing first. Let's drop the map function. 5000 / 1023 is more or less 5. We keep integer math for now:

Code:
int fsrPin = 0;
int fsrReading, fsrVoltage;
unsigned long fsrResistance, fsrConductance;
long fsrForce;

unsigned long time1, time2;
unsigned long loopNumber= 10000;

void setup(){
  Serial.begin(9600);
  time1 = micros();
  for(long i = 0; i < loopNumber; i++){
    fsrReading = analogRead(fsrPin);
//  fsrVoltage = map(fsrReading, 0, 1023, 0, 5000);
    fsrVoltage = fsrReading * 5;

    fsrResistance = 5000-fsrVoltage;
    fsrResistance *= 10000;
    fsrResistance /=fsrVoltage;
   
    fsrConductance = 1000000 / fsrResistance;                  // don't divide by 0 - add check!
   
    if (fsrConductance <= 100) fsrForce = fsrConductance / 20; // be aware some bad integer math can happen here
    if (fsrConductance >  100) fsrForce = fsrConductance / 50; // same same problem
  }
  time2=micros();
  Serial.println((time2-time1)*1.0/loopNumber);         // uS
  Serial.println(1000.0/((time2-time1)/loopNumber));    // kHz
}

void loop(){
}

Output:
232.01 uS
4.31 mHz


Wow - 750 Hz up - let's how much we loose on float math:

Code:
int fsrPin = 0;
int fsrReading;
float fsrVoltage, fsrResistance, fsrConductance, fsrForce;

unsigned long time1, time2;
unsigned long loopNumber= 10000;

void setup(){
  Serial.begin(9600);
  time1 = micros();
  for(long i = 0; i < loopNumber; i++){
    fsrReading = analogRead(fsrPin);
    fsrVoltage = fsrReading*(5000.0/1023.0);
 
    fsrResistance = 5000.0-fsrVoltage;
    fsrResistance *= 10000.0;
    fsrResistance /=fsrVoltage;
   
    fsrConductance = 1000000.0 / fsrResistance;  // don't divide by 0 add check!
   
    if (fsrConductance <= 100) fsrForce = fsrConductance / 20.0;
    if (fsrConductance >  100) fsrForce = fsrConductance / 50.0;
  }
  time2=micros();
  Serial.println((time2-time1)*1.0/loopNumber);
  Serial.println(1000.0/((time2-time1)/loopNumber));
}

void loop(){
}

Result:
240.05 uS
4.17 kHz


Only 140 Hz down. Not a lot.

Well, time to bring in the big canon. I did a search for "Faster analog read", and to be honest I don't get much of it except: Yeah -prescale can be set to 16 with no significant loss of quality. Cut and paste code from that thread:

Code:
// defines for setting and clearing register bits
#ifndef cbi
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#endif
#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif

int fsrPin = 0;
int fsrReading;
float fsrVoltage, fsrResistance, fsrConductance, fsrForce;

unsigned long time1, time2;
unsigned long loopNumber= 10000;

void setup(){
  // set prescale to 16
  sbi(ADCSRA,ADPS2) ;
  cbi(ADCSRA,ADPS1) ;
  cbi(ADCSRA,ADPS0) ;
 
  Serial.begin(9600);
  time1 = micros();
  for(long i = 0; i < loopNumber; i++){
    fsrReading = analogRead(fsrPin);
    fsrVoltage = fsrReading*(5000.0/1023.0);
 
    fsrResistance = 5000.0-fsrVoltage;
    fsrResistance *= 10000.0;
    fsrResistance /=fsrVoltage;
   
    fsrConductance = 1000000.0 / fsrResistance;  // don't divide by 0 add check!
   
    if (fsrConductance <= 100) fsrForce = fsrConductance / 20.0;
    if (fsrConductance >  100) fsrForce = fsrConductance / 50.0;
  }
  time2=micros();
  Serial.println((time2-time1)*1.0/loopNumber);
  Serial.println(1000.0/((time2-time1)/loopNumber));
}

void loop(){
}

Result:
142.81 uS
7.04 kHz


Wow, that kinda did the trick. Even with 3 * analogRead you should be surfing above the 2 kHz level.

-Fletcher
 
Logged

Global Moderator
Offline Offline
Brattain Member
*****
Karma: 474
Posts: 18696
Lua rocks!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

I got with your code, Fletcher Chr:

Code:
144.33 uS
6.94 kHz

Now if you combine your code with the idea of doing the ADC conversions in the background you can get even faster:

Code:
// defines for setting and clearing register bits
#ifndef cbi
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#endif
#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif

int fsrPin = 0;
int fsrReading;
float fsrVoltage, fsrResistance, fsrConductance, fsrForce;

unsigned long time1, time2;
unsigned long loopNumber= 10000;

volatile int adcReading;
volatile boolean adcDone;

// 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 setup(){
  // set prescale to 16
  sbi(ADCSRA,ADPS2) ;
  cbi(ADCSRA,ADPS1) ;
  cbi(ADCSRA,ADPS0) ;

  // 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) | (fsrPin & 0x07);
 
  Serial.begin(115200);
  time1 = micros();
 
  // start first conversion
  ADCSRA |= _BV (ADSC) | _BV (ADIE);
 
}

long count = 0;

void loop(){
 
  // if last reading finished, process it
  if (adcDone)
    {
    // save previous reading
    fsrReading = adcReading;
    adcDone = false;
   
    // start next conversion
    ADCSRA |= _BV (ADSC) | _BV (ADIE);

    fsrVoltage = fsrReading*(5000.0/1023.0);
 
    fsrResistance = 5000.0-fsrVoltage;
    fsrResistance *= 10000.0;
    fsrResistance /=fsrVoltage;
   
    fsrConductance = 1000000.0 / fsrResistance;  // don't divide by 0 add check!
   
    if (fsrConductance <= 100) fsrForce = fsrConductance / 20.0;
    if (fsrConductance >  100) fsrForce = fsrConductance / 50.0;

    count++;
   
    if (count >= loopNumber)
      {
      time2=micros();
      Serial.println((time2-time1)*1.0/loopNumber);
      Serial.println(1000.0/((time2-time1)/loopNumber));
      while (true) {}  // now just loop
      }  // end done required number
     
    }  // end last reading done
 
}  // end of loop

Results:

Code:
101.08 uS
9.90 kHz

That saved 44 uS by doing those calculations while the ADC was taking the next reading.

And without fiddling with the prescaler:

Code:
int fsrPin = 0;
int fsrReading;
float fsrVoltage, fsrResistance, fsrConductance, fsrForce;

unsigned long time1, time2;
unsigned long loopNumber= 10000;

volatile int adcReading;
volatile boolean adcDone;

// 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 setup(){
  // 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) | (fsrPin & 0x07);
 
  Serial.begin(115200);
  time1 = micros();
 
  // start first conversion
  ADCSRA |= _BV (ADSC) | _BV (ADIE);
}  // end of setup

long count = 0;

void loop(){
 
  // if last reading finished, process it
  if (adcDone)
    {
    // save previous reading
    fsrReading = adcReading;
    adcDone = false;
   
    // start next conversion
    ADCSRA |= _BV (ADSC) | _BV (ADIE);

    fsrVoltage = fsrReading*(5000.0/1023.0);
 
    fsrResistance = 5000.0-fsrVoltage;
    fsrResistance *= 10000.0;
    fsrResistance /=fsrVoltage;
   
    fsrConductance = 1000000.0 / fsrResistance;  // don't divide by 0 add check!
   
    if (fsrConductance <= 100) fsrForce = fsrConductance / 20.0;
    if (fsrConductance >  100) fsrForce = fsrConductance / 50.0;

    count++;
   
    if (count >= loopNumber)
      {
      time2=micros();
      Serial.println((time2-time1)*1.0/loopNumber);
      Serial.println(1000.0/((time2-time1)/loopNumber));
      while (true) {}  // now just loop
      }  // end done required number
     
    }  // end last reading done
 
}  // end of loop

Results:

Code:
121.74 uS
8.26 kHz
Logged

Pittsburgh, PA, USA
Offline Offline
Faraday Member
**
Karma: 92
Posts: 4730
I learn a bit every time I visit the forum.
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Does CPU activity affect ADC accuracy much?
Logged

I find it harder to express logic in English than in Code.
Sometimes an example says more than many times as many words.

Global Moderator
Offline Offline
Brattain Member
*****
Karma: 474
Posts: 18696
Lua rocks!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

I am guessing not, because the datasheet describes the interrupt processing in some detail. The "accuracy" part does not mention CPU activity as making it less accurate, excepting that you get less noise if you put the processor to sleep during the conversion.

In fact what you could do to slightly improve the throughput is to make each conversion automatically trigger a new one (there is a flag for that) so you don't wait the handful of clock cycles to manually start the next. But I don't think it would make a heap of difference in this case.

Particularly as the OP is limited by data transfer speeds to the host computer.
Logged

Pittsburgh, PA, USA
Offline Offline
Faraday Member
**
Karma: 92
Posts: 4730
I learn a bit every time I visit the forum.
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

excepting that you get less noise if you put the processor to sleep during the conversion.

That's the part that I wondered about, but it must be for extra-special measurements.

Maybe the OP could get a sound chip (or chip set) to give 22k-44k 16-bit sampling? Could an UNO, especially with the serial bottleneck, even keep up with that?


Logged

I find it harder to express logic in English than in Code.
Sometimes an example says more than many times as many words.

Global Moderator
Offline Offline
Brattain Member
*****
Karma: 474
Posts: 18696
Lua rocks!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Doubt it. 10K with the normal prescaler would be the limit, and even adjusting the prescaler would only make a modest difference. Fletcher's change only took us from 4 KHz to 7 KHz. Not a huge amount. I think my change with the calculating in the background, where you got up to 9.9 KHz is probably about the max.

Bear in mind we were playing with an ADC chip a week or so ago in another thread on the forum. That converted at about the rate you read from it using SPI, so that was about 4 uS or so per reading.
Logged

0
Offline Offline
Full Member
***
Karma: 2
Posts: 156
It was all digital
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hi Nick

I did the analysis to see where time was draining away. It surprised me that the map function was takeing so much time. With the map(myValue, 0,A,0,B) the conversion would be a simple B/A ratio. I might look into the engine room of the function to see how much optimization could be applyed.

I'm not sure why Hong89 needs to sample with 2 kHz. In this thread
http://arduino.cc/forum/index.php/topic,101718.msg763256.html#msg763256

He is pointing to a Force Sensor Resistor:
http://dshop.ch/osc/product_info.php?cPath=23_37&products_id=109375

where they write:
These sensors are simple to set up and great for sensing pressure, but they aren't incredibly accurate. Use them to sense if it's being squeezed, but you may not want to use it as a scale.


I guess a change of pree scale won't harm the readings  smiley-razz
We are not talking high res super fast sound analysis here. So why the 2 kHz issue?

It's also hard to figure out what the force result from the 3 sensors are used for. Motorcontrole? datalogging?

-Fletcher
« Last Edit: April 19, 2012, 03:40:23 am by Fletcher Chr » Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 33
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Sorry for the misleading.

My whole project is building an automated system with realtime feedback from the force sensors and hall sensor.
The system is build in matlab simulink already and now I am code it into arduino. It's important to have the feedback from the force sensor as fast as possible to allow the system to responce immediately. By the time, the sampling rate have to be high to avoid of lost counting from tha hall sensor. The position feedback is a main criteria of the system as well.

Now I'm trying with the code from Nick Gammon and Fletcher to combine with the main code of my system.



Many thanks for the help =)
« Last Edit: April 19, 2012, 05:01:14 am by hong89 » Logged

Global Moderator
Offline Offline
Brattain Member
*****
Karma: 474
Posts: 18696
Lua rocks!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Now I'm trying with the code from Nick Gomman ...

Never heard of the fella.
Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 33
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset


Sorry for the typo.      smiley-eek-blue

Logged

0
Offline Offline
Full Member
***
Karma: 2
Posts: 156
It was all digital
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hi

Quote
It's important to have the feedback from the force sensor as fast as possible to allow the system to responce immediately. By the time, the sampling rate have to be high to avoid of lost counting from tha hall sensor.

So I guess the magnet is mounted on a rotating shaft - well how fast is this shaft rotating? 1-2 kHz is rather fast. I have seen 2-stroke motors for RC model airplanes rotate at this speed, but it's a rare speed with substantial shaft mass.

Do you need to export the force sensor and hall count in real time to matlab simulink or do you need to export at a certain event like: The force is above a critical value?

-Fletcher
Logged

Pittsburgh, PA, USA
Offline Offline
Faraday Member
**
Karma: 92
Posts: 4730
I learn a bit every time I visit the forum.
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Might be better to connect the Hall to an interrupt and send micros since last turn, 16-bit.
Might be better to only send force when it changes past a threshold more than sensor error.

2 kHz is fast? I had a Yamaha 650cc cruiser up to 10.5k more than a couple times, about 120 mph. That's nothing compared to a decent crotch rocket.

Logged

I find it harder to express logic in English than in Code.
Sometimes an example says more than many times as many words.

Global Moderator
UK
Offline Offline
Brattain Member
*****
Karma: 290
Posts: 25735
I don't think you connected the grounds, Dave.
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
2 kHz is fast? I had a Yamaha 650cc cruiser up to 10.5k more than a couple times, about 120 mph
Hz != RPM
Logged

"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.

Pittsburgh, PA, USA
Offline Offline
Faraday Member
**
Karma: 92
Posts: 4730
I learn a bit every time I visit the forum.
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Oh, you're right! Hz is per second, not per minute!
Logged

I find it harder to express logic in English than in Code.
Sometimes an example says more than many times as many words.

Pages: 1 2 3 [4]   Go Up
Jump to: