Teensy 3.1: Interference between SDwrite and I2C?

Status
Not open for further replies.

Chopsticks

Active member
While i'm busy with my project for measuring ECG signals, there are some issues which came across.

I've managed to write 2 analog signals synchronous to the sdcard.

But at this very moment:
a) I'm using an usb-to-I2C adapter which is connected to my Teensy and digital potentiometer (parallel).
b) With LabVIEW i can adjust the resistor value on the digital potentiometer by using the usb-to-i2c module.
c) I'm using an usb-6008 tool from NI to measure/analyze the analog values that comes out of the sensor. I haven't used the teensy I2C yet but i've managed to control this module by LabVIEW

The problem:
When I upload the code which measures the adc values and write the values to an SD-card, there is noise on the output of the sensor. Keep in mind, the output is determined by the digital potentiometer (adjustable gain amplifier).
The digital potentiometer is connected with the i2c-bus on teensy and the usb-to-i2c module.

Anyhow... when i upload an example of the teensy's i2c_t3 library (slave). i don't get those interference on my output signal...

So i thought it could be the settings? So i adjusted my code where the i2c slave settings has been included. But even in this scenario, the results are still worse..

I quess this has something to do with the MISO/MOSI/CS/SCLK, which interferes the i2c output?

The question:
1) What could the problem be?
2) Is it possible to use i2c and write to sd card at the same time? without interfering each other?

Code File:
Code:
/* Example for synchonized measurements using both ADC present in Teensy 3.1
*  You can change the number of averages, bits of resolution and also the comparison value or range.
*/

//Inclusing Libraries
#include <ADC.h>
#include <SD.h>
#include <string.h>
#include <Time.h>  

// I2C - Libraries
#include <i2c_t3.h>
#ifdef I2C_DEBUG
    #include <rbuf.h> // linker fix
#endif

//Define ADC Inputs
#define ADC_0 1
#define ADC_1 1

// I2C - Command definitions
#define WRITE 0x10
#define READ  0x20

// I2C - Function prototypes
void receiveEvent(size_t len);
void requestEvent(void);

// I2C - Memory
#define MEM_LEN 256
uint8_t mem[MEM_LEN];
uint8_t cmd;
size_t addr;

//Teensy 3.1 has the LED on pin 13
//const int ledPin = 13;
const int readPin0 = A2;  //ADC read A2
const int readPin1 = A3;  //ADC read A3
const int chipSelect = 10;  //CS(SS) select SD-Card


ADC *adc = new ADC(); // adc object

File myFile;
char filename[] = "00000000.txt";

void setup() 
{
  setSyncProvider(getTeensy3Time);
  //pinMode(ledPin, OUTPUT);
  pinMode(readPin0, INPUT);  //Read analog signal pin1
  pinMode(readPin1, INPUT);  //Read analog signal pin2

  //Set Serial
  Serial.begin(115200);
  //  while (!Serial);  // Wait for Arduino Serial Monitor to open
    
  //Set Time
  if (timeStatus()!= timeSet) {
    Serial.println("Unable to sync with the RTC");
  } else {
    Serial.println("RTC has set the system time");
  }
  //Set ADC (Averaging and Resolution)  
  adc->setAveraging(16, ADC_0); // set number of averages
  adc->setResolution(12), ADC_0; // set bits of resolution

  adc->setAveraging(16, ADC_1); // set number of averages
  adc->setResolution(12, ADC_1); // set bits of resolution

  // always call the compare functions after changing the resolution!
  //adc->enableCompare(1.0/3.3*adc.getMaxValue(), 0, ADC_0); // measurement will be ready if value < 1.0V
  //adc->enableCompareRange(1.0*adc.getMaxValue(ADC_1)/3.3, 2.0*adc.getMaxValue(ADC_1)/3.3, 0, 1, ADC_1); // ready if value lies out of [1.0,2.0] V

  //delay(100);
  Serial.println("end setup");
  Serial.println("");
  
  //Initializing SD Card for writing
  Serial.print("Initializing SD card...");
  // On the Ethernet Shield, CS is pin 4. It's set as an output by default.
  // Note that even if it's not used as the CS pin, the hardware SS pin 
  // (10 on most Arduino boards, 53 on the Mega) must be left as an output 
  // or the SD library functions will not work. 
  pinMode(chipSelect, OUTPUT);
   
  if (!SD.begin(chipSelect)) {
    Serial.println("Initialization failed!");
    return;
  }
  Serial.println("Initialization done.");
 
  getFileName();
  createFileName(); 
 
  // Setup for Slave mode, address 0x44, pins 18/19, external pullups, 400kHz
    Wire.begin(I2C_SLAVE, 0x44, I2C_PINS_18_19, I2C_PULLUP_EXT, I2C_RATE_100);

    // init vars
    cmd = 0;
    addr = 0;
    for(size_t i=0; i < MEM_LEN; i++)
        mem[i] = 0;

    // register events
    Wire.onReceive(receiveEvent);
    Wire.onRequest(requestEvent); 
}

