I have been working on an 8bit Arduino (Mega) Oscilloscope.
Part of my code uses interrupt reading to record data at up to 147KHz (Prescalar 8-128)
The prescalar equals 4 section uses register calls to control single ADC conversion, achieving a 227.3 KHz conversion rate.
Others may find this approach useful:
flags:
hasdata - used to signal data exists
pwtoggle - square wave on pw2
triggered - signal data is ready to collect
writeit - inform main loop that data is read to process
trigplus -true for a positive slope trigger
showdetails - true for serial monitor output
byte trigger- contains trigger threshold
buffer bufa- buf-size=1000 bytes
byte prescalar- sets the adc prescalar
byte adport - sets the adc port- I used 1
// Defines for clearing register bits
#ifndef cbi
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#endif
// Defines for setting register bits
#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif
#define BUF_SIZE 1000
uint8_t bufa[BUF_SIZE];
const byte check = 1<<ADSC;
The following code shows the key subroutines for data recording:
void startad(){
hasdata=false;
if(pwtoggle) {
analogWrite(testpin, 127);
//delayMicroseconds(800);
}
while (Serial.available() > 0) junk = Serial.read();
bufcount=0;
writeit=false;
// triggering variables
trigcount=0;
if (trigger==0) {
triggered=true;
} else {
triggered=false;
oldval=255;
if(!trigplus) oldval=0;
}
if(showdetails){
Serial.println(F("Logging.."));
Serial.flush();
}
// Setup continuous reading of the adc port 'adport' using an interrupt
cli();//disable interrupts
ADCSRA = 0; // clear ADCSRA register
ADCSRB = 0; // free running - only has effect if ADATE in ADCSRA=1
ADMUX |= adport; //set up continuous sampling of analog pin adport
ADMUX |= (1 << REFS0); // set reference voltage to Vcc
ADMUX |= (1 << ADLAR); // left align the ADC value- so we can read highest 8 bits from ADCH register only
if (prescalar > 4){
// 8 prescalar - 147.3 Khz (after tolerable interrupt speed reduction)
if (prescalar==8) ADCSRA |= (1 << ADPS1) | (1 << ADPS0);
// 16 prescalar - 76.8 Khz sampling
if (prescalar==16) ADCSRA |= (1 << ADPS2);
// 32 prescaler - 38.4 Khz sampling
if (prescalar==32) ADCSRA |= (1 << ADPS2) | (1 << ADPS0);
// 64 prescalar - 19.2 Khz sampling
if(prescalar==64) ADCSRA |= (1 << ADPS2) | (1 << ADPS1);
// 128 prescalar - 9.6 Khz sampling
if (prescalar==128) ADCSRA |= (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0);
ADCSRA |= (1 << ADATE); // enable auto trigger
ADCSRA |= (1 << ADIE); //Activate ADC Conversion Complete Interrupt
sbi(ADCSRA,ADEN); // enable ADC
ADCSRA |= (1 << ADSC); //start ADC measurements on interrupt
starttime=micros();
sei();//enable interrupts
}else{
ADCSRA |= (1 << ADPS1); // prescalar 4
sbi(ADCSRA,ADEN); // enable ADC
sei();
/* Fast read via registers
cf pages 242-260 of ATmega328P manual
"A single conversion is started by writing logical 1 to
the ADC Start conversion bit ADSC. This bit stays high
as long as the conversion is in progress and will be cleared
by hardware when the conversion is completed."
227.3 KHz !!!!!
*/
sbi(ADCSRA,ADSC); // First conversion- initialises ADC
while((ADCSRA & check)== check); // wait for ADSC byte to go low
sbi(ADCSRA,ADSC);// New conversion
while (!triggered){
while((ADCSRA & check)== check); // wait for adc conversion
newval=ADCH;
sbi(ADCSRA,ADSC); // New conversion
trip = newval-oldval;
if(!trigplus) trip = -trip;
if (trip > trigger) triggered=true; else oldval=newval;
}
starttime=micros();
for(i=0;i<BUF_SIZE;i++){
while((ADCSRA & check)== check); // wait for conversion
bufa[i]=ADCH;
sbi(ADCSRA,ADSC); // New conversion
}
endtime=micros();
cbi(ADCSRA,ADEN); // disable ADC
writeit=true;
}
}
// Interrupt routine *******************************************
ISR(ADC_vect) {
if (triggered){
bufa[bufcount]=ADCH;;
bufcount++; // increment buffer counter
if (bufcount==BUF_SIZE) {
cbi(ADCSRA,ADEN); // disable ADC
endtime=micros();
writeit=true; // flag that a write is needed
}
} else {
// look for a trigger
newval=ADCH;
trigcount++;
trip = newval-oldval;
if(!trigplus) trip = -trip;
if (trip > trigger) triggered=true; else oldval=newval;
}
}
// End Interupt section *******************************************
The project is published here http://www.instructables.com/id/Arduino-High-speed-Oscilloscope-with-PC-interface/?ALLSTEPS
To date, I have not released the version with the 227.3 KHz section.
I have tested using free running conversion with a prescalar of 4.
The converted values ranged between 7 and 242. This issue disappeared at a prescalar of 8.
My single conversion method produces 0-255 at a prescalar of 4.
I tested at a prescalar of 2. PWM input worked ok, but real Audio signals were not read correctly.