Give this a try perhaps?
Code:
/**
* TeensyADC - High speed audio sampling with the teeny 3.2 ARM board This code samples the ADC on the teensy3.2 at
* the highest stable rate possible and dumps that data out the USB port using a double buffer. The purpose of
* this is for high speed bio-acustics, where most of the processing will be done on a second board
* (intel edison at the moment)
*
* Enable the debug and hook pin 2 and 3 to an oscilloscope to watch the timing. Further optimizations
* Could be possible to increase speed. As well as analog signal processing on
* the input before sampling.
*
* @author Ethan Slattery
* @contact Ethan.Slattery@yahoo.com
* @date 15-July-2016
*/
#include <ADC.h>
#define BIT_DEPTH 12 // 13-bit usable ADC sample, Mask with 0x1FFF (8, 10, 12 or 16)
#define SAMPLE_AVERAGES 1 // Increases sample time linearly (0, 4, 8, 16 or 32)
#define SAMPLE_RATE_HZ 150000 // Highest stable sample rate at the moment is 160000, which leaves a 2.6us gap for USB wich takes 2.5us
#define HEADER_SIZE = 2; // Size of the header buffer
#define BUFFER_SIZE 64-HEADER_SIZE // Size of the buffer in samples - the header. This should total 64 bytes
#define DEBUG_ON 0 // enables the debug GPIO (disable for depoyment)
#define DEBUG_PIN_1 2 // GPIO pin for timing the read ISR
#define DEBUG_PIN_2 3 // GPIO pin for timing the PDB frequency
#define LED_PIN LED_BUILTIN // Teensy 3.2 Built in LED pin
#define READ_PIN A2 // Pin for sampling the audio input
//elapsedMicros benchmark1; // Benchmark timer, auto increasing obj :)
ADC *adc = new ADC(); // The ADC object
/*** GLOBAL DATA VARIABLES ***/
volatile uint16_t buffer[BUFFER_SIZE]; // Buffer for the samples to send over USB
uint8_t header[HEADER_SIZE] = {0xFF, 0xFF}; // Header bytes for delimiting packets
volatile uint8_t bufferReady = false;
void setup(void) {
// Set all the pin modes
pinMode(LED_PIN, OUTPUT); // LED PIN
pinMode(DEBUG_PIN_1, OUTPUT); // hardware debug pin
pinMode(DEBUG_PIN_2, OUTPUT); // hardware debug pin
pinMode(READ_PIN, INPUT); // ADC sampling for hydrophone
// Start serial output, 12Mbit/sec no matter what over teensy USB
Serial.begin(115200);
/*** Setup the ADC samples ***/
adc->setAveraging(SAMPLE_AVERAGES);
adc->setResolution(BIT_DEPTH);
adc->setReference(ADC_REF_3V3);
// Set the conversion and sampling speed of the single ADC measurements
// Options: ADC_VERY_LOW_SPEED, ADC_LOW_SPEED, ADC_MED_SPEED,
// ADC_HIGH_SPEED_16BITS, ADC_HIGH_SPEED or ADC_VERY_HIGH_SPEED
adc->setConversionSpeed(ADC_HIGH_SPEED);
adc->setSamplingSpeed(ADC_HIGH_SPEED);
// Enable intterrupts on the ADC module (ADC_0 or ADC_1)
adc->enableInterrupts(ADC_0);
// Perform one read to burn in settings
adc->analogRead(READ_PIN, ADC_0);
// Start the PDB timer at the given freq.
adc->adc0->stopPDB();
adc->adc0->startPDB(SAMPLE_RATE_HZ);
// Get rid of that pesky unused pdb_isr()... I think this may work no idea
NVIC_DISABLE_IRQ(pdb_isr);
// Lets give that ADC irq the high priority it deserves
NVIC_SET_PRIORITY(IRQ_ADC0, 0); // 0 = highest priority
// slight delay to let everything settle and flash light to show setup is complete
digitalWrite(LED_BUILTIN, HIGH);
delay(500);
digitalWrite(LED_BUILTIN, LOW);
delay(250);
while(!Serial){}
// blink LED to show serial is connected and sampling starts
digitalWrite(LED_BUILTIN, HIGH);
delay(250);
digitalWrite(LED_BUILTIN, LOW);
} // END of setup()
void loop(void) {
// Load the header into the USB fifo
Serial.write(header, HEADER_SIZE);
while(!bufferReady) {} // Wait for the buffer to be filled
Serial.write(reinterpret_cast<char *>(buffer), BUFFER_SIZE);
bufferReady = false;
//Serial.flush() Maybe use this just to ensure the data has been sent
}
/**
* ADC Interrupt Service Routine
* This ISR is called once the ADC (triggered by the PDB) is done with it's
* measurement. The appropriate buffer is then selected and the sample is placed
* into the appropriate buffer, split into two bytes if necessary. This entire
* ISR currrently takes 1.2usec to execute.
*/
void adc0_isr() {
static uint8_t buffCounter = 0; // Keeps track of the samples in buffer
#if DEBUG_ON
digitalWriteFast(DEBUG_PIN_1, HIGH);
#endif
buffer[buffCounter++] = uint16_t(adc->readSingle());
// Switch buffer if necessary
if(buffCounter >= BUFFER_SIZE/2) {
buffCounter = 0;
bufferReady = true;
}
#if DEBUG_ON
digitalWriteFast(DEBUG_PIN_1, LOW);
#endif
}
/**
* PDB interrupt
* This interrupt is called when the PDB starts an ADC sample. The starting of
* a sample is automatic, so the only code needed here is to clear the PDB flag.
* Once the sample is done the ADC will trigger an interrupt, currently each
* Sample is ready in 1.72usec
*/
void pdb_isr(void) {
#if DEBUG_ON
digitalWriteFast(DEBUG_PIN_2, HIGH);
#endif
// Clears the interrupt flag from the PDB
PDB0_SC &=~PDB_SC_PDBIF;
#if DEBUG_ON
digitalWriteFast(DEBUG_PIN_2, LOW);
#endif
}
Looking at the source files I do believe Serial.write() is triggering an interrupt. You'd best have a higher priority
Here's that double buffering version I was referring to:
Code:
/**
* TeensyADC - High speed audio sampling with the teeny 3.2 ARM board This code samples the ADC on the teensy3.2 at
* the highest stable rate possible and dumps that data out the USB port using a double buffer. The purpose of
* this is for high speed bio-acustics, where most of the processing will be done on a second board
* (intel edison at the moment)
*
* Enable the debug and hook pin 2 and 3 to an oscilloscope to watch the timing. Further optimizations
* Could be possible to increase speed. As well as analog signal processing on
* the input before sampling.
*
* @author Ethan Slattery
* @contact Ethan.Slattery@yahoo.com
* @date 15-July-2016
*/
#include <ADC.h>
#define BIT_DEPTH 12 // 13-bit usable ADC sample, Mask with 0x1FFF (8, 10, 12 or 16)
#define SAMPLE_AVERAGES 1 // Increases sample time linearly (0, 4, 8, 16 or 32)
#define SAMPLE_RATE_HZ 150000 // Highest stable sample rate at the moment is 160000, which leaves a 2.6us gap for USB wich takes 2.5us
#define HEADER_SIZE = 2; // Size of the header buffer
#define BUFFER_SIZE 64-HEADER_SIZE // Size of the buffer in samples - the header. This should total 64 bytes
#define DEBUG_ON 0 // enables the debug GPIO (disable for depoyment)
#define DEBUG_PIN_1 2 // GPIO pin for timing the read ISR
#define DEBUG_PIN_2 3 // GPIO pin for timing the PDB frequency
#define LED_PIN LED_BUILTIN // Teensy 3.2 Built in LED pin
#define READ_PIN A2 // Pin for sampling the audio input
//elapsedMicros benchmark1; // Benchmark timer, auto increasing obj :)
ADC *adc = new ADC(); // The ADC object
/*** GLOBAL DATA VARIABLES ***/
uint8_t header[HEADER_SIZE] = {0xFF, 0xFF}; // Header bytes for delimiting packets
volatile uint8_t bufferReady = false;
// Buffers for the samples to send over USB
volatile uint16_t buffer1[BUFFER_SIZE];
volatile uint16_t buffer2[BUFFER_SIZE];
// Buffer pointers
volatile uint16_t *volatile frontBuffer = &buffer1[0];
volatile uint16_t *volatile rearBuffer = &buffer2[0];
void setup(void) {
// Set all the pin modes
pinMode(LED_PIN, OUTPUT); // LED PIN
pinMode(DEBUG_PIN_1, OUTPUT); // hardware debug pin
pinMode(DEBUG_PIN_2, OUTPUT); // hardware debug pin
pinMode(READ_PIN, INPUT); // ADC sampling for hydrophone
// Start serial output, 12Mbit/sec no matter what over teensy USB
Serial.begin(115200);
/*** Setup the ADC samples ***/
adc->setAveraging(SAMPLE_AVERAGES);
adc->setResolution(BIT_DEPTH);
adc->setReference(ADC_REF_3V3);
// Set the conversion and sampling speed of the single ADC measurements
// Options: ADC_VERY_LOW_SPEED, ADC_LOW_SPEED, ADC_MED_SPEED,
// ADC_HIGH_SPEED_16BITS, ADC_HIGH_SPEED or ADC_VERY_HIGH_SPEED
adc->setConversionSpeed(ADC_HIGH_SPEED);
adc->setSamplingSpeed(ADC_HIGH_SPEED);
// Enable intterrupts on the ADC module (ADC_0 or ADC_1)
adc->enableInterrupts(ADC_0);
// Perform one read to burn in settings
adc->analogRead(READ_PIN, ADC_0);
// Start the PDB timer at the given freq.
adc->adc0->stopPDB();
adc->adc0->startPDB(SAMPLE_RATE_HZ);
// Get rid of that pesky unused pdb_isr()... I think this may work no idea
NVIC_DISABLE_IRQ(pdb_isr);
// Lets give that ADC irq the high priority it deserves
NVIC_SET_PRIORITY(IRQ_ADC0, 0); // 0 = highest priority
// slight delay to let everything settle and flash light to show setup is complete
digitalWrite(LED_BUILTIN, HIGH);
delay(500);
digitalWrite(LED_BUILTIN, LOW);
delay(250);
while(!Serial){}
// blink LED to show serial is connected and sampling starts
digitalWrite(LED_BUILTIN, HIGH);
delay(250);
digitalWrite(LED_BUILTIN, LOW);
} // END of setup()
void loop(void) {
// Load the header into the USB fifo
Serial.write(header, HEADER_SIZE);
while(!bufferReady) {} // Wait for the buffer to be filled
Serial.write(reinterpret_cast<char *>(frontBuffer), BUFFER_SIZE);
bufferReady = false;
//Serial.flush() Maybe use this just to ensure the data has been sent
}
/**
* ADC Interrupt Service Routine
* This ISR is called once the ADC (triggered by the PDB) is done with it's
* measurement. The appropriate buffer is then selected and the sample is placed
* into the appropriate buffer, split into two bytes if necessary. This entire
* ISR currrently takes 1.2usec to execute.
*/
void adc0_isr() {
static uint8_t buffCounter = 0; // Keeps track of the samples in buffer
#if DEBUG_ON
digitalWriteFast(DEBUG_PIN_1, HIGH);
#endif
rearBuffer[buffCounter++] = uint16_t(adc->readSingle());
// Switch buffer if necessary
if(buffCounter >= BUFFER_SIZE/2) {
buffCounter = 0;
bufferReady = true;
// Swap the buffers around
if(frontBuffer == &buffer1) {
frontBuffer = &buffer2;
rearBuffer = &buffer1;
} else {
frontBuffer = &buffer1;
rearBuffer = &buffer2;
}
}
#if DEBUG_ON
digitalWriteFast(DEBUG_PIN_1, LOW);
#endif
}
/**
* PDB interrupt
* This interrupt is called when the PDB starts an ADC sample. The starting of
* a sample is automatic, so the only code needed here is to clear the PDB flag.
* Once the sample is done the ADC will trigger an interrupt, currently each
* Sample is ready in 1.72usec
*/
void pdb_isr(void) {
#if DEBUG_ON
digitalWriteFast(DEBUG_PIN_2, HIGH);
#endif
// Clears the interrupt flag from the PDB
PDB0_SC &=~PDB_SC_PDBIF;
#if DEBUG_ON
digitalWriteFast(DEBUG_PIN_2, LOW);
#endif
}