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

Thread: Teensy 3.6 DAQ Project -Troubleshooting Help

  1. #1

    Teensy 3.6 DAQ Project -Troubleshooting Help

    Hi all,

    Right now, I'm trying to get my Teensy 3.6 up and running in the Arduino environment. My goal is the following:

    1. Be able to read 3 analog pins at a given time and write them all out to serial >500Hz
    2. Be able to read the state of 3 digital I/O pins (timing less critical)

    So far, I was able to modify analogReadIntervalTimer.ino to the point where I've got it reading A7,A8, and A9 and printing them out to serial monitor.


    Code:
    /*
        This example shows how to use the IntervalTimer library and the ADC library in the Teensy 3.0/3.1
        The three important objects are: the ADC, the (one or more) IntervalTimer and the same number of RingBuffer.
        The ADC sets up the internal ADC, you can change the settings as usual. Enable interrupts.
        The IntervalTimer (timerX) executes a function every 'period' microseconds.
        This function, timerX_callback, starts the desired ADC measurement, for example:
            startSingleRead, startSingleDifferential, startSynchronizedSingleRead, startSynchronizedSingleDifferential
        you can use the ADC0 or ADC1 (only for Teensy 3.1).
        When the measurement is done, the adc_isr is executed:
            - If you have more than one timer per ADC module you need to know which pin was measured.
            - Then you store/process the data
            - Finally, if you have more than one timer you can check whether the last measurement interruped a
              a previous one (using adc->adcX->adcWasInUse), and restart it if so.
              The settings of the interrupted measurement are stored in the adc->adcX->adc_config struct.
    */
    
    
    
    #include "ADC.h"
    #include "RingBuffer.h"
    // and IntervalTimer
    #include <IntervalTimer.h>
    
    //#include <SPI.h>
    //#include <SD.h>
    
    
    //const int chipSelect = BUILTIN_SDCARD;
    const int ledPin = LED_BUILTIN;
    
    const int readPin0 = A8;
    const int period0 = 500; // us
    
    const int readPin1 = A9;
    const int period1 = 500; // us
    
    const int readPin2 = A7;
    const int period2 = 500;//us
    
    const int readPeriod = 500; // us
    
    ADC *adc = new ADC(); // adc object
    
    IntervalTimer timer0, timer1,timer2; // timers
    
    RingBuffer *buffer0 = new RingBuffer; // buffers to store the values
    RingBuffer *buffer1 = new RingBuffer;
    RingBuffer *buffer2 = new RingBuffer;
    
    
    bool firstLoop=true;
    int startTimerValue0 = 0, startTimerValue1 = 0, startTimerValue2 = 0;
    
    void setup() {
    
        pinMode(0,INPUT);
        pinMode(ledPin, OUTPUT); // led blinks every loop
    
        pinMode(ledPin+1, OUTPUT); // timer0 starts a measurement
        pinMode(ledPin+2, OUTPUT); // timer1 starts a measurement
        pinMode(ledPin+3, OUTPUT); // adc0_isr, measurement finished for readPin0
        pinMode(ledPin+4, OUTPUT); // adc0_isr, measurement finished for readPin1
    
        pinMode(readPin0, INPUT);
        pinMode(readPin1, INPUT);
    
        Serial.begin(19200);
    
        delay(1000);
    
        ///// ADC0 ////
        // reference can be ADC_REFERENCE::REF_3V3, ADC_REFERENCE::REF_1V2 (not for Teensy LC) or ADC_REFERENCE::REF_EXT.
        //adc->setReference(ADC_REFERENCE::REF_1V2, ADC_0); // change all 3.3 to 1.2 if you change the reference to 1V2
    
        adc->setAveraging(1); // set number of averages
        adc->setResolution(12); // set bits of resolution
    
        // it can be any of the ADC_CONVERSION_SPEED enum: VERY_LOW_SPEED, LOW_SPEED, MED_SPEED, HIGH_SPEED_16BITS, HIGH_SPEED or VERY_HIGH_SPEED
        // see the documentation for more information
        // additionally the conversion speed can also be ADACK_2_4, ADACK_4_0, ADACK_5_2 and ADACK_6_2,
        // where the numbers are the frequency of the ADC clock in MHz and are independent on the bus speed.
        adc->setConversionSpeed(ADC_CONVERSION_SPEED::MED_SPEED); // change the conversion speed
        // it can be any of the ADC_MED_SPEED enum: VERY_LOW_SPEED, LOW_SPEED, MED_SPEED, HIGH_SPEED or VERY_HIGH_SPEED
        adc->setSamplingSpeed(ADC_SAMPLING_SPEED::VERY_LOW_SPEED); // change the sampling speed
    
        // always call the compare functions after changing the resolution!
        //adc->enableCompare(1.0/3.3*adc->getMaxValue(ADC_0), 0, ADC_0); // measurement will be ready if value < 1.0V
        //adc->enableCompareRange(1.0*adc->getMaxValue(ADC_0)/3.3, 2.0*adc->getMaxValue(ADC_0)/3.3, 0, 1, ADC_0); // ready if value lies out of [1.0,2.0] V
    
        // If you enable interrupts, notice that the isr will read the result, so that isComplete() will return false (most of the time)
        //adc->enableInterrupts(ADC_0);
    
    //////////////////////JC ADDED CODE FOR LOGGING///////////////////////////////////////////////////////////////////////
    
    /*
          
      Serial.print("Initializing SD card...");
    
    
    
      // see if the card is present and can be initialized:
      if (!SD.begin(chipSelect)) {
        Serial.println("Card failed, or not present");
        // don't do anything more:
        while (1);
      }
      Serial.println("card initialized.");
      Serial.println("Waiting 3 seconds to start data collection");
      delay(3000);
    
    */
    
    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    
    
        Serial.println("Starting Timers");
    
        // start the timers, if it's not possible, startTimerValuex will be false
    //    startTimerValue0 = timer0.begin(timer0_callback, period0);
        // wait enough time for the first timer conversion to finish (depends on resolution and averaging),
        // with 16 averages, 12 bits, and ADC_MED_SPEED in both sampling and conversion speeds it takes about 36 us.
       // delayMicroseconds(25); // if we wait less than 36us the timer1 will interrupt the conversion
        // initiated by timer0. The adc_isr will restart the timer0's measurement.
    
        // You can check with an oscilloscope:
        // Pin 14 corresponds to the timer0 initiating a measurement
        // Pin 15 the same for the timer1
        // Pin 16 is the adc_isr when there's a new measurement on readpin0
        // Pin 17 is the adc_isr when there's a new measurement on readpin1
    
        // Timer0 starts a comversion and 25 us later timer1 starts a new one, "pausing" the first, about 36 us later timer1's conversion
        // is done, and timer0's is restarted, 36 us later timer0's conversion finishes. About 14 us later timer0 starts a new conversion again.
        // (times don't add up to 120 us because the timer_callbacks and adc_isr take time to execute, about 2.5 us and 1 us, respectively)
        // so in the worst case timer0 gets a new value in about twice as long as it would take alone.
        // if you change the periods, make sure you don't go into a loop, with the timers always interrupting each other
        startTimerValue0 = timer0.begin(timer0_callback, period0);
        startTimerValue1 = timer1.begin(timer1_callback, period1);
        startTimerValue2 = timer2.begin(timer2_callback, period2);/////////Added
        
        adc->enableInterrupts(ADC_0);
        
        Serial.println("Timers started");
    
        delay(500);
    
    }
    
    int value = 0;
    char c=0;
    
    
    void loop() {
    
        if(startTimerValue0==false) {
                Serial.println("Timer0 setup failed");
        }
        /*
        if(startTimerValue1==false) {
                Serial.println("Timer1 setup failed");
        }
    
        if(startTimerValue2==false) {
                Serial.println("Timer2 setup failed");
        }
        */
        
        if(!buffer0->isEmpty()) { // read the values in the buffer
            Serial.print("Read pin 0: ");
            Serial.println(buffer0->read());//*3.3/adc->getMaxValue());
            
    //////////////////JC ADDED CODE FOR SD CARD LOGGING       ///////////////////////////////////////////////////////////////
    /*
          String dataString = "";
    
      // read sensor 0 from buffer
      {
          dataString+=buffer0->read();//*3.3/adc->getMaxValue();
          dataString += "-0";
        }
      
    
      // open the file. note that only one file can be open at a time,
      // so you have to close this one before opening another.
      File dataFile = SD.open("JimLog.txt", FILE_WRITE);
    
      if(firstLoop){
        dataFile.println("New Trial Ts="+String (period0)+"us");
        firstLoop=false;
      }
      // if the file is available, write to it:
      if (dataFile) {
        dataFile.println(dataString);
        dataFile.close();
        Serial.println(dataString);
      }
    
        else {
    //    Serial.println("error opening datalog.txt");
      }
    */
      ///////////////////////////////////////////////////////////////////////////////////////////////////////////
            
        }
        if(!buffer1->isEmpty()) { // read the values in the buffer
            Serial.print("Read pin 1: ");
            Serial.println(buffer1->read());//*3.3/adc->getMaxValue());
            //Serial.println("New value!");
    
            //////////////////JC ADDED CODE FOR SD CARD LOGGING   ///////////////////////////////////////////////////////////////
    /*
          String dataString = "";
    
      // read sensor 0 from buffer
      {
          dataString+=buffer0->read();//*3.3/adc->getMaxValue();
          dataString += "-1";
        }
      
    
      // open the file. note that only one file can be open at a time,
      // so you have to close this one before opening another.
      File dataFile = SD.open("JimLog.txt", FILE_WRITE);
    
      if(firstLoop){
        dataFile.println("New Trial Ts="+String (period0)+"us");
        firstLoop=false;
      }
      // if the file is available, write to it:
      if (dataFile) {
        dataFile.println(dataString);
        dataFile.close();
        Serial.println(dataString);
      }
    
        else {
    //    Serial.println("error opening datalog.txt");
      }
    */
      ///////////////////////////////////////////////////////////////////////////////////////////////////////////
        }
    
        if(!buffer2->isEmpty()) { // read the values in the buffer
            Serial.print("Read pin 2: ");
            Serial.println(buffer2->read());//*3.3/adc->getMaxValue());
            
    //////////////////JC ADDED CODE FOR SD CARD LOGGING       ///////////////////////////////////////////////////////////////
    /*
          String dataString = "";
    
      // read sensor 0 from buffer
      {
          dataString+=buffer0->read();//*3.3/adc->getMaxValue();
          dataString += "-0";
        }
      
    
      // open the file. note that only one file can be open at a time,
      // so you have to close this one before opening another.
      File dataFile = SD.open("JimLog.txt", FILE_WRITE);
    
      if(firstLoop){
        dataFile.println("New Trial Ts="+String (period0)+"us");
        firstLoop=false;
      }
      // if the file is available, write to it:
      if (dataFile) {
        dataFile.println(dataString);
        dataFile.close();
        Serial.println(dataString);
      }
    
        else {
    //    Serial.println("error opening datalog.txt");
      }
    */
      ///////////////////////////////////////////////////////////////////////////////////////////////////////////
            
        }
    
        if (Serial.available()) {
            c = Serial.read();
            
            if(firstLoop){
              timer1.end();
              delay(2000);
              startTimerValue1 = timer1.begin(timer1_callback, period1);
              delay(2000); 
              firstLoop=false;     
            }
            
            if(c=='s') { // stop timer
                Serial.println("Stop timer1");
                timer1.end();
            } else if(c=='r') { // restart timer
                Serial.println("Restart timer1");
                startTimerValue1 = timer1.begin(timer1_callback, period1);
            } else if(c=='p') { // restart timer
                Serial.print("isContinuous: ");
                Serial.println(adc->adc0->isContinuous());
            }
        }
    
        
        //digitalWriteFast(LED_BUILTIN, !digitalReadFast(LED_BUILTIN) );
    
        delayMicroseconds(readPeriod);
    }
    
    
    // This function will be called with the desired frequency
    // start the measurement
    // in my low-res oscilloscope this function seems to take 1.5-2 us.
    void timer0_callback(void) {
    
        digitalWriteFast(ledPin+1, HIGH);
    
        adc->startSingleRead(readPin0, ADC_0); // also: startSingleDifferential, analogSynchronizedRead, analogSynchronizedReadDifferential
    
        digitalWriteFast(ledPin+1, LOW);
        //digitalWriteFast(ledPin+1, !digitalReadFast(ledPin+1));
    
    
    }
    
    // This function will be called with the desired frequency
    // start the measurement
    void timer1_callback(void) {
    
        digitalWriteFast(ledPin+2, HIGH);
    
        adc->startSingleRead(readPin1, ADC_0);
    
        digitalWriteFast(ledPin+2, LOW);
    
    }
    
    // This function will be called with the desired frequency
    // start the measurement
    void timer2_callback(void) {
    
        //digitalWriteFast(ledPin+2, HIGH);
    
        adc->startSingleRead(readPin2, ADC_0);
    
        //digitalWriteFast(ledPin+2, LOW);
    
    }
    
    // when the measurement finishes, this will be called
    // first: see which pin finished and then save the measurement into the correct buffer
    void adc0_isr() {
    
        uint8_t pin = ADC::sc1a2channelADC0[ADC0_SC1A&ADC_SC1A_CHANNELS]; // the bits 0-4 of ADC0_SC1A have the channel
    
        // add value to correct buffer
        if(pin==readPin0) {
            digitalWriteFast(ledPin+3, HIGH);
            buffer0->write(adc->readSingle());
            digitalWriteFast(ledPin+3, LOW);
        } else if(pin==readPin1) {
            digitalWriteFast(ledPin+4, HIGH);
            buffer1->write(adc->readSingle());
            if(adc->adc0->isConverting()) {
                digitalWriteFast(LED_BUILTIN, 1);
            }
            digitalWriteFast(ledPin+4, LOW);
        } 
          else if(pin==readPin2){
            buffer2->write(adc->readSingle());
          }
          else { // clear interrupt anyway
            adc->readSingle();
        }
    
        // restore ADC config if it was in use before being interrupted by the analog timer
        if (adc->adc0->adcWasInUse) {
            // restore ADC config, and restart conversion
            adc->adc0->loadConfig(&adc->adc0->adc_config);
            // avoid a conversion started by this isr to repeat itself
            adc->adc0->adcWasInUse = false;
        }
    
    
        //digitalWriteFast(ledPin+2, !digitalReadFast(ledPin+2));
    
    }
    
    
    //Original ISRs
    
    /*
     * 
     * 
     * 
     * 
     * 
    
     * 
     * 
     * 
     * 
     * 
     * 
     * 
     * 
     * 
     * 
     * // This function will be called with the desired frequency
    // start the measurement
    // in my low-res oscilloscope this function seems to take 1.5-2 us.
    void timer0_callback(void) {
    
        digitalWriteFast(ledPin+1, HIGH);
    
        adc->startSingleRead(readPin0, ADC_0); // also: startSingleDifferential, analogSynchronizedRead, analogSynchronizedReadDifferential
        adc->startSingleRead(readPin1,ADC_0);
        adc->startSingleRead(readPin2,ADC_0);
        digitalWriteFast(ledPin+1, LOW);
        //digitalWriteFast(ledPin+1, !digitalReadFast(ledPin+1));
    
    
    }
    
    // This function will be called with the desired frequency
    // start the measurement
    void timer1_callback(void) {
    
        digitalWriteFast(ledPin+2, HIGH);
    
        adc->startSingleRead(readPin1, ADC_0);
    
        digitalWriteFast(ledPin+2, LOW);
    
    }
    
    // This function will be called with the desired frequency
    // start the measurement
    void timer2_callback(void) {
    
        //digitalWriteFast(ledPin+2, HIGH);
    
        adc->startSingleRead(readPin2, ADC_0);
    
        //digitalWriteFast(ledPin+2, LOW);
    
    }
    
    // when the measurement finishes, this will be called
    // first: see which pin finished and then save the measurement into the correct buffer
    void adc0_isr() {
    
        uint8_t pin = ADC::sc1a2channelADC0[ADC0_SC1A&ADC_SC1A_CHANNELS]; // the bits 0-4 of ADC0_SC1A have the channel
    
        // add value to correct buffer
        if(pin==readPin0) {
            digitalWriteFast(ledPin+3, HIGH);
            buffer0->write(adc->readSingle());
            digitalWriteFast(ledPin+3, LOW);
        } else if(pin==readPin1) {
            digitalWriteFast(ledPin+4, HIGH);
            buffer1->write(adc->readSingle());
            if(adc->adc0->isConverting()) {
                digitalWriteFast(LED_BUILTIN, 1);
            }
            digitalWriteFast(ledPin+4, LOW);
        } 
          else if(pin==readPin2){
            buffer2->write(adc->readSingle());
          }
          else { // clear interrupt anyway
            adc->readSingle();
        }
    
        // restore ADC config if it was in use before being interrupted by the analog timer
        if (adc->adc0->adcWasInUse) {
            // restore ADC config, and restart conversion
            adc->adc0->loadConfig(&adc->adc0->adc_config);
            // avoid a conversion started by this isr to repeat itself
            adc->adc0->adcWasInUse = false;
        }
    
    
        //digitalWriteFast(ledPin+2, !digitalReadFast(ledPin+2));
    
    }
     * 
     * 
     * /
     */
    I input a function generator running at 40Hz across all three pins and shorted them together as a test.

    What I notice is that there are a lot of steps in the serial monitor output here:
    Click image for larger version. 

