Very new to Arduino programming but getting somewhere.
I'm using a Duemilanove with an Adafruit ADC1115 16 bit ADC to read from a potentiometer and move a microstepper motor driver with 25600 steps to focus a camera in real time. I found the built in ADC not high enough resolution to provide smooth movement.
The following code works but its incredibly slow taking 10-15 seconds to do a full turn.
Is there a better way of doing this or is the ADC inherently slow to read?
#include <Wire.h>
#include <Adafruit_ADS1015.h>
#define PotIn 0
Adafruit_ADS1115 ads1115;
void setup(){
// initialize the digital pins as an output.
pinMode(13, OUTPUT);
pinMode(12, OUTPUT);
ads1115.begin();
}
int pos=0; //Position in steps
void loop(){
int val = ads1115.readADC_SingleEnded(0); //get the potentiometer value (range 47-26294)
if(abs(val - pos)> 1 ){ //if diference is greater than 1 steps.
if((val - pos)> 1){
digitalWrite(12, HIGH); // set the direction forward
digitalWrite(13, HIGH); // send pulse to driver
digitalWrite(13, LOW); // turn off pulse by making the voltage LOW
pos++;
}
if((val - pos)< 1){
digitalWrite(12, LOW); // set the direction reversd
digitalWrite(13, HIGH); // send puls to driver
digitalWrite(13, LOW); // turn off pulse by making the voltage LOW
pos--;
}
}
}
I am also getting some slow results with the Adafruit ADC1115. Not in the second range by any means. But It is taking ~30ms per loop. With the 860 hz sampling rate of the ADC1115 I figured i should be getting around 5ms per loop. Maybe a little slower with the serial output. Any suggestions would be appreciated.
Serial.println("Getting single-ended readings from AIN0..3");
Serial.println("ADC Range: +/- 6.144V (1 bit = 3mV)");
// The ADC input range (or gain) can be changed via the following
// functions, but be careful never to exceed VDD +0.3V max, or to
// exceed the upper and lower limits if you adjust the input range!
// Setting these values incorrectly may destroy your ADC!
ads1115.setGain(GAIN_TWO); // 2x gain +/- 2.048V 1 bit = 1mV
ads1115.begin();
}
void loop(void)
{
int16_t adc0, adc1, adc2, adc3;
String data = "";
adc0 = ads1115.readADC_SingleEnded(0);
adc1 = ads1115.readADC_SingleEnded(1);
adc2 = ads1115.readADC_SingleEnded(2);
adc3 = ads1115.readADC_SingleEnded(3);
data +=String(millis()); data+=",";
data +=String(adc0); data+=",";
data +=String(adc1); data+=",";
data +=String(adc2); data+=",";
data +=String(adc3); data+=",";
Serial.println(data);
[ Don't use the String class or Serial in a timing loop, both take an unknown and potential
long time! ]
You basic problem is that you want to implement a servo feedback loop which has to monitor a
slow ADC and simultaneously output tonnes of pulses to a stepper motor. You need
to offload one of these tasks to an interrupt routine I think, so that the pulses can stream
smoothly. You also need something like a PID loop (perhaps just PI terms) to control the
rate of pulse production from the sensed error - then you'll get realistic acceleration. The
I term should get the final stopping position spot-on. This is going to be fairly complex/tricky
to tune I suspect.
digitalWrite is a great place to start when learning, from a uno @ 16mhz it takes 56 cycles whereas directly setting a bit in the register takes 2 cycles.
Sorry guys I am pretty new to this. I will figure out the character array's and I'm sure that will help. Still I could be wrong but I don't think that using the String class accounts for an extra 25 ms per cycle. "Time taken to concatenate 100 bytes: 2,480 µS." - Nick Gammon. I have used a code similar to this many times where I was just reading in 4 voltage channels(10-bit integers) on the analog inputs and writing to an SD card. The times I would get were around 2ms per cycle. I have also used the i2c pins to read and write 4 measures off of the mpu-6050 and it was much faster than this ~5ms.
I have also tried using Jeff Rowberg's library which actually lets you choose the cycles per second.
* Basic testing and check out sketch for a TI ADS1115 4 channel,16 bit, I2C, analog to digital converter chip
Leftyretro 08/06/11
With thanks to Jeff Rowberg for the development of the ADS1115 and I2Cdev I2C libraries
*/
#include <Wire.h>
#include "ADS1115.h"
#include "I2Cdev.h"
ADS1115 adc;
void setup()
{
Wire.begin(); // join I2C bus
Serial.begin(115200); // initialize serial communication
Serial.println("Initializing I2C devices...");
adc.initialize(); // initialize ADS1115 16 bit A/D chip
Serial.println("Testing device connections...");
Serial.println(adc.testConnection() ? "ADS1115 connection successful" : "ADS1115 connection failed");
Serial.println(" ");
// select desired conversion method
adc.setMode(ADS1115_MODE_CONTINUOUS); // free running conversion
// adc.setMode(ADS1115_MODE_SINGLESHOT); // single conversion
// select desired measurement range
adc.setGain(ADS1115_PGA_0P512); // +/- .512 range, .000015625 volts/step
// Select desired sample speed
adc.setRate(ADS1115_RATE_860); // 860 samples per second
//adc.setMultiplexer(ADS1115_MUX_P0_N1); // AN0+ Vs AN1-
adc.setMultiplexer(ADS1115_MUX_P0_NG); // AN0+ Vs ground
}
void loop() {
String data = "";
data+= adc.getConversionP0GND();data+= ",";
data+= adc.getConversionP1GND();data+= ",";
data+= adc.getConversionP2GND();data+= ",";
data+= adc.getConversionP3GND();data+= ",";
data+= millis();
Serial.println(data);
}
I will figure out the character array's and I'm sure that will help.
Why? Is there some reason to send the data using a single call to Serial.println()? Whatever you are sending the data to can't tell whether the data was put in the outgoing buffer using one call, 4 calls, or 25 calls. Don't waste space collecting data. Just write the data to the port as you have it.