FlexiTimer2 under the scope!

To see how consistent our data sampling is, I made Pin 13 (blue channel) go HIGH whenever the interrupt function started executing, and drove it LOW again when the function ended. Pin 12 (yellow channel) goes HIGH right before we start reading the acceleration data on the I2C bus, and is driven it LOW when the reading completes.

The code is provided after the screenshots.

Observations:

  • We get a (reasonably?) consistent sampling rate of around 249 Hz with the FlexiTimer2, quite close to the desired 250 Hz.
  • I2C reading happens at about 920 Hz.
  • Interesting things happen when the two events come close, but does it throw the sampling off?
  • Using noInterrupts() and interrupts() to enclose the I2C reading doesn’t seem to affect much.
  • A digital pin driven high (hiZ on load?) gets pulled to about 680 mV (on pin 12), but only 580 mV on pin 13. That means that the drop across the on-board LED 13 is about a 100 mV!

Scope captures, without having I2C reads within no/interrupts():

TEK00026

zoomed in:

TEK00027

Now, I2C reads are enclosed within no/Interrupts():

TEK00028

Zoomed in:

TEK00029

Trigger on I2C’s LED for getting a rough sampling rate (~920 Hz):TEK00030 TEK00031 TEK00032

/////////////////////////////////////////////
// Team UMAR-VARS                          //
// Arduino code for grabbing               //
// - ECG/EMG data (A0, A1)                 //
// - PPG data     (A2)                     //
// - acc data     (A5, A6) - I2C - raw     //
////////////////////////////////////////////////////////////////////////////////////////////
// Links/Credits:
// - User manual
// https://www.olimex.com/Products/Duino/Shields/SHIELD-EKG-EMG/resources/SHIELD-EKG-EMG.pdf
// - ECG code from Stan12's code (which is adapted from Olimex's sample code)
// https://www.olimex.com/forum/index.php?topic=572.0
// - MPU-6050 code from JohnChi's example sketch
////////////////////////////////////////////*BEGIN*/////////////////////////////////////////
 
  #include<FlexiTimer2.h>;  //http://playground.arduino.cc/Main/FlexiTimer2 for interrupts (timing)
  //#include<compat/deprecated.h>; //seems to be outdated
  #include<Wire.h>;                 //for I2C
 
  // MPU-6050:
      const int MPU=0x68;  // I2C address of the MPU-6050
      int i=0;
      //int16_t AcX,AcY,AcZ; //,Tmp,GyX,GyY,GyZ;  
  // ECG shield:
      volatile unsigned char CurrentCh = 0;       // Current channel being sampled.
      volatile unsigned int  ADC_Value = 0;   // ADC current value, 2 byte value (16 bits total)
      volatile unsigned char TXBuf[1+1+2*3+2*3];  // sync + packetNum + heartData + accData
      volatile unsigned char TXIndex;             // Next byte to write in the transmission packet.
 
  void setup() {
    pinMode(13, OUTPUT);
    pinMode(12, OUTPUT);
  // MPU-6050:   
      //do NOT place noInterrupts() here. Seems to affect I2C?
      Wire.begin();
      Wire.beginTransmission(MPU);
      Wire.write(0x6B);  // PWR_MGMT_1 register
      Wire.write(0);     // set to zero (wakes up the MPU-6050)
      Wire.endTransmission(true);
       
  // ECG shield:
      noInterrupts();  // Disable all interrupts before initialization
    
      //Packet | units?
      TXBuf[0] = 0xa5;    //Sync 0 -- could help with parsing -- unchanged for legacy
      TXBuf[1] = 0x0;     //Packet counter -- helps to see what we might miss
      TXBuf[2] = 0x02;    //ECG-1 | A0 High Byte
      TXBuf[3] = 0x00;    //ECG-1 | A0 Low Byte
      TXBuf[4] = 0x02;    //ECG-2 | A1 High Byte
      TXBuf[5] = 0x00;    //ECG-2 | A1 Low Byte
      TXBuf[6] = 0x02;    //PPG   | A2 High Byte
      TXBuf[7] = 0x00;    //PPG   | A2 Low Byte
      TXBuf[8] = 0x02;    //AccX  | I2C High Byte
      TXBuf[9] = 0x00;    //AccX  | I2C Low Byte
      TXBuf[10] = 0x02;   //AccY  | I2C High Byte
      TXBuf[11] = 0x00;   //AccY  | I2C Low Byte
      TXBuf[12] = 0x02;   //AccZ  | I2C High Byte
      TXBuf[13] = 0x00;   //AccZ  | I2C Low Byte
       
      FlexiTimer2::set(4, 1.0/1000, Timer2_Overflow_ISR); //4 units of 1 ms resolution => sampling at FS
      // FlexiTimer2::set(unsigned long units, double resolution, void (*f)())
      // E.g. units=1, resolution = 1.0/3000 will call f 3000 times per second, 
      // whereas it would be called only 1500 times per second when units=2.
 
      //serial prints seem to freeze for certain rates. Data might still be coming through.
 
      FlexiTimer2::start();
      //enables the interrupt.
  
      // Serial Port
      Serial.begin(57600);       //why this baud rate? too slow vs too many errors
 
      interrupts();              // Enable all interrupts after initialization has been completed
  }
 
  void Timer2_Overflow_ISR() {
    digitalWrite(13, HIGH);
    // Serial.println("Hey!");   //use for experimenting with interrupt frequency
   
  // ECG/PPG:
    //Read the ADC channels (ECG-1, ECG-2, PPG)
    for(CurrentCh=0; CurrentCh<3; CurrentCh++) {
      ADC_Value = analogRead(CurrentCh); //first two channels reserved for I2C
      TXBuf[2*CurrentCh + 2] = ((unsigned char)((ADC_Value & 0xFF00) >> 8));// Write High Byte
      TXBuf[2*CurrentCh + 3] = ((unsigned char) (ADC_Value & 0x00FF));      // Write Low Byte
    }
    // http://arduino.cc/en/Reference/analogRead
    // 10-bit ADC, mapping input voltages 0-5V -> 0-1023 => resolution = 4.9 mV
    // The input range and resolution can be changed using analogReference().
    // It takes about (0.0001s to read an analog input => max rate is about 10,000 Hz.
  
  // MPU-6050 (I2C routine):
    //couldn't get it to work here. Moved to void loop().
     
    //Serial.print(TXBuf[1], HEX);
    // Send Packet
    for(TXIndex=0;TXIndex<13;TXIndex++){
      Serial.write(TXBuf[TXIndex]);
    }
    Serial.println("");
 
 
    //Serial.println(TXBuf[1], HEX);
    TXBuf[1]++;                          // Increment the packet counter
 digitalWrite(13, LOW);
  }
 
  void loop() {
    Wire.beginTransmission(MPU);
    Wire.write(0x3B);                    // starting with register 0x3B (ACCEL_XOUT_H)
    Wire.endTransmission(false);
    Wire.requestFrom(MPU,6,true);        // request 6 (consecutive?) registers
    
    digitalWrite(12, HIGH);
 
    i = 0;
    noInterrupts();   // commented out for captures 1 and 2 (26, 27)
    

    while(Wire.available() && i<6) {     // Reads the following 6 registers:
      TXBuf[8+i]=Wire.read();            // 0x3B (ACCEL_XOUT_H), 0x3C (ACCEL_XOUT_L)
      i++;                               // 0x3D (ACCEL_YOUT_H), 0x3E (ACCEL_YOUT_L)
    }                                    // 0x3F (ACCEL_ZOUT_H), 0x40 (ACCEL_ZOUT_L)
    interrupts(); // commented out for captures 1 and 2 (26, 27)
    //delay(2);                          // occasionally helps with serial.print()
 
    //__asm__ __volatile__ ("sleep");
    digitalWrite(12, LOW);
  }
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s