//int value0 = 0;
//int value1 = 0;
ADC::Sync_result result;

void loop() {
  if (Serial.available()) {
     time_t t = processSyncMessage();
     if (t != 0) {
     Teensy3Clock.set(t); // set the RTC
     setTime(t);
     }
  }
    //delay(5000);  
  
  myFile = SD.open(filename, FILE_WRITE);
 
//ADC values   
  result = adc->analogSynchronizedRead(readPin0, readPin1);
  int ADCvalue_0 = result.result_adc0;
  int ADCvalue_1 = result.result_adc1;
  // open the file. note that only one file can be open at a time,
  // so you have to close this one before opening another.
  
  
  if( (result.result_adc0 !=ADC_ERROR_VALUE) && (result.result_adc1 !=ADC_ERROR_VALUE) ) {

    // the test results below were obtained commenting out all Serial.print*() and the delay() lines
    // the decimal point is a comma, print.cpp has been changed for this. instead of a point, a comma has been used  
      //GPIOC_PTOR = 1<<5;
      Serial.print("Time: ");
      digitalClockDisplay_WriteToSerial();
        Serial.print("\t(ADC0): ");  
        Serial.print(ADCvalue_0*3.3/adc->adc0->getMaxValue(), DEC);
        Serial.print("\tA3(ADC1): ");
        Serial.println(ADCvalue_1*3.3/adc->adc1->getMaxValue(), DEC);
      if (myFile) {   
          (digitalClockDisplay_WriteToSD());
          myFile.print("\t");
          myFile.print(10*ADCvalue_0*3.3/adc->adc0->getMaxValue(), DEC);
          myFile.print("\t");
          myFile.println(10*ADCvalue_1*3.3/adc->adc1->getMaxValue(), DEC);
    
      //close the file:
      myFile.close();
      //Serial.println("done.");

  } else {
 // if the file didn't open, print an error:
    Serial.println("Error 1: Can't open file!");
  } 
  } else {
      Serial.println("Error 2: Comparison failed");
  }
  

    GPIOC_PTOR = 1<<5;


  delay(4);
}

void readFile(){  
      // re-open the file for reading:
      myFile = SD.open(filename);
      if (myFile) {
          Serial.println("Last file:");
    
       // read from the file until there's nothing else in it:
      while (myFile.available()) {
    	  Serial.write(myFile.read());
       }
 
      // close the file:
      myFile.close();
      } else {
      	// if the file didn't open, print an error:
        Serial.println("Error 3: Can't open file");
      }
}      


