I am currently trying to find a way to simply over sample an incoming wave. I have looked around lots of forums and found out the best way to do such thing is to bypass the analogRead() function and to use interrupts. I am aware that I have to use ISR(ADC_vect). I just do not know how to incorporate this when I am passing in a wave from a waveform generator and I want to get more samples per second than an analogRead().
Details:
I am using the Arduino IDE. I am inputting a 50Hz signal and I would like 1,000 data points. I would like to sample at 1KHz. Ideally I would like to check the values attained through a Serial.println() and copy and paste the results to an excel document to see if the data points are in fact giving me the correct waveform that I inputted with (of course) a lot of data points.
Questions for the non-n00bs:
What needs to be in my void setup() to achieve this? (If I do need anything there)
I happened to have a very similar requirement in a project a while ago. Here are the relevant code snippets. It uses timer 2 to generate an interrupt every 1ms, which is used to kick off the ADC conversion.
const int mainsMonitorRefPin = 0;
static volatile unsigned char currentAdcChannel;
void setup()
{
// Set up the ADC. We need to read the mains voltage frequently in order to get a sufficiently accurate RMS reading.
// To avoid using too much CPU time, we use the conversion-complete interrupt. This means we can't use analogRead(),
// we have to access the ADC ports directly.
currentAdcChannel = mainsMonitorRefPin; // set up which analog input we will read first
ADMUX = B01000000 | currentAdcChannel;
ADCSRB = B00000000; // Analog Input bank 1
ADCSRA = B10011111; // ADC enable, manual trigger mode, ADC interrupt enable, prescaler = 128
// Set up a timer 2 interrupt every 1ms to kick off ADC conversions etc.
// Do this last after we have initialized everything else
ASSR = 0;
TCCR2A = (1 << WGM21); // CTC mode
TCCR2B = (1 << CS22); // prescaler = 64
TCNT2 = 0; // restart counter
OCR2A = 249; // compare register = 249, will be reached after 1ms
TIMSK2 = (1 << OCIE2A);
}
// Interrupt service routine for the 1ms tick
ISR(TIMER2_COMPA_vect)
{
TIFR2 = (1 << OCF2B); // shouldn't be needed according to the documentation, but is (perhaps the ISR is too short without it?)
// Kick off a new ADC conversion. We already set the multiplexer to the correct channel when the last conversion finished.
ADCSRA = B11001111; // ADC enable, ADC start, manual trigger mode, ADC interrupt enable, prescaler = 128
}
// Interrupt service routine for ADC conversion complete
ISR(ADC_vect)
{
// The mcu requires us to read the ADC data in the order (low byte, high byte)
unsigned char adcl = ADCL;
unsigned char adch = ADCH;
unsigned int adcVal = (adch << 8) | adcl;
switch(currentAdcChannel)
{
case mainsMonitorRefPin:
// do whatever you want with the reading in adcVal
// set currentAdcChannel to the next pin you want to read
...
break;
// add switch cases fort the other channels you want to read here...
}
// Set the ADC multiplexer to the channel we want to read next. Setting if here rather than in the tick ISR allows the input
// to settle, which is required for ADC input sources with >10k resistance.
ADMUX = (B01000000 | currentAdcChannel); // Vcc reference, select current channel
}
It is crazy how the simpliest things do what you want! I put in a 50 Hz signal and got 50 samples per cycle which turns out to be 1,000 samples per second!