Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 18 of 18

Thread: Periodic Noise Spikes When Using Teensy 3.5 ADC

  1. #1

    Periodic Noise Spikes When Using Teensy 3.5 ADC

    Hello! I have been experiencing some seemingly periodic noise spikes (~15mV) when using the ADC of the Teensy 3.5. (Code below.)

    For my project, I take one row of data every 10 milliseconds. Each row is composed of 131 columns: the first columns is the microseconds elapsed since the previous row was taken (used for debugging and such), and the other 130 columns are the results of ADC conversions. Each ADC data column is taken 10 microseconds after the previous column (i.e. the conversions across a row are 10 microseconds apart). The data is stored in two two-dimensional arrays, and essentially while one array is being filled the other is being written to an SD card to allow continuous data collection and saving. All this seems to be working as intended.

    However, I’ve noticed when analyzing the data that there are significant spikes in noise about every 20 seconds (or 130 columns * ~2000 rows ~= 260,000 recorded ADC conversions). See attached graph:

    Click image for larger version. 

Name:	ADC Conversions of 0.58 Volts, One Column Over 30,000 Rows.jpg 
Views:	10 
Size:	130.9 KB 
ID:	21889

    I provide the number of recorded ADC conversions because it seems that the noise is periodic in number of recorded conversions rather than in time. For example, when I reduce each row’s columns of ADC conversions from 130 to 65 (reducing COLUMNS_OF_DATA to 66), the temporal period of the noise shifts to ~40 seconds:

    Click image for larger version. 