void getFileName(){
filename[0] = (month()/10)%10 + '0'; //To get 1st digit from year()      '2';
filename[1] = month()%10 + '0'; //To get 2nd digit from year()      '0';
filename[2] = (day()/10)%10 + '0'; //To get 3rd digit from year()      '1';
filename[3] = day()%10 + '0'; //To get 4th digit from year()      '4';
filename[4] = (hour()/10)%10 + '0'; //To get 1st digit from month()      '0';
filename[5] = hour()%10 + '0'; //To get 2nd digit from month()      '5';
filename[6] = (minute()/10)%10 + '0'; //To get 1st digit from day()      '1';
filename[7] = minute()%10 + '0'; //To get 2nd digit from day()      '6';
/*filename[0] = (year()/1000)%10 + '0'; //To get 1st digit from year()      '2';
filename[1] = (year()/100)%10 + '0'; //To get 2nd digit from year()      '0';
filename[2] = (year()/10)%10 + '0'; //To get 3rd digit from year()      '1';
filename[3] = year()%10 + '0'; //To get 4th digit from year()      '4';
filename[4] = (month()/10)%10 + '0'; //To get 1st digit from month()      '0';
filename[5] = month()%10 + '0'; //To get 2nd digit from month()      '5';
filename[6] = (day()/10)%10 + '0'; //To get 1st digit from day()      '1';
filename[7] = day()%10 + '0'; //To get 2nd digit from day()      '6';        */                  
filename[8] = '.';
filename[9] = 't';
filename[10] = 'x';
filename[11] = 't';
Serial.println(filename);
}

void createFileName(){
//Check file name exist?
if (SD.exists(filename)) {
Serial.println("exists.");
}
else {
Serial.println("Filename doesn't exist.");
Serial.println("Creating a new file");
Serial.println(filename);
myFile = SD.open(filename, FILE_WRITE);
  myFile.print("Date: ");
  myFile.print(day());
  myFile.print("-");
  myFile.print(month());
  myFile.print("-");
  myFile.print(year()); 
  myFile.print("\t");
   myFile.print(hour());
  myFile.print(":");
  myFile.print(minute());
  myFile.print(":");
  myFile.println(second());
myFile.close();
}
}

void digitalClockDisplay_WriteToSerial() {
  // digital clock display of the time
  Serial.print(day());
  Serial.print("-");
  Serial.print(month());
  Serial.print("-");
  Serial.print(year()); 
  Serial.print("\t");
  Serial.print(hour());
  printDigits(minute());
  printDigits(second());
  //Serial.println("\t"); 
}

void digitalClockDisplay_WriteToSD() {
  // digital clock display of the time
//  myFile.print(day());
//  myFile.print("-");
//  myFile.print(month());
//  myFile.print("-");
//  myFile.print(year()); 
//  myFile.print("\t");
  myFile.print(hour());
  myFile.print(":");
  myFile.print(minute());
  myFile.print(":");
  myFile.print(second());
  myFile.print(",");
  myFile.print(millis());
 //Serial.println("\t"); 
}

time_t getTeensy3Time()
{
  return Teensy3Clock.get();
}

/*  code to process time sync messages from the serial port   */
#define TIME_HEADER  "T"   // Header tag for serial time sync message

unsigned long processSyncMessage() {
  unsigned long pctime = 0L;
  const unsigned long DEFAULT_TIME = 1357041600; // Jan 1 2013 

  if(Serial.find(TIME_HEADER)) {
     pctime = Serial.parseInt();
     return pctime;
     if( pctime < DEFAULT_TIME) { // check the value is a valid time (greater than Jan 1 2013)
       pctime = 0L; // return 0 to indicate that the time is not valid
     }
  }
  return pctime;
}

void printDigits(int digits){
  // utility function for digital clock display: prints preceding colon and leading 0
  Serial.print(":");
  if(digits < 10)
    Serial.print('0');
  Serial.print(digits);
}





//
// handle Rx Event (incoming I2C request/data)
//
void receiveEvent(size_t len)
{
    if(Wire.available())
    {
        // grab command
        cmd = Wire.readByte();
        switch(cmd)
        {
        case WRITE:
            addr = Wire.readByte();                // grab addr
            while(Wire.available())
                if(addr < MEM_LEN)                 // drop data beyond mem boundary
                    mem[addr++] = Wire.readByte(); // copy data to mem
                else
                    Wire.readByte();               // drop data if mem full
            break;

        case READ:
            addr = Wire.readByte();                // grab addr
            break;
        }
    }
}

//
// handle Tx Event (outgoing I2C data)
//
void requestEvent(void)
{
    switch(cmd)
    {
    case READ:
        Wire.write(&mem[addr], MEM_LEN-addr); // fill Tx buffer (from addr location to end of mem)
        break;
    }
}

