I am trying to sample signals at fast rates (>10kHz) with Teensy 3.6/3.5 and record them on the resident SD. It can be viewed as an Audio problem but I am also interested in general signals.
So I tested a simple program collecting ADC samples in a buffer then save the buffer when full. I find that every time the SD is activated there is a significant interference with the ADC (impulses). Apparently, the SD/SPI draws enough current to introduce ADC level shifts and -eventually- noise. I tried different configurations, power supplies (naturally gets worse when connected to a computer), and external SD cards with varying levels of noise but without an actual answer. I would presume that the audio community may have a solution for this (audio contains like periodic clicks) but I could not find any related thread. I would appreciate any ideas or comments.
The following is a typical plot of the problem. Sampling is at 20kHz (T0050, in us), hardware averaging AVG 8 (N08) points and 32 points (I know 32 points requires more time to stabilize the ADC so I probably get some double measurements but it is OK).
I am trying to sample signals at fast rates (>10kHz) with Teensy 3.6/3.5 and record them on the resident SD. It can be viewed as an Audio problem but I am also interested in general signals.
So I tested a simple program collecting ADC samples in a buffer then save the buffer when full. I find that every time the SD is activated there is a significant interference with the ADC (impulses). Apparently, the SD/SPI draws enough current to introduce ADC level shifts and -eventually- noise. I tried different configurations, power supplies (naturally gets worse when connected to a computer), and external SD cards with varying levels of noise but without an actual answer. I would presume that the audio community may have a solution for this (audio contains like periodic clicks) but I could not find any related thread. I would appreciate any ideas or comments.
The following is a typical plot of the problem. Sampling is at 20kHz (T0050, in us), hardware averaging AVG 8 (N08) points. The signal fluctuates between 3390-3395 (ADC) and at the time instants of saves "spikes" appear with amplitude 50 (ADC) and lasting for about 10-20ms.
Code:
So I tested a simple program collecting ADC samples in a buffer then save the buffer when full. I find that every time the SD is activated there is a significant interference with the ADC (impulses). Apparently, the SD/SPI draws enough current to introduce ADC level shifts and -eventually- noise. I tried different configurations, power supplies (naturally gets worse when connected to a computer), and external SD cards with varying levels of noise but without an actual answer. I would presume that the audio community may have a solution for this (audio contains like periodic clicks) but I could not find any related thread. I would appreciate any ideas or comments.
The following is a typical plot of the problem. Sampling is at 20kHz (T0050, in us), hardware averaging AVG 8 (N08) points and 32 points (I know 32 points requires more time to stabilize the ADC so I probably get some double measurements but it is OK).
I am trying to sample signals at fast rates (>10kHz) with Teensy 3.6/3.5 and record them on the resident SD. It can be viewed as an Audio problem but I am also interested in general signals.
So I tested a simple program collecting ADC samples in a buffer then save the buffer when full. I find that every time the SD is activated there is a significant interference with the ADC (impulses). Apparently, the SD/SPI draws enough current to introduce ADC level shifts and -eventually- noise. I tried different configurations, power supplies (naturally gets worse when connected to a computer), and external SD cards with varying levels of noise but without an actual answer. I would presume that the audio community may have a solution for this (audio contains like periodic clicks) but I could not find any related thread. I would appreciate any ideas or comments.
The following is a typical plot of the problem. Sampling is at 20kHz (T0050, in us), hardware averaging AVG 8 (N08) points. The signal fluctuates between 3390-3395 (ADC) and at the time instants of saves "spikes" appear with amplitude 50 (ADC) and lasting for about 10-20ms.
Code:
Code:
/*
* Audio recording 2 channels
*/
#define IOROWS 9000
#define IOBUFFERSIZE 72000UL
#define LED13 13
#include <DS1307RTC.h> // Real time clock
#include <IntervalTimer.h>
IntervalTimer TeensyTimer1;
IntervalTimer TeensyTimer2;
#include <Wire.h>
#include <SPI.h>
#include <SdFat.h>
#include <EEPROM.h>
#include <ADC.h>
ADC *adc = new ADC(); // adc object;
SdFatSdioEX sdEx;
File file;
char RAWFileName[] = "RawData00.dat";
char CharIn;
uint8_t FileCount[2], FileCountNum;
uint8_t BUFF1[IOBUFFERSIZE], BUFF2[IOBUFFERSIZE];
volatile uint32_t BufferIndex1, BufferIndex2, TSI=200;
volatile int16_t buff16[4], queue, AVG=1;
volatile uint16_t i,j, ii, o_count, j13=1, j2, hiB, hiA;
volatile uint32_t time0, dtime0, time1, dtime1, MM, MM1, RTnow;
volatile uint32_t RawSavingTimeStart, RawSavingTime;
volatile uint32_t ADCcount, ADCcountLast, ADCActive;
volatile int16_t ADC14[2], Signal ;
volatile int32_t Signal2Min=32000, Signal2Max=0, SignalMin=32000, SignalMax=0, RTinit;
uint8_t adc_pins[] = {A1,A16, A2,A17 };
void setup() {
pinMode(LED13, OUTPUT);
pinMode(2, OUTPUT);
pinMode(adc_pins[0], INPUT);
pinMode(adc_pins[1], INPUT);
Serial.begin(115200);
delay(200);
Wire.begin(0x10); // join i2c bus with address
Wire.setClock(400000L) ;
rtc_set(0UL); RTinit = 0UL;
sdEx.begin(); sdEx.chvol(); // sd.begin(); sd.chvol();
delay(200);
SamplingInterval();
TeensyTimer2.begin(SecondTimerIsr,TSI*2000);
TeensyTimer1.begin(SamplingTimerIsr,TSI);
ADCcount=0;
fileSavingResetRaw(); BufferIndex1 = 0; BufferIndex2 = 0;
queue = 0; o_count = 0;
}
void SamplingInterval (){
AVG = uint16_t (EEPROM.read(35)) ; AVG = constrain(AVG,1,32);
adc->setAveraging(AVG,ADC_0); adc->setAveraging(AVG,ADC_1);
TSI = uint32_t (EEPROM.read(32)) + (uint32_t(EEPROM.read(33)) << 8);
TSI = constrain(TSI,10UL,50000UL);
TeensyTimer1.update(TSI);
TeensyTimer2.update(TSI*2000);
for (i=0; i< 2; i++){
pinMode(adc_pins[i], INPUT); // analogReadResolution(16); analogReadAveraging(1);
adc->setResolution(12,ADC_0); adc->setAveraging(AVG,ADC_0); //analogReference(EXTERNAL);
adc->setResolution(12,ADC_1); adc->setAveraging(AVG,ADC_1); //analogReference(EXTERNAL);
adc->setConversionSpeed(ADC_CONVERSION_SPEED::VERY_HIGH_SPEED,ADC_0); adc->setSamplingSpeed(ADC_SAMPLING_SPEED::VERY_HIGH_SPEED,ADC_0);
adc->setConversionSpeed(ADC_CONVERSION_SPEED::VERY_HIGH_SPEED,ADC_1); adc->setSamplingSpeed(ADC_SAMPLING_SPEED::VERY_HIGH_SPEED,ADC_1);
}
adc->startContinuous(adc_pins[0], ADC_0);
adc->startContinuous(adc_pins[1], ADC_1);
}
void loop() {
if(BufferIndex1 >= IOROWS) { file.write(BUFF1,IOBUFFERSIZE); file.flush();
BufferIndex1 = 0; o_count +=1;SignalMax=0;SignalMin=32000;Signal2Max=0;Signal2Min=32000; }
if(BufferIndex2 >= IOROWS) { file.write(BUFF2,IOBUFFERSIZE); file.flush();
BufferIndex2 = 0; o_count +=1; SignalMax=0;SignalMin=32000; Signal2Max=0; Signal2Min=32000; }
if (Serial.available() > 0) { CharIn = Serial.read(); SerialCommands(); }
RTnow = rtc_get();
if((RTnow - MM1 > 4) ) { MM1 = RTnow; j2=!j2; digitalWrite(2,j2);}
if((RTnow - MM >= 1) ) { SerialOut(); MM = RTnow; }
}
void fileSavingResetRaw() {
file.close();
if(file.open(RAWFileName, FILE_WRITE)){
file.truncate(0);
Serial.print("File name "); Serial.println(RAWFileName);
RawSavingTimeStart = rtc_get();
}
else { SDFail(0); TeensyTimer2.update(1000000); }
}
void SDFail(int nn){
Serial.print("SD fail"); Serial.println(nn);
}
void SamplingTimerIsr(){
time1=micros();
ADC14[1] = (adc->analogReadContinuous(ADC_0));
ADC14[0] = (adc->analogReadContinuous(ADC_1));
buff16[0] = ADC14[0]; buff16[1] = ADC14[1];
buff16[2] = ((uint16_t) ((ADCcount<<16)>>16) ); buff16[3] = ((uint16_t) (ADCcount>>16) );
Signal = (buff16[0]);
SignalMax = max(SignalMax,buff16[0]); SignalMin = min(SignalMin,buff16[0]);
Signal2Max = max(Signal2Max,buff16[1]); Signal2Min = min(Signal2Min,buff16[1]);
if ( BufferIndex1 < IOROWS && queue == 0) {
for (i=0; i<4; i++){
BUFF1[BufferIndex1*8+i*2]=lowByte(buff16[i]);
BUFF1[BufferIndex1*8+i*2+1]=highByte(buff16[i]);
}
BufferIndex1 +=1; if (BufferIndex1 >= IOROWS){ queue = 1;}
}
else if ( BufferIndex2 < IOROWS && queue == 1) {
for (i=0; i<4; i++){
BUFF2[BufferIndex2*8+i*2]=lowByte(buff16[i]);
BUFF2[BufferIndex2*8+i*2+1]=highByte(buff16[i]);
}
BufferIndex2 +=1; if (BufferIndex2 >= IOROWS){ queue = 0;}
}
ADCcount += 1;
dtime1=micros()-time1;
}
void SecondTimerIsr(){
j13=!j13; digitalWrite(LED13,j13);
}
void SerialCommands(){
if (CharIn == 'C'){ Serial.print("Clearing EEPROM: "); cli();
for (ii=0; ii<4096; ii++) { EEPROM.write(ii,0);} sei(); }
if (CharIn == 'T'){
if (Serial.available() > 0) { CharIn = Serial.read(); TSI = uint32_t(CharIn - 48)*1000; }
if (Serial.available() > 0) { CharIn = Serial.read(); TSI += uint32_t(CharIn - 48)*100; }
if (Serial.available() > 0) { CharIn = Serial.read(); TSI += uint32_t(CharIn - 48)*10; }
if (Serial.available() > 0) { CharIn = Serial.read(); TSI += uint32_t(CharIn - 48); }
TSI = constrain(TSI,10UL,50000UL);
TeensyTimer1.update(TSI);
Serial.print("NewSamplingTime "); Serial.println(TSI);
cli(); EEPROM.write(32,lowByte(uint16_t(TSI))); EEPROM.write(33,highByte(uint16_t(TSI))); sei();
SamplingInterval();
}
if (CharIn == 'N'){
if (Serial.available() > 0) { CharIn = Serial.read(); AVG = uint16_t(CharIn - 48)*10; }
if (Serial.available() > 0) { CharIn = Serial.read(); AVG += uint16_t(CharIn - 48); }
AVG = constrain(AVG, 1,64);
Serial.print("New ADC Averaging "); Serial.println(AVG);
cli(); EEPROM.write(35,(AVG)); sei();
SamplingInterval();
}
if (CharIn == '?'){ Serial.println("Serial Commands: ");
Serial.println("T = Sampling Time (followed by 4 digits, Tnnnn in usec, > 20us depending on AVG) ");
Serial.println("N = ADC averaging (followed by 2 digits, 1-32) ");
Serial.println("C = Clear EEPROM: ");
}
}
void SerialOut(){
Serial.print("TIME: "); //Serial.print(rtc_get());
Serial.print(RTnow); Serial.print(": ");
Serial.print(" Averaging "); Serial.print(AVG);
Serial.print(" TSI "); Serial.print(TSI);
Serial.print(" D-Signal "); Serial.print(SignalMax-SignalMin);
Serial.print(" Signalmax "); Serial.print(SignalMax);
Serial.print(" D-Signal2 "); Serial.print(Signal2Max-Signal2Min);
Serial.print(" Signal2max "); Serial.print(Signal2Max);
Serial.print(" ADC "); Serial.print(ADCcount);
Serial.print(" OUT "); Serial.print(o_count);
Serial.print(" dt1 "); Serial.print(dtime1);
Serial.println(" ");
}