Name:	ADC Conversions of 1.03 Volts, One Column Over 30,000 Rows (Each row has 65 ADC columns).jpg 
Views:	8 
Size:	141.6 KB 
ID:	21890

    The data for these graphs was taken using the version of my code provided below. Each graph is just one column over 30,000 rows, but the noise is visible in all columns. The only things connected to the Teensy 3.5 were:
    Power supply connected to ADC pin (A0 in this case) and ground (problem persists when converting voltage from other sources)
    0.2 uF, 2 uF, and 20 uF decoupling capacitors between ADC input pin (Teensy pin A0) and ground
    USB power from my computer (problem persists when Teensy is powered from Vin pin using batteries)

    One thing that is especially confusing to me about this noise is that if I understand my code correctly I am making an ADC conversion every 10 microseconds whether I save it to a data array or not (i.e. once I begin making ADC conversions triggered by the PDB, I never stop or change the frequency from the specified 100 kHz for the duration of the program)—so why does changing the number of columns of recorded ADC conversions per row impact the period of the noise? Also, the exact “shape” (position of first instance from the beginning of data, width, etc) of the noise is not extremely consistent run to run; there have even been a few times that I have taken data and not had this noise occur despite identical software and hardware.

    I would love some help in understanding possible sources of and solutions to this problem. Let me know if I can provide any more information, and thank you so much for your help!

    Code:
    #define FILENAME "adc_0.csv"
    //1 timing + 130 ADC conversions
    #define COLUMNS_OF_DATA 131     // One "sample" is composed of this many columns
    #include <ADC.h>
    #include <SdFat.h>
    SdFatSdio SD;
    #define RESOLUTION 16           // Sets bits of resolution in ADC output (2^number)
    #define INTEGERMAX float((2<<(RESOLUTION-1))-1)                 // 2^RESOLUTION-1 for accurate voltage readouts
    #define ADC_CONVERSION_FREQUENCY 100000      //for 100kHz ADC triggering
    #define PDB_DELAY F_BUS/ADC_CONVERSION_FREQUENCY * 1/8         //this sets the time delay as a fraction of ADC_CONVERSION_FREQUENCY between the rising edge of the FTM clock and the beginning of the ADC conversion
    
    #define SAMPLES_BUFFER_SIZE 150 //number of "samples"
    volatile uint16_t data_buffer_true[SAMPLES_BUFFER_SIZE][COLUMNS_OF_DATA];
    volatile uint16_t data_buffer_false[SAMPLES_BUFFER_SIZE][COLUMNS_OF_DATA];
    volatile bool buffer_switch = true;
    volatile uint16_t sample_index_true = 0;
    volatile uint16_t sample_index_false = 0;
    
    //Used to give first column data, microseconds between rows
    unsigned long this_time = 0;
    unsigned long last_time = 0;
    
    const int AD_VIDEO = A0;            //Input signal to be converted by ADC
    const int WRITE_PIN_CLOCK = A7;     // Clock written to this pin
    const int WRITE_PIN_RESET = A9;     // Reset written to this pin
    
    const int BEGIN_DATA_COLLECTION = 3; //This is the number of clock cycles after the reset signal goes low before we start collecting data
    const int END_DATA_COLLECTION = COLUMNS_OF_DATA + BEGIN_DATA_COLLECTION;
    
    const uint8_t ADC_INTERRUPT_PRIORITY = 64;          // Needs to be smaller than 128; Default priority is 128, higher priorities are lower numbers, steps of sixteen
    const double CLOCK_PERIOD = 10.0;                   // Clock period in microseconds (10.0 = 10.0us)
    volatile bool resetPulseValue = true;               // Boolean to set reset high and low
    volatile uint16_t readCount;                        //Keep track of how many ADC conversions we do in a row
    volatile uint16_t num_times_called = 0;             //Used in ftm0_isr (ftm0_isr gets called every clock cycle, but things only happen on Reset rising and falling edges)
    volatile bool permission_to_write_to_sd;            //Only write to SD card when not collecting video data, so this value keeps track of when writing is allowed
    volatile bool begin_write_to_sd = false;            //Indicates when one of the buffers is full and data needs to be written to SD card
    volatile uint32_t bufferIndex = 1;                  //increment to fill buffers, let timing column be first
    volatile uint16_t bufvalue = 0;                     //ADC readings temporarily stored here
    ADC *adc = new ADC();                               // Create ADC object
    File myFile;                                        //Create SD file object
    
    //this function is very similar to the startPDB function in ADC_Module, but it is not the same
    void PDBstart(uint32_t freq) {
      if (!(SIM_SCGC6 & SIM_SCGC6_PDB)) { // setup PDB
        SIM_SCGC6 |= SIM_SCGC6_PDB; // enable pdb clock
      }
    
      if(freq>F_BUS) return; // too high
      if(freq<1) return; // too low
    
      // mod will have to be a 16 bit value
      // we detect if it's higher than 0xFFFF and scale it back accordingly.
      uint32_t mod = (F_BUS / freq);
    
      uint8_t prescaler = 0; // from 0 to 7: factor of 1, 2, 4, 8, 16, 32, 64 or 128
      uint8_t mult = 0; // from 0 to 3, factor of 1, 10, 20 or 40
    
      // if mod is too high we need to use prescaler and mult to bring it down to a 16 bit number
      const uint32_t min_level = 0xFFFF;
      if(mod>min_level) {
        if( mod < 2*min_level ) { prescaler = 1; }
        else if( mod < 4*min_level ) { prescaler = 2; }
        else if( mod < 8*min_level ) { prescaler = 3; }
        else if( mod < 10*min_level ) { mult = 1; }
        else if( mod < 16*min_level ) { prescaler = 4; }
        else if( mod < 20*min_level ) { mult = 2; }
        else if( mod < 32*min_level ) { prescaler = 5; }
        else if( mod < 40*min_level ) { mult = 3; }
        else if( mod < 64*min_level ) { prescaler = 6; }
        else if( mod < 128*min_level ) { prescaler = 7; }
        else if( mod < 160*min_level ) /*16*10*/ { prescaler = 4; mult = 1; }
        else if( mod < 320*min_level ) /*16*20*/ { prescaler = 4; mult = 2; }
        else if( mod < 640*min_level ) /*16*40*/ { prescaler = 4; mult = 3; }
        else if( mod < 1280*min_level ) /*32*40*/ { prescaler = 5; mult = 3; }
        else if( mod < 2560*min_level ) /*64*40*/ { prescaler = 6; mult = 3; }
        else if( mod < 5120*min_level ) /*128*40*/ { prescaler = 7; mult = 3; }
        else /*frequency too low*/ { return; }
    
        mod >>= prescaler;
        if(mult>0) {
          mod /= 10;
          mod >>= (mult-1);
        }
      }
      
      adc->adc0->setHardwareTrigger(); // trigger ADC with hardware
      PDB0_IDLY = 1; // the pdb interrupt happens when IDLY is equal to CNT+1
      PDB0_MOD = (uint16_t)(mod-1);
      //        software trigger    enable PDB     PDB interrupt  continuous mode load immediately
      PDB0_SC = PDB_SC_TRGSEL(8) | PDB_SC_PDBEN | PDB_SC_PDBIE | PDB_SC_CONT |   PDB_SC_LDMOD(0) | PDB_SC_PRESCALER(prescaler) | PDB_SC_MULT(mult) | PDB_SC_LDOK; // load all new values
      PDB0_SC &= ~PDB_SC_CONT;
      PDB0_CH0C1 = 0x0101; // enable pretrigger 0 (SC1A)
    }
    
    void setup() {
    
      pinMode(AD_VIDEO, INPUT);                     // Set up measurement pin as an input
      digitalWrite(AD_VIDEO, LOW);
      pinMode(WRITE_PIN_RESET, OUTPUT);               // Set up reset pin as an output
      //---------------------------------------------------------------------------------------------------------------------
      //SD card reader setup
      SD.begin();
      myFile = SD.open(FILENAME, O_WRITE | O_CREAT);
      //---------------------------------------------------------------------------------------------------------------------
      //ADC 0 setup
      adc->adc0->setReference(ADC_REFERENCE::REF_3V3);                      //Voltage reference 0 - 3.3 V
      adc->adc0->setAveraging(8);                                           //Set number of averages (ADC_SC3_AVGE = 8 which means averaging 8 samples)
      adc->adc0->setResolution(RESOLUTION);                                 //Set bits of resolution (ADC_CFG1_MODE = 11 if RESOLUTION == 16 which means single-ended 16-bit conversion)
      adc->adc0->setConversionSpeed(ADC_CONVERSION_SPEED::VERY_HIGH_SPEED); //Set conversion speed (ADC_CFG2_ADHSC = 1 which means high speed congiguration, ADC_CFG1_ADLPC = 0 which means normal power, ADC_CFG2_ADACKEN = 0 which means disable async. clk)
      adc->adc0->setSamplingSpeed(ADC_SAMPLING_SPEED::VERY_HIGH_SPEED);     //Set sampling speed (ADC_CFG1_ADLSMP = 0 which means short sample time)
      adc->adc0->enableInterrupts(adc0_isr, ADC_INTERRUPT_PRIORITY);        //Enables conversion complete interrupts (ADC_SC1_AIEN = 1 which means when COCO bit is high, interrupt is asserted)
      //---------------------------------------------------------------------------------------------------------------------
      //PIT module setup
      SIM_SCGC6 |= SIM_SCGC6_PIT;       //Enable the clock gate to PIT module -- for some reason this is needed even though we aren't using the PIT module
      //---------------------------------------------------------------------------------------------------------------------
      //FTM 0 setup
      analogWriteFrequency(WRITE_PIN_CLOCK, 100000);          //Set up PWM signal
      analogWrite(WRITE_PIN_CLOCK, 25);                       //Set up PWM signal to be high for 25 counts (period is 256 counts)
    
      FTM0_EXTTRIG = FTM_EXTTRIG_INITTRIGEN;                  //Enable external trigger on FTM0 Channel 0 for PDB -> corresponds to pin 23
      FTM0_CONF = FTM_CONF_NUMTOF(24);                        //Trigger interrupt once every 25 times the timer overflows (set to 24 because the setting is "trigger once, then don't trigger x times")
      FTM0_SC = FTM_SC_CLKS(1) | FTM_SC_PS(0) | FTM_SC_TOIE;  //update status register > sys clock | div by 1 | enable TOF interrupt
    
      NVIC_SET_PRIORITY(IRQ_FTM0, 96);                        //Needs to be smaller than 128; Default priority is 128, higher priorities are lower numbers, steps of sixteen
      NVIC_ENABLE_IRQ(IRQ_FTM0);                              //Enable FTM0 interrupt controller
      //---------------------------------------------------------------------------------------------------------------------
      //PDB setup
      adc->adc0->stopPDB();
      adc->adc0->startSingleRead(AD_VIDEO);     //call this to setup everything before the pdb starts, differential is also possible
      PDBstart(ADC_CONVERSION_FREQUENCY);       //calls function at the top of this file
      PDB0_IDLY = PDB_DELAY;                    //This delay was chosen to be 3/4 (or 5/8 if averaging) of clock period so PDB will trigger ADC 3/4 (or 5/8) of clock period after rising edge of clock
      PDB0_CH0DLY0 = PDB_DELAY;                 //This delay was chosen to be 3/4 (or 5/8 if averaging) of clock period so PDB will trigger ADC 3/4 (or 5/8) of clock period after rising edge of clock
      PDB0_SC |= PDB_SC_LDOK;                   //load everything we just set into the registers
      //---------------------------------------------------------------------------------------------------------------------
      
      //This line fixes mysterious problems we were having before; also present above
      PIT_MCR = 0x00;                              //Activates PIT module
    
      //Initialize values
      for (uint16_t i = 0; i < SAMPLES_BUFFER_SIZE; i++) {
          for (uint16_t j = 0; j < COLUMNS_OF_DATA; j++) {
              data_buffer_true[i][j] = 0;
              data_buffer_false[i][j] = 0;
        } 
      }
    }
    
    //SD card writes are performed in this function -- They have lower priority than everything else so they get interrupted when needed and paused when Video data is getting collected
    //If SD writes and Video data collection happen at the same time, Video data is very noisy (amplitude 20 mV when ADC resolution is set to 16-bit)
    void loop() {
    
      if (begin_write_to_sd) { //When either of the sample buffers is full
        begin_write_to_sd = false;
        
        if (!buffer_switch) { //Write from the _true buffer
          sample_index_true = 0;
          for (uint16_t sample = 0; sample < SAMPLES_BUFFER_SIZE; sample++) {
            for (uint16_t column = 0; column < COLUMNS_OF_DATA; column++) {
              while (!permission_to_write_to_sd) {}      //If ADC data is getting read, wait until it finishes
              myFile.print(data_buffer_true[sample][column]);
              myFile.print(", ");
            }
            while (!permission_to_write_to_sd) {}        //If ADC data is getting read, wait until it finishes
            myFile.println();
          }
        }
        else { //Write from the _false buffer
          sample_index_false = 0;
          for (uint16_t sample = 0; sample < SAMPLES_BUFFER_SIZE; sample++) {
            for (uint16_t column = 0; column < COLUMNS_OF_DATA; column++) {
              while (!permission_to_write_to_sd) {}     //If ADC data is getting read, wait until it finishes
              myFile.print(data_buffer_false[sample][column]);
              myFile.print(", ");
            }
            while (!permission_to_write_to_sd) {}      //If ADC data is getting read, wait until it finishes
            myFile.println();
          }
        }
    
        while (!permission_to_write_to_sd) {}         //If video data is getting read, wait until it finishes
        myFile.flush(); //Saves what was written
        
      } //End write conditional
          
    } //End loop()
    
    //This interrupt is called once per ADC conversion, triggered by completion of ADC conversion -- this interrupt has highest priority
    //Video data and MAX data are written to buffers in this function
    void adc0_isr() {
    
      bufvalue = ADC0_RA; //Read ADC0 data register
    
      if ((readCount > BEGIN_DATA_COLLECTION) && (readCount < END_DATA_COLLECTION)) {
        ++readCount;
        
        if (buffer_switch) {
          data_buffer_true[sample_index_true][bufferIndex] = bufvalue;
        }
        else {
          data_buffer_false[sample_index_false][bufferIndex] = bufvalue;
        }
        
        ++bufferIndex;
      }
      else if (readCount < END_DATA_COLLECTION) {
        ++readCount;
      }
      else if (readCount == END_DATA_COLLECTION) {
    
        this_time = micros();
        if (buffer_switch) {
          data_buffer_true[sample_index_true][0] = this_time - last_time; //Store time between rows in column 1
          ++sample_index_true;
          if (sample_index_true >= SAMPLES_BUFFER_SIZE) {
            buffer_switch = false;    //Begin filling _false buffer
            begin_write_to_sd = true; //Begin writing to SD card in main loop
          }
        }
        else {
          data_buffer_false[sample_index_false][0] = this_time - last_time;
          ++sample_index_false;
          if (sample_index_false >= SAMPLES_BUFFER_SIZE) {
            buffer_switch = true;     //Begin filling _true buffer
            begin_write_to_sd = true; //Begin writing to SD card in main loop
          }
        }
        bufferIndex = 1;
        last_time = this_time;
        ++readCount;
      }
    
    }
    
    //This interrupt gets called once per 25 Clock periods, triggered by Clock rising edge -- num_times_called is incremented until it is time for either a rising or falling edge on Reset signal
    //Reset signal is controlled in this function
    void ftm0_isr() {
    
      FTM0_SC &= ~FTM_SC_TOF;           //clear module interrupt
      ++num_times_called;
      if (!resetPulseValue && (num_times_called == 20)) {
        permission_to_write_to_sd = false;
      }
      else if (!resetPulseValue && (num_times_called > 33)) {                  // If Reset signal is high
        num_times_called = 0;
        digitalWriteFast(WRITE_PIN_RESET, resetPulseValue);
        resetPulseValue = !resetPulseValue;   //Switch from high to low, so invert boolean value
        readCount = 0;
      }
      else if (resetPulseValue && (num_times_called > 5)) {                    // Else if Reset signal is low
        num_times_called = 0;
        digitalWriteFast(WRITE_PIN_RESET, resetPulseValue);
        resetPulseValue = !resetPulseValue;   //Switch from low to high, so invert boolean value
        permission_to_write_to_sd = true;
      }
    }

  2. #2
    Senior Member
    Join Date
    May 2015
    Location
    USA
    Posts
    705
    Start simplifying until the issue goes away. For example, what happens if you only write values to memory (no SD card involvement)?

  3. #3
    I am not too familiar with the Teensy 3.5 ADC, but how many bits resolution does it have? Is it 12bits?

    When you get much above 10 bits you have to be very picky about your layout and grounding to reduce noise coupling.

    You say that when you cut the number of samples in half the period of the noise doubles? It looks to me that you may be getting some aliasing between your sampling rate and some stray signal.

  4. #4
    Quote Originally Posted by jonr View Post
    Start simplifying until the issue goes away. For example, what happens if you only write values to memory (no SD card involvement)?
    Thank you for your response! I just tested this, modifying the write routines in the loop so that the SD card is not written to nor flushed but instead printing column 1 of each row to serial. See code below. I then copied and pasted the serial monitor values to an Excel spreadsheet to obtain this graph.

    Click image for larger version. 

