I have a microphone and I need as precise values as possible. I have been told by my teacher, that it is somehow possible to make a ADC timer interrupt, which would interrupt always when the value is ready, so that the reading itself would not slow the program down.
I know, how to use timer interrupts to read the analog value every other ms or something and I even have a code that lets the ADC read automaticly and takes the value, but I need to make it take the value always when it's ready, so that I would have every value the ADC read. I tried to look into the datasheet, but it's too complicated for me.
How can I achieve such thing? I am using arduino uno.
this is the code I have that sets up the ADC interrupt:
ADCSRA = 0;
ADCSRB = 0;
ADMUX = 0;
ADMUX |= (1 << REFS0) + (1 << REFS1);
ADMUX |= (1 << ADLAR);
ADCSRA |= (1 << ADATE); //enabble auto trigger
ADCSRA |= (1 << ADIE); //enable interrupts when measurement complete
ADCSRA |= (1 << ADEN); //enable ADC
ADCSRA |= (1 << ADSC);
ADCSRA |= 0b010;
ISR(ADC_vect) {//when new ADC value ready
af = af + ADCH;
aaf++;
}
I found this on the internet, but it didn't work as I expected.
2 Answers 2
I have been told by my teacher that it is somehow possible to make a ADC timer interrupt.
You might have asked him or he could explain it ;)
Generally, if you want to do anything with an Arduino, which isn't implemented by the Arduino libraries, you're going to have to roll your own code (or copy it from the internet).
The answer from Nick will most certainly be enough to help you with the problem. However, it'd be way more cooler to find out how you can solve these problems yourself.
Getting started
I couldn't find a duplicate of this question, though it's a rather common question. You should've checked some of the tutorials on the web, it'll get you started a lot faster.
But don't forget the datasheet and manuals of the ATMega328(P). In case you can't really find something that fits your needs, or if you want to check some background information.
Setting registers (ADC)
This piece of code (from Nick Gammon), sets up the ADC.
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
Fair enough, but if you wanted to change anything of it, you'll think: "How did he ever come up with this."
These are the registers of the microcontroller. By setting bits in the right pattern, you can enable/disable functions or change settings of the microcontroller.
You should download the ATMega328(P) datasheet. You can very easily navigate through it (if you've got a PDF reader that supports "bookmarks").
Check for "24. Analog to Digital converter" you should read through it, and go to "24.9 (Analog to Digital converter) - Register description.".
Here you can see, which bits of (in example) the ADCSRA
register you have to set to enable the adc (ADEN
), start the conversion (ADSC
). And enable interrupts for it (ADIE
).
Interrupts
Since you also want behavior that has to do with interrupts. You should also check out the interrupts bookmark (#12). 12.1 will show you an table with all interrupt vectors.
It's quite a shame that they don't have a nice C example code, they do have an reference manual though, but you'll implement an interrupt handler like this (from Nick Gammon).
// ADC complete ISR
ISR (ADC_vect)
{
adcReading = ADC;
adcDone = true;
} // end of ADC_vect
If you now want to interrupt on SPI you can check the datasheet or reference manual for the vector name. (SPI_STC_vect
)
it is somehow possible to make a ADC timer interrupt
The ADC and the timers are two different things. If you mean a timer interrupt, I'm not sure why you would want that. If you mean an ADC interrupt, yes you can do do that.
Here is some example code from my page about the ADC which shows using the interrupt (on a Uno):
const byte adcPin = 0;
volatile int adcReading;
volatile boolean adcDone;
boolean adcStarted;
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
Serial.println (adcReading);
delay (500);
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);
}
// do other stuff here
} // end of loop
I should point out that if you do an analogRead
it blocks until the read is done, so the very next line to be executed after analogRead
will be when the data is ready.
ISR(ADC_vect){...}
You also probably want to use a timer to start the conversions at an constant interval.