I'm trying to set the ADC in free-running mode, and take data when a conversion is complete.
However, when I include:
ADCSRA |= (1 << ADFR);
in my sketch, the compiler gives an error. I've included both avr/io.h and avr/interrupt.h in my sketch, and the other AVR values I'm accessing work fine. What am I missing?
From a quick glance at the ATmega328 datasheet, enabling "free running" mode is a bit more complicated than setting a bit. You might want to spend some time reading.
"ADFR" is a label on a control line; it is not a defined bit in the ADCSRA register. While you have the datasheet open, be sure to read through the "ADCSRA – ADC Control and Status Register A" section.
I've included both avr/io.h and avr/interrupt.h in my sketch
This is not necessary. But I don't think it will cause problems.
In future posts, you should also include which board / processor you're using.
I think free-running has been discussed here a few times. You may also want to search the forum.
I believe free-running requires an interrupt service routine. If that's the case, be sure to read about the volatile keyword, disabling interrupts, and atomic operations.
Thanks for the reply. Free-running has been discussed here a few times, as well as on the AVRFreaks forum, and I read the pertinent posts on both.
I'm still not certain why the ADFR didn't work, maybe only the 128 has that and not the 328p? I was under the impression that they were more or less identical except for their memory?
I don't think you need an interrupt service routine to run the ADC in free-running mode, though I was trying to implement free-running so I could call the conversion complete interrupt with clock regularity.
I was able to get it to work using ADATE to set auto-triggering and setting ADCSRB to free-running mode. However, a word of warning to anyone who might be reading this: make sure you read ADCL before ADCH! I read that fact multiple times and STILL made the mistake, it's an easy one to make.
Anyway, thanks for the input everyone, but it looks like I've finally got the thing working!
I've chopped up my code, removing the non-pertinent parts, to present what should give an average ADC value every second, but don't come knocking on my door if it breaks everything. I haven't tested it with all the omissions.
But I figured I'd include it anyway just in case it proves helpful to anyone (or if some gurus want to point out that I've done something horrendously wrong).
volatile long adcbin;
volatile long cnt;
void setup()
{
Serial.begin(115200); //begin Serial comm
adcbin = 0; //accumulation bin
cnt = 0; //count variable
ADMUX |= (1 << REFS0); // Set ADC reference to AVCC
ADCSRA |= (1 << ADEN); // Enable ADC
ADCSRA |= (1 << ADATE); // Enable auto-triggering
ADCSRA |= (1 << ADIE); // Enable ADC Interrupt
sei(); // Enable Global Interrupts
ADCSRA |= (1 << ADSC); // Start A2D Conversions
}
void loop()
{
delay(1000); //Every second:
ADCSRA &= ~(1 << ADSC); //Stop ADC
ADCSRA &= ~(1(ADCSRA,ADIE); //Disable ADC interrupts
Serial.print(adcbin,DEC); //Print the sum of ADC values
Serial.print("/");
Serial.print(cnt,DEC); //divided by the number of samples
cnt = adcbin / cnt;
Serial.println(cnt,DEC); //Gives you the average sample's ADC value
cnt = 0; //Re-initialize
adcbin = 0;
ADCSRA |= (1 << ADIE); //Re-enable ADC interrupts and A2D conversions
ADCSRA |= (1 << ADSC);
}
ISR(ADC_vect) //ADC interrupt
{
uint8_t high,low;
low = ADCL; //Make certain to read ADCL first, it locks the values
high = ADCH; //and ADCH releases them.
aval = (high << 8) | low;
/* for further brevity at cost of clarity
aval = ADCL | (ADCH << 8);
also seems to work */
adcbin = adcbin + aval; //accumulate the ADC values
cnt++; //iterate the counter
}