Name:	Screen Shot 2020-09-28 at 5.04.15 PM.jpg 
Views:	11 
Size:	131.7 KB 
ID:	21892

    Comparing this with a graph of data obtained the same way but with myFile.print() uncommented, it's quite clear that the SD card is the source of (at least most of) the noise!

    Click image for larger version. 

Name:	Screen Shot 2020-09-28 at 5.11.02 PM.jpg 
Views:	11 
Size:	95.5 KB 
ID:	21893

    So your nudge in the right direction was very helpful. Do you happen to know if there are reliable ways to reduce this sort of SD card print noise that also allow for continuous data collection? No worries if not, I have a better sense of where to go looking. Thanks again!

    Code:
        if (!buffer_switch) { //Write from the _true buffer
          sample_index_true = 0;
          for (uint16_t sample = 0; sample < SAMPLES_BUFFER_SIZE; sample++) {
            for (uint16_t column = 0; column < COLUMNS_OF_DATA; column++) {
              while (!permission_to_write_to_sd) {}      //If ADC data is getting read, wait until it finishes
              //myFile.print(data_buffer_true[sample][column]);
              //myFile.print(", ");
              if (column == 1) { //First column of not timing data
                Serial.println(data_buffer_false[sample][column]);
              }
            }
            while (!permission_to_write_to_sd) {}        //If ADC data is getting read, wait until it finishes
            //myFile.println();
          }
        }
    Last edited by davis_nanogroup; 09-29-2020 at 12:23 AM. Reason: Attempting to make it more clear who I am responding to.

  5. #5
    What is the bandwidth of the signals you are looking at?

    You could try a simple RC filter at the output of your sensors. Or maybe just a capacitor.

    You might also try some capacitors on the supply voltage line to the sensors. Maybe something like a 0.1uF ceramic in parallel with a 10uF electrolytic.

    And you could probably add some extra capacitors to the supply voltage on your processor.

  6. #6
    0.2 uF, 2 uF, and 20 uF decoupling capacitors between ADC input pin (Teensy pin A0) and ground
    Which ground? There is an analog ground pin provided.

  7. #7
    Quote Originally Posted by UhClem View Post
    Which ground? There is an analog ground pin provided.
    Analog and digital ground should be tied together someplace, but the capacitors should go between the ADC input and analog ground for best effect.

  8. #8
    Senior Member
    Join Date
    May 2015
    Location
    USA
    Posts
    705
    All your reading are proportional to the "3.3V" rail. Which will vary with load.

  9. #9
    Quote Originally Posted by OhioJim View Post
    Analog and digital ground should be tied together someplace,
    They are. The Teensy has some extra filtering between the digital and analog power pins.

  10. #10
    Senior Member
    Join Date
    Feb 2018
    Location
    Corvallis, OR
    Posts
    243
    Offhand, I'm not sure about the Teensy 3.5, but on the T3.6, you can have your ADC pins use an internal 1.2V reference. This reference will not be affected as much by the the current spikes from SD Card writes as is the 3.3V power supply. OTOH, you would have to reconfigure your sensor inputs to stay within the 0 to 1.2V range of the ADC when using this reference.

    Noise from SD card writes has been an issue for me for 5 to 10 years on various systems. The large current spikes usually occur when the SD card has to erase an internal flash block. That requires the card to turn on a voltage multiplier circuit to get the higher voltage needed for the erase process. It seems that the current spikes are at a very high frequency and can show up as RF interference on some sensors---like very high impedance piezoelectric oceanographic shear sensors. Shielding, filtering and good wiring practices are all part of the design process, particularly when you want the noise levels down to a few bits at 16-bit resolution.

  11. #11
    Senior Member
    Join Date
    Jul 2020
    Posts
    463
    An external ADC with separate analog and digital supply pins is recommended to get clean readings - internal ADC on
    a complex processor chip are always a compromize in performance (but often very handy when the requirements aren't
    too stringent). And of course if a separate ADC is on a breakout you need to check they bothered to use a separate linear
    regulator/precision voltage reference for the chip - otherwise you may not have gained much.

    You might get some mileage by running a separate 3.3V regulator to the SDcard from the MPU, keeping most of the
    noise on a separate circuit. This goes for ground wires as well, star-grounding applies so the ground current to the
    SDcard must not flow through the same wire as is used for the analog input you are sampling.

  12. #12
    Quote Originally Posted by OhioJim View Post
    Analog and digital ground should be tied together someplace, but the capacitors should go between the ADC input and analog ground for best effect.
    Previously I had the ADC input pin as A0, and so was using the GND closest to minimize the length of capacitor leads. Upon reading this I switched the ADC input pin to A9, closest to AGND, and moved the capacitors to AGND. I do not see much of an impact on the periodic nature of the noise, but it seems to have reduced the magnitude of it a bit? Will need more testing to be sure. Thanks for the suggestion!

    Click image for larger version. 

