I am trying to have 2 analogread pins read one after another in the background while other code runs to speed up loop times.
Using this code on one analog input (of 2) I can get my loop frequency up to 8khz from 6khz (using analogreadfast library)
I have spent about 8 hrs (Im not skilled) trying about 100 different variations, but cant get both inputs to run in the background.
I would like to know if its even possible before spending even more time trying.
Thanks
const byte adcPin = 0;
volatile int adcReading;
volatile boolean adcDone;
boolean adcStarted;
int = Vin;
void setup ()
{
Serial.begin (115200);
ADCSRA = bit (ADEN); // turn ADC on
ADCSRA |= bit (ADPS0) | bit (ADPS1) | bit (ADPS2); // Prescaler of 128
ADMUX = bit (REFS0) | (adcPin & 0x07); // AVcc and select input port
} // end of setup
// ADC complete ISR
ISR (ADC_vect)
{
adcReading = ADC;
adcDone = true;
} // end of ADC_vect
void loop ()
{
// if last reading finished, process it
if (adcDone)
{
adcStarted = false;
// do something with the reading, for example, print it
Vin = (adcReading);
adcDone = false;
}
// if we aren't taking a reading, start a new one
if (!adcStarted)
{
adcStarted = true;
// start the conversion
ADCSRA |= bit (ADSC) | bit (ADIE);
}
// main code here
} // end of loop
Remember that an analogRead() takes time to execute.Also, most Arduino boards only have a single ADC that is switched between analogue inputs. This means that a setting time must be allowed between readings on more than a single pin
The ADC can only convert one channel at a time. So if you set the ADC to free-run mode, then alternate between inputs using ADMUX, you can get the fastest rate on 2 inputs.
don't understand what your code is trying to do, don't understand what adcReading is. shouldn't it be
return (ADCH << 8) | ADCL;
it's pretty common to immediately start the next conversion after the previous one completes and the value is read. so your code should just test to see if the conversion is complete, process it and restart
Not exactly clear from your question, are you concerned about how fast the code in loop runs, or how many samples you can get from the ADC?
As @gcjr points out, if you want the ADC readings to go faster, don't mess with starting the reading in the main code, have the interrupt switch inputs and start the next conversion. This will require separate flags and storage for the data from each ADC input.
Additionally, interrupts need to be disabled while accessing the int value from the interrupt.
This code works and only takes 2.12us to analogread.
I am trying to get this speed benefit while reading analog5 and a seperate measurement on analog6.
serial print isnt used, it represents the other irrelevant code running.
to give a bigger picture I want to:
read voltage in (takes 16us with analogreadfast) would like to make it 2us
read voltage out (takes 16us with analogreadfast)
use readings to adjust pwm duty cycle (which influence both vin and vout) takes aprox 20us
repeat as fast as possible
I can only use 1 reading per variable per loop, if those variables arent ready I have to wait for them.
I already have lots of different code that does that with analogreadfast, I want to learn how to do it faster.
const byte adcPin = 5;
volatile int adcReading;
volatile boolean adcDone;
boolean adcStarted;
int Vin = 0;
void setup() {
DDRB |= (1 << DDB5); // Set pin 13 to toggle to time loop
Serial.begin(19200);
ADCSRA = bit(ADEN); // turn ADC on
ADCSRA |= bit(ADPS0) | bit(ADPS1) | bit(ADPS2); // Prescaler of 128
ADMUX = bit(REFS0) | (adcPin & 0x07); // AVcc and select input port
} // end of setup
// ADC complete ISR
ISR(ADC_vect) {
adcReading = ADC;
adcDone = true;
} // end of ADC_vect
void loop() {
// if last reading finished, process it
if (adcDone) {
adcStarted = false;
// do something with the reading, for example, print it
Vin = (adcReading);
adcDone = false;
}
// if we aren't taking a reading, start a new one
if (!adcStarted) {
adcStarted = true;
// start the conversion
ADCSRA |= bit(ADSC) | bit(ADIE);
}
PORTB &= ~(1 << PORTB5); // Set pin 13 LOW
/////////////////////////////////////////////////////////////////////////////////// main code from here
Serial.print("Vin:");
Serial.print(Vin);
Serial.println(" ");
//////////////////////////////////////////////////////////////////////////////////// to here
PORTB |= (1 << PORTB5); // Set pin 13 HIGH
}
I am exploring that option and have a few different ones on the way, but it in my case it dosent make sense to use something else unless I need to.
This isnt for one specific project, its for a set of boards I built that are sort of "4 sizes fits all" kind of thing where I can control a PWM from 8v to 600v off of whatever sensors I want, so versatility and ease of use are #1.
edit, from what I can tell the teensy 4.0 takes 18.6us to do a reading compared to 16us I am currently getting from the nano, that and considering I fried 3 nanos today I cant afford to do that for the marginal increase in speed.
I guess the teensy could go faster, but will need fancy programming to do.
Did you try the code and library @sherzaad provided?
Did you read it and see
if (adcRead != ADC_BUSY) { ////Check if conversion is complete
Serial.println(adcRead); //print out ADC value
Analog.PinSelect(adc_ch[ch_select]); //change selected ADC Channel
ch_select ^= 1; //alternate channel selection
Analog.adcStart(); //Starts next the ADC conversion
} else {
when the result is ready, it is printed. Then the channel is changed, and a new conversion initiated.
Perhaps the demo should have been more fleshy out. Use the ch_select variable to see which channel is being reported, viz:
if (adcRead != ADC_BUSY) { ////Check if conversion is complete
Serial.pirnt(" result for channel "); Serial.print(ch_select ? "one : " : "zero : ");
Serial.println(adcRead); //print out ADC value
Analog.PinSelect(adc_ch[ch_select]); //change selected ADC Channel
ch_select ^= 1; //alternate channel selection
Analog.adcStart(); //Starts next the ADC conversion
} else {
// non-blocking analog read multiple inputs
// Arduino IDE sets ADC with standard settings and enables it
#define numInputs 6 // number of analog inputs
uint8_t anaInput[numInputs] = { 0, 1, 2, 3, 4, 5 }; // A0, A1, A2, A3, A4, A5
volatile uint16_t anaValue[numInputs]; // to store results
ISR(ADC_vect) { // reset of flag happens automatically
static uint8_t i = 0; // counter for input number
anaValue[i] = ADC; // tranfer value to array
if (++i == numInputs) i = 0; // increment or reset counter
ADMUX = (ADMUX & 0xF0) | anaInput[i]; // clear previous setting and select next input
ADCSRA |= 1 << ADSC; // start ADC
}
void setup() {
ADMUX = (ADMUX & 0xF0) | anaInput[0]; // clear previous setting and select first input
ADCSRA |= 1 << ADSC; // start ADC
}
void loop() {
// results from array anaValue[] can be used at anytime, the value will be less than 1ms old
}
I have made a test run of the sketch of post #14 on UNO with 3.3V connected at A0-pin. Unfortunately, the program gives 0.0. I am investigating the missing points (if any!).
In the meantime:
Should the above be: anaValue[i] = ADCW; //anyway, this has not solved the problem.
I have used the following codes to display the Ch-0 voltage at 2-sec interval using millis() function.
unsigned long prMillis = millis();
void loop()
{
if (millis() - prMillis >= 2000)
{
prMillis = millis();
noInterrupts(); //critical section
Serial.println((5.0/1023.0)*anaValue[0], 1); //expecting 3.3V test volt Ch-0
interrupts();
}
else
{
//do something else
}
}
I got the library @sherzaad working... but unless I am doing something wrong its no good since its significantly slower (110us) than just analogreadfast (50 us) for 2 readings.
Rookie mistake, I was testing with one input pulled high and the other left floating, which means I may have had a code that worked, but my testing was worthless
Took some time. Working through a disassembled program is slow. And something came up inbetween.
A number of mistakes:
I messed up the bounderies of the arrays
I forgot to enable ADC interrupt
I assumed the standard setup of Arduino IDE would set the reference voltage
The last one was the hard one to find (to confirm or disprove).
The algorithm was fine, the devil was in the details.
// non-blocking analog read multiple inputs
// Arduino IDE sets ADC with some settings and enables it
#define numInputs 6 // number of analog inputs
uint8_t anaInput[numInputs] = { 0, 1, 2, 3, 4, 5 }; // A0, A1, A2, A3, A4, A5
volatile uint16_t anaValue[numInputs]; // to store results
ISR(ADC_vect) {
static uint8_t i = 0; // counter for input number
anaValue[i] = ADC; // tranfer value to array
if (++i == numInputs) i = 0; // increment or reset counter (corrected)
ADMUX = 0x40 | anaInput[i]; // set referencevoltage and select next input
ADCSRA |= 1 << ADSC; // start ADC
}
void setup() {
ADMUX = 0x40 | anaInput[0]; // set referencevoltage and select first input (assumption failed)
ADCSRA |= 1 << ADIE; // enable ADC interrupt (added)
ADCSRA |= 1 << ADSC; // start ADC
Serial.begin(9600);
}
void loop() {
// results from array anaValue[] can be used at anytime, the value will be less than 1ms old
static uint8_t i = 0;
Serial.print(anaValue[i]);
Serial.print(", ");
if (++i == numInputs) {
i = 0;
Serial.println();
}
delay(500);
}
For ADCL | ADCH << 8: in this case the shift takes precedence over the bitwise OR, but braces are always a good idea. If you use ADC or ADCW you won’t need it.