//
// print I2C status
//
void print_i2c_status(void)
{
    switch(Wire.status())
    {
    case I2C_WAITING:  Serial.print("I2C waiting, no errors\n"); break;
    case I2C_ADDR_NAK: Serial.print("Slave addr not acknowledged\n"); break;
    case I2C_DATA_NAK: Serial.print("Slave data not acknowledged\n"); break;
    case I2C_ARB_LOST: Serial.print("Bus Error: Arbitration Lost\n"); break;
    default:           Serial.print("I2C busy\n"); break;
    }
}

https://github.com/WildeSkippy/Sourcefiles/
"Teensy 3.1 - ADC Measurement + SD Write + I2C"

Anyone can help with this?

Greetings!
Klaas
 
Last edited:
Oh so many unknowns in this one. I have no idea what this "usb-to-I2C adapter" might be. I also don't use LabView.

I can tell you the most common cause of measurement noise is generically reffered to as "ground loops". The problem occurs when something unrelated to the analog signal, perhaps a SD card or USB interface, draws significant current, where some of that current passes through the same ground wires used to connect whatever analog signal you're measuring.

On a schematic, everything has a ground symbol and we we just assume they're all the same perfect zero volts. But in reality, ground wires have resistance and inductance. Any current follow through those wires causes a small voltage. Even if the wire were a superconductor, it would still have inductance due to the physical size and its proximity to whatever other wire is conducting the current.

Nobody knows? :)

Yeah, pretty much. These analog signal quality issues are tough to solve, even when you've got everything right there on a test bench with lots of great equipment. Over the internet, on a forum post, and lacking specific connection info like a schematic, wiring diagram, or a photo, even guessing is quite difficult.

But I would guess you're seeing some sort of ground loop problem, because that's what these things almost always turn out to be.

The other type of issue that sometimes comes up is changing reference voltage, which might be an issue since your code doesn't appear to be using the internal 1.2V reference... but you might have a shunt reference connected to AREF? (again, guesswork since I can't see your circuit....) If you're using 3.3V as your ADC reference, and you're measuring absolute voltages, of course you should switch to the 1.2V internal reference or connect a shunt ref like a LM336 to AREF, so your measurements are all based on a stable reference. Using 3.3V power as a reference only makes sense for ratiometric measurements... but then, perhaps that is what you're measuring? You did mention an e-pot. But my guess it you've got very low voltage signals, since this is an ECG. Can you see how unclear my mental image of your setup is?
 
Ahhh, would be smart to post some pictures / links next time.. ghehe

The i2c adapter can be found in this link: http://www.robot-electronics.co.uk/htm/usb_i2c_tech.htm
schematics: Project ECG.jpg

there are two sensor outputs, which both enters the first stage (S1_GAI or S2_GAI). The outsignal (S1_GAO or S2_GAO) enters the levelshifter (S1_LSI and S2_LSI). S1_LSO and S2_LSO are both connected to S1_OUT_TEENSY_ADC /S2_OUT_TEENSY_ADC.

In the schematics AGND isn't connected. I left that pin open because i wasn't sure what effect it actually has when it's connected to the ground(usb ground). I used a wire attached to the pin to connect it to the ground.

The problem still occurs even when agnd is connnected to ground and not. Using labVIEW isn't the problem, because i read the signals trough the NI_USB-6008 tool. Which is actually separated from the teensy.

The other type of issue that sometimes comes up is changing reference voltage, which might be an issue since your code doesn't appear to be using the internal 1.2V reference... but you might have a shunt reference connected to AREF? If you're using 3.3V as your ADC reference, and you're measuring absolute voltages, of course you should switch to the 1.2V internal reference or connect a shunt ref like a LM336 to AREF, so your measurements are all based on a stable reference. Using 3.3V power as a reference only makes sense for ratiometric measurements

Well, i havent used aref, i'm using the internal 3.3V as ADC reference thought... Do i need to connect a shunt reference? even if i am using the internal voltage?
And what about the 1.2V internal reference? As far I was aware of, the teensy had a 3.3V internal reference tho?

Maybe the schematic and link i have send make some 'mental image'?
 
Last edited:
Status
Not open for further replies.
Back
Top