Name:	Screen Shot 2020-09-29 at 11.40.08 AM.png 
Views:	9 
Size:	544.3 KB 
ID:	21901

  13. #13
    Quote Originally Posted by jonr View Post
    All your reading are proportional to the "3.3V" rail. Which will vary with load.
    Thanks for pointing this out! I modified the code to use an external reference, then applied a reference voltage from a power supply (with capacitors) to the Teensy's AREF pin. My understanding is that this should account for the varying of the 3.3V rail, as the comparisons are now being made with respect to the applied reference? Here are my results:

    Code:
      //Was previously using ADC_REFERENCE::REF_3V3
      adc->adc0->setReference(ADC_REFERENCE::REF_EXT);
    First, while writing to the SD card:
    Click image for larger version. 

Name:	Screen Shot 2020-09-29 at 12.21.36 PM.jpg 
Views:	7 
Size:	79.1 KB 
ID:	21902

    Next, without writing to the SD card but with the same physical setup and reference voltage, for comparison:
    Click image for larger version. 

Name:	Screen Shot 2020-09-29 at 12.29.37 PM.jpg 
Views:	9 
Size:	76.4 KB 
ID:	21903

    I'm not super sure what to make of these. It seems to me that the large-magnitude periodic spikes may be not present on the version that writes to the SD card. However, zooming in on the graph, it looks like the bifurcated-looking nature of the readings is due do the same sort of periodic noise, though less severe:
    Click image for larger version. 