Name:	Capture.jpg 
Views:	37 
Size:	45.0 KB 
ID:	12189
    I assume that the issue is that my ADC conversions are getting interrupted by other ADC requests. I've tried lowering the conversion/sampling speed however that does not eliminate the issue. Anyone have recommendations on how to resolve the issue or alternate approaches?

  2. #2
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    16,279
    Oh my, that's a pretty incredibly complicated mess.

    First, please try something very simple, like this:

    Code:
    const int readPin0 = A8;
    const int readPin1 = A9;
    const int readPin2 = A7;
    
    elapsedMicros usec;
    
    void setup() {
      analogReadResolution(12);
      usec = 0;
    }
    
    void loop() {
      if (usec >= 500) {
        usec -= 500;
        Serial.print("Read pin 0: ");
        Serial.println(analogRead(readPin0));
        Serial.print("Read pin 1: ");
        Serial.println(analogRead(readPin1));
        Serial.print("Read pin 2: ");
        Serial.println(analogRead(readPin2));
      }
    }
    I ran this just now with a 40 Hz sine wave on those 3 pins. Here's what I see in the serial plotted (or at least this is a screengrab... the actual window updates very fast)

    Click image for larger version. 

Name:	sc.png 
Views:	33 
Size:	29.4 KB 
ID:	12191

    I looked through the timer code. There's a *lot* going on. Sorry, I don't have time to get into all that code. But my general impression is each timer is causing the ADC library to start a new conversion while the ADC is very likely still busy doing a prior conversion. There's a lot of complicated things that can go wrong.

    If your program doesn't need to also do something other work while the ADC makes the conversions, I'd highly recommend keeping things simple. Simple is good.

  3. #3
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    16,279
    If you need to use the IntervalTimer, which really only makes sense if you add other code in loop() which takes enough time that polling an elapsedMicros variable would cause some samples to be taken late, here's a copy of the original code edited to just use analogRead and wait within the interrupt. Not idea, but much simpler and easier to get working.

    Code:
    #include "RingBuffer.h"
    #include <IntervalTimer.h>
    
    const int ledPin = 13;
    
    const int readPin0 = A8;
    const int readPin1 = A9;
    const int readPin2 = A7;
    
    const int readPeriod = 500; // us
    
    IntervalTimer timer0; // timers
    
    RingBuffer *buffer0 = new RingBuffer; // buffers to store the values
    RingBuffer *buffer1 = new RingBuffer;
    RingBuffer *buffer2 = new RingBuffer;
    
    bool firstLoop = true;
    int startTimerValue0 = 0;
    
    void setup() {
    
      pinMode(0, INPUT);
      pinMode(ledPin, OUTPUT); // led blinks every loop
    
      pinMode(ledPin + 1, OUTPUT); // timer0 starts a measurement
      pinMode(ledPin + 2, OUTPUT); // timer1 starts a measurement
      pinMode(ledPin + 3, OUTPUT); // adc0_isr, measurement finished for readPin0
      pinMode(ledPin + 4, OUTPUT); // adc0_isr, measurement finished for readPin1
    
      pinMode(readPin0, INPUT_DISABLE);
      pinMode(readPin1, INPUT_DISABLE);
      pinMode(readPin2, INPUT_DISABLE);
    
      analogReadResolution(12);
    
      Serial.begin(19200);
    
      delay(1000);
    
      Serial.println("Starting Timers");
    
      startTimerValue0 = timer0.begin(timer0_callback, readPeriod);
    
      Serial.println("Timers started");
    
      delay(500);
    }
    
    
    void loop() {
      int value = 0;
      char c = 0;
    
      if (startTimerValue0 == false) {
        Serial.println("Timer0 setup failed");
      }
    
      if (!buffer0->isEmpty()) { // read the values in the buffer
        Serial.print("Read pin 0: ");
        Serial.println(buffer0->read());//*3.3/adc->getMaxValue());
      }
      if (!buffer1->isEmpty()) { // read the values in the buffer
        Serial.print("Read pin 1: ");
        Serial.println(buffer1->read());//*3.3/adc->getMaxValue());
      }
    
      if (!buffer2->isEmpty()) { // read the values in the buffer
        Serial.print("Read pin 2: ");
        Serial.println(buffer2->read());//*3.3/adc->getMaxValue());
      }
    
      if (Serial.available()) {
        c = Serial.read();
        if (firstLoop) {
          timer0.end();
          delay(2000);
          startTimerValue0 = timer0.begin(timer0_callback, readPeriod);
          delay(2000);
          firstLoop = false;
        }
        if (c == 's') { // stop timer
          Serial.println("Stop timer1");
          timer0.end();
        } else if (c == 'r') { // restart timer
          Serial.println("Restart timer1");
          startTimerValue0 = timer0.begin(timer0_callback, readPeriod);
        } else if (c == 'p') { // restart timer
          Serial.print("isContinuous: ");
        }
      }
    }
    
    
    // This function will be called with the desired frequency
    // start the measurement
    // in my low-res oscilloscope this function seems to take 1.5-2 us.
    void timer0_callback(void) {
      buffer0->write(analogRead(readPin0));
      buffer1->write(analogRead(readPin1));
      buffer2->write(analogRead(readPin2));
    }

  4. #4
    Member
    Join Date
    Mar 2017
    Location
    Oakland, CA, USA
    Posts
    26
    Curious what program you used to plot those? Some quickie Processing thing?

  5. #5
    Junior Member
    Join Date
    May 2017
    Location
    Austin, Texas, United States
    Posts
    1
    The plot looks like the Serial Plotter of the Arduino IDE (Tools >> Serial Plotter).

Posting Permissions

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