Teensy 3.6 Interference between Fast Sampling and SD Logging

Status
Not open for further replies.

KostasT

New member
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.

hzdNeAMLzwoAAAAASUVORK5CYII=



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("  ");
}
 
Have you tried adding a large-ish 4.7 to 10uF capacitor down near the on-board SD-card for 3.3v? Pads near 46-47 or along the 'H' crossbar would be a good place for the on-board SD-card.
Paul only has so much real estate on the PCB to place extra decoupling.
 
Have you tried adding a large-ish 4.7 to 10uF capacitor down near the on-board SD-card for 3.3v? Pads near 46-47 or along the 'H' crossbar would be a good place for the on-board SD-card.
Paul only has so much real estate on the PCB to place extra decoupling.

I tried a big Tantalum at the crossbar, didn't make much of a difference. I will try for a different type cap later.
 
Status
Not open for further replies.
Back
Top