Name:	Screen Shot 2020-09-29 at 12.42.35 PM.jpg 
Views:	8 
Size:	65.0 KB 
ID:	21904

    This looks better than what I had before, so I'm happy for the change! What do you think could explain the persistence of the "split" readings, if no longer the reference voltage? Does the reference ground also vary as a result of SD card activity? Thank you very much for your help.

  14. #14
    Quote Originally Posted by mborgerson View Post
    Offhand, I'm not sure about the Teensy 3.5, but on the T3.6, you can have your ADC pins use an internal 1.2V reference. This reference will not be affected as much by the the current spikes from SD Card writes as is the 3.3V power supply. OTOH, you would have to reconfigure your sensor inputs to stay within the 0 to 1.2V range of the ADC when using this reference.
    I understand the 3.5 is also capable of a 1.2V internal reference. Unfortunately, the signal in my application is up to 2.5V, and reconfiguring is not a viable option. I hope to be able to reap the benefits of a more stable reference voltage by using an external reference voltage, applied to the AREF pin, that will not fluctuate when the SD card draws power (see my response to jonr). Thank you for your response!

  15. #15
    Senior Member
    Join Date
    May 2015
    Location
    USA
    Posts
    705
    > Does the reference ground also vary as a result of SD card activity?

    Typically yes. But generically (T3.5?), this can be addressed by using differential inputs - one attached to ground at the sensor. Treat both lines exactly the same - same routing, same filtering, etc. Or make sure that your sensor ground, reference ground, and AGND are all the same.
    Last edited by jonr; 09-29-2020 at 08:54 PM.

  16. #16
    As stated before, for best ADC performance you need an external ADC which you can isolate from all of the digital noise present on the MCU chip. That does make some effort to isolate the ADC system but it can never be perfect.

    I looked at your code and have some comments:

    Buffer space is far too small. You are mostly relying on whatever happens in the FAT library. I use large buffers (power of 2 times 512 bytes) as this works best with the SD card. You have lots of RAM available, use it. Using many 16KB buffers will also protect you from the periodic stalls present in writing SD cards. (The specification for the maximum FAT write time is 750ms.)

    With a 100KSPS rate I think that DMA is the better way to go for handling ADC data. Using interrupts you have a limited number of clock cycles to get your work done.

    It appears as though you are triggering the ADC at 100KHz and it is set for 16 bit mode and to average 8 samples. This exceeds the maximum conversion rate specification. Or perhaps the ADC is putting out data at 100,000/8 SPS. Hard to say. The version of ADC.h I looked at says that VERY_HIGH_SPEED may result in a clock rate that is out of specification. In any case your source must be very low impedance to support the very short sampling time.

    I think that for best performance, you need to use "SD.begin(BUILTIN_SDCARD)" which forces use of the nice fast 4 bit SD interface rather than the slower SPI version.

  17. #17
    Thank you, everyone, for all your great suggestions! Recently we took a closer look at the way we were writing to the SD card and discovered that, although we were using the SDFat library, we weren't utilizing it fully. We changed the type of our SD object from SdFatSdio to SdFatSdioEx and the type of our myFile object from File to SdFile. This seems to have significantly decreased the noise we were seeing every 20 seconds. This graph shows preliminary results.

    Click image for larger version. 

Name:	Illustration.png 
Views:	11 
Size:	67.7 KB 
ID:	21986

    Making these changes seems to have significantly decreased the noise we were seeing every 20 seconds. However, the ADC input is a constant voltage and, having reduced the 20-second noise, we now see a periodic 4-second noise which corresponds to each time we write to the SD card. We have staggered the SD card writes in our code so that we only write to the SD card during times we aren't collecting data (there is a period of time between the end of each row of data we collect and the beginning of the next row during which we can write to the SD card). There appears to be some sort of ringing in between times writing to the SD card that carries over into the signal we are converting. We will definitely try suggestions we have received to reduce this noise as well, but we would be happy to hear any further ideas!

  18. #18
    It's been a while, but I thought I would come back and update this thread with one other thing that we have done that has successfully decreased our noise. By switching our file from a .csv to .bin and writing to the file with myFile.write(buf, len) rather than myFile.print() we have dramatically decreased the time it takes to write our data, and therefore reduced the noise shown in our last post. Key to this approach was defining our data array as a union of a two dimensional array of uint16_t and a one dimensional array of bytes; this allows an entire data buffer to be written to the file with one call to write() rather than num_rows * num_columns calls to print(). A simple Python program allows us to convert the resulting binary file into a human-readable CSV, if desired.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •