12 Replies Latest reply on Mar 3, 2015 9:54 AM by MPayne

    Edison Arduino Pins not working as expected

    chipmc

      I am using an MMA8452 accelerometer with my Edison and the Arduino expansion board.  Most sketches have been performing as expected but not this one.

       

      I have validated that the accelerometer works with the Edison using a basic sketch.  I have switched J9 to 3.3V as this is a 3.3 device without issue.  The problem comes when I use a sketch that polls the D3 pin for a HIGH in order to indicate some movement of the sensor (a Tap).  This sketch works fine on my Arduino.  The Sparkfun breakout board, has pull-ups on SDA and SCL but not on the Interrupt pins.  So in setup(), I have these commands:

        pinMode(int1Pin, INPUT);

        digitalWrite(int1Pin, LOW);

        pinMode(int2Pin, INPUT);

        digitalWrite(int2Pin, LOW);

      This sketch works fine with an Arduino Pro Mini 3.3V but not with the Edison.  Why?  Do I need to do something different to initialize pins here?

       

      /*

      Bare Bones Demo for the Sparkfun MMA8452 Accelerometer breakout board

       

       

      Arduino Pro Mini

      Sparkfun MMA8452 breakout board

       

       

      Circuit:

         - SDA - Pin A4 <- External 4.7k pull ups on breadboard

         - SCL - Pin A5 <- Ditto

         - INT2 - D3 - Interrup for Knocks and Tilts

         - INT1 - D2  - Interrupt for changes in acceleration

         - Sensitivity Adjustment Pot - A0

      */

       

       

      //Reads a knock from the accelerometer

      //and prints it on the serial port

       

       

      //Include Wire I2C library

      #include <Wire.h>

       

       

       

       

      const int MMA8452_ADDRESS = 0x1D; // The SparkFun MMA8452 breakout board defaults to 1 for an address of 0x1D

      const byte SCALE = 2;  // Sets full-scale range to +/-2, 4, or 8g. Used to calc real g values.

      const byte dataRate = 3;  // output data rate - 0=800Hz, 1=400, 2=200, 3=100, 4=50, 5=12.5, 6=6.25, 7=1.56

       

       

      int InputValue = 0;            // Raw sensitivity input

      byte Sensitivity = 0x00;       // Hex variable for sensitivity

      static byte source;

      int ledState = LOW;            // variable used to store the last LED status, to toggle the light

      unsigned long LastKnock = 0;   // Used for debouncing

      int Debounce = 1000;           // Time in ms between knocks

       

       

      const int TrimPot = A0;        // Potentiometer used to adjust sensitivity

      const int ledPin = 13;          // led connected to digital pin 13

      const int int1Pin = 2;         // Not used now but wired for future use

      const int int2Pin = 3;         // This is the interrupt pin that registers taps

       

       

      void setup() {

        Serial.begin(19200);          //Start serial communication at 9600 baud

        Wire.begin();                //Create a Wire object

        pinMode(int2Pin, INPUT);     // Set up the Accelerometer interrupt pins, they're set as active high, push-pull

        digitalWrite(int2Pin, LOW);

        pinMode(int1Pin, INPUT); 

        digitalWrite(int2Pin, LOW);

        byte c = readRegister(MMA8452_ADDRESS, 0x0D);            // Read WHO_AM_I register to test communications

        if (c == 0x2A) {                  // WHO_AM_I should always be 0x2A

          initMMA8452(SCALE, dataRate);   // init the accelerometer if communication is OK

          Serial.println("MMA8452Q is online...");

        }

        else {                          // Problem with communucations

          Serial.print("Could not connect to MMA8452Q: 0x");

          Serial.println(c, HEX);

          while(1) ;                   // Loop forever if communication doesn't happen

        }

      }

       

       

      void loop()

      {

        if (digitalRead(int2Pin)==1)    // If int2 goes high, either p/l has changed or there's been a single/double tap

        {

          source = readRegister(MMA8452_ADDRESS, 0x0C);  // Read the interrupt source reg.

          if ((source & 0x08)==0x08) { // We are only interested in the TAP register so read that

            if (millis() >= (LastKnock + Debounce)) {    // Need to debounce the knocks

              Serial.println("Tap");

              ledState = !ledState;                        // toggle the status of the ledPin:

              digitalWrite(ledPin, ledState);              // update the LED pin itself

              LastKnock = millis();

            }

            byte source = readRegister(MMA8452_ADDRESS, 0x22);  // Reads the PULSE_SRC register to reset it

          } 

        }

      }

       

       

      // Initialize the MMA8452 registers

      // See the many application notes for more info on setting all of these registers:

      // http://www.freescale.com/webapp/sps/site/prod_summary.jsp?code=MMA8452Q

      // Feel free to modify any values, these are settings that work well for me.

      void initMMA8452(byte fsr, byte dataRate)

      {

        MMA8452Standby();  // Must be in standby to change registers

        // Set up the full scale range to 2, 4, or 8g.

        if ((fsr==2)||(fsr==4)||(fsr==8))

          writeRegister(0x1D, 0x0E, fsr >> 2);

        else

          writeRegister(0x1D,0x0E, 0);

        // Setup the 3 data rate bits, from 0 to 7

        writeRegister(0x1D, 0x2A, readRegister(0x1D,0x2A) & ~(0x38));

        if (dataRate <= 7)

          writeRegister(0x1D,0x2A, readRegister(0x1D,0x2A) | (dataRate << 3)); 

        // Read the Sensitivity input - Note this is done once at startup - need to press reset to take effect if changed

        InputValue = analogRead(TrimPot);

        InputValue = map(InputValue,0,1023,0,255);

        Sensitivity = byte(InputValue);

        Serial.print("Input set to =");

        Serial.println(Sensitivity);

        /* Set up single and double tap - 5 steps:

         1. Set up single and/or double tap detection on each axis individually.

         2. Set the threshold - minimum required acceleration to cause a tap.

         3. Set the time limit - the maximum time that a tap can be above the threshold

         4. Set the pulse latency - the minimum required time between one pulse and the next

         5. Set the second pulse window - maximum allowed time between end of latency and start of second pulse

         for more info check out this app note: http://cache.freescale.com/files/sensors/doc/app_note/AN4072.pdf */

        //writeRegister(0x21, 0x7F);  // 1. enable single/double taps on all axes

        writeRegister(0x1D, 0x21, 0x55);  // 1. single taps only on all axes

        // writeRegister(0x21, 0x6A);  // 1. double taps only on all axes

        writeRegister(0x1D, 0x23, Sensitivity);  // 2. x thresh at 2g (0x20), multiply the value by 0.0625g/LSB to get the threshold

        writeRegister(0x1D, 0x24, Sensitivity);  // 2. y thresh at 2g (0x20), multiply the value by 0.0625g/LSB to get the threshold

        writeRegister(0x1D, 0x25, Sensitivity);  // 2. z thresh at .5g (0x08), multiply the value by 0.0625g/LSB to get the threshold

        writeRegister(0x1D, 0x26, 0x30);  // 3. 30ms time limit at 800Hz odr, this is very dependent on data rate, see the app note

        writeRegister(0x1D, 0x27, 0xC8);  // 4. 1000ms (at 100Hz odr, Normal, and LPF Disabled) between taps min, this also depends on the data rate

        writeRegister(0x1D, 0x28, 0xFF);  // 5. 318ms (max value) between taps max

        // Set up interrupt 1 and 2

        writeRegister(0x1D, 0x2C, 0x02);  // Active high, push-pull interrupts

        writeRegister(0x1D, 0x2D, 0x19);  // DRDY, P/L and tap ints enabled

        writeRegister(0x1D, 0x2E, 0x01);  // DRDY on INT1, P/L and taps on INT2

        MMA8452Active();  // Set to active to start reading

      }

       

       

      // Sets the MMA8452 to standby mode.

      // It must be in standby to change most register settings

      void MMA8452Standby()

      {

        byte c = readRegister(0x1D,0x2A);

        writeRegister(0x1D,0x2A, c & ~(0x01));

      }

       

       

      // Sets the MMA8452 to active mode.

      // Needs to be in this mode to output data

      void MMA8452Active()

      {

        byte c = readRegister(0x1D,0x2A);

        writeRegister(0x1D,0x2A, c | 0x01);

      }

       

       

       

       

      // Read a single byte from address and return it as a byte

      byte readRegister(int I2CAddress, byte address)

      {

        //Send a request

        //Start talking to the device at the specified address

        Wire.beginTransmission(I2CAddress);

        //Send a bit asking for requested register address

        Wire.write(address);

        //Complete Transmission

        Wire.endTransmission(false);

        //Read the register from the device

        //Request 1 Byte from the specified address

        Wire.requestFrom(I2CAddress, 1);

        //wait for response

        while(Wire.available() == 0);

        // Get the temp and read it into a variable

        byte data = Wire.read();

        return data;

      }

       

       

      // Writes a single byte (data) into address

      void writeRegister(int I2CAddress, unsigned char address, unsigned char data)

      {

        //Send a request

        //Start talking to the device at the specified address

        Wire.beginTransmission(I2CAddress);

        //Send a bit asking for requested register address

        Wire.write(address);

        Wire.write(data);

        //Complete Transmission

        Wire.endTransmission(false);

      }

       

      Thank you in advance for any advice.

        • 1. Re: Edison Arduino Pins not working as expected
          Intel_Alvarado

          Hi chipmc,

           

          I tried running that code with my Edison and Arduino expansion board. It did run and upload correctly. Unfortunately I don’t have that same sensor so that I can try it but I will run some tests with a similar one and let you know my results.

           

          Regards

          Sergio

          • 2. Re: Edison Arduino Pins not working as expected
            chipmc

            Sergio,

             

            Thank you for taking a look at this.  I have simplified the code to make it easier to see what is going on.  Again, this code works fine on the Arduino but the not the Edison.  For some reason, the Edison goes right into the interrupt loop and stays there instead of waiting for the Interrupt flag to be asserted.

             

            Simplified code:

            /*

            Bare Bones Demo for the Sparkfun MMA8452 Accelerometer breakout board

            Arduino Pro Mini or Intel Edison with the Arduino shield (note J9 setting below)

            Sparkfun MMA8452 breakout board

             

            Circuit - Using the Edison Arduino sheild

            ****  Set Jumper J-9 to 3.3V ************

               - SDA - Pin A4 <- External 4.7k pull ups on breadboard

               - SCL - Pin A5 <- Ditto

               - INT2 - D3 - Interrup for Knocks and Tilts

               - INT1 - D2  - Interrupt for changes in acceleration

            */

             

            //Include Wire I2C library

            #include <Wire.h>

             

             

            // Settings for the Accelerometer

            const int MMA8452_ADDRESS = 0x1D; // The SparkFun MMA8452 breakout board defaults to 1 for an address of 0x1D

            const byte SCALE = 2;  // Sets full-scale range to +/-2, 4, or 8g. Used to calc real g values.

            const byte dataRate = 3;  // output data rate - 0=800Hz, 1=400, 2=200, 3=100, 4=50, 5=12.5, 6=6.25, 7=1.56

             

             

            // Variables

            static byte source;

            int ledState = LOW;            // variable used to store the last LED status, to toggle the light

            const int ledPin = 13;          // led connected to digital pin 13

            const int int1Pin = 2;         // Not used now but wired for future use

            const int int2Pin = 3;         // This is the interrupt pin that registers taps

             

             

            void setup() {

              Serial.begin(19200);          //Start serial communication at 9600 baud

              Wire.begin();                //Create a Wire object

              pinMode(int2Pin, INPUT);     // Set up the Accelerometer interrupt pins, they're set as active high, push-pull

              digitalWrite(int2Pin, LOW);

              pinMode(int1Pin, INPUT);  

              digitalWrite(int2Pin, LOW);

              byte c = readRegister(MMA8452_ADDRESS, 0x0D);            // Read WHO_AM_I register to test communications

              if (c == 0x2A) {                  // WHO_AM_I should always be 0x2A

                initMMA8452(SCALE, dataRate);   // init the accelerometer if communication is OK

                Serial.println("MMA8452Q is online...");

              }

              else {                          // Problem with communucations

                Serial.print("Could not connect to MMA8452Q: 0x");

                Serial.println(c, HEX);

                while(1) ;                   // Loop forever if communication doesn't happen

              }

            }

             

             

            void loop()

            {

              if (digitalRead(int2Pin)==1)    // If int2 goes high, either p/l has changed or there's been a single/double tap

              {

                source = readRegister(MMA8452_ADDRESS, 0x0C);         // Read the interrupt source reg.

                if ((source & 0x08)==0x08) {                          // We are only interested in the TAP register so read that

                  byte source = readRegister(MMA8452_ADDRESS, 0x22);  // Reads the PULSE_SRC register to reset it

                  Serial.println("Tap");

                  delay(1000);                                        // simple debounce - remove in final code

                  ledState = !ledState;                               // toggle the status of the ledPin:

                  digitalWrite(ledPin, ledState);                     // update the LED pin itself

                }

              }  

            }

             

             

            // Initialize the MMA8452 registers

            // See the many application notes for more info on setting all of these registers:

            // http://www.freescale.com/webapp/sps/site/prod_summary.jsp?code=MMA8452Q

            // Feel free to modify any values, these are settings that work well for me.

            void initMMA8452(byte fsr, byte dataRate)

            {

              MMA8452Standby();  // Must be in standby to change registers

              if ((fsr==2)||(fsr==4)||(fsr==8))  // Set up the full scale range to 2, 4, or 8g.

                writeRegister(0x1D, 0x0E, fsr >> 2); 

              else

                writeRegister(0x1D,0x0E, 0);  // Setup the 3 data rate bits, from 0 to 7

              writeRegister(0x1D, 0x2A, readRegister(0x1D,0x2A) & ~(0x38));

              if (dataRate <= 7)

                writeRegister(0x1D,0x2A, readRegister(0x1D,0x2A) | (dataRate << 3));  

              byte Sensitivity = 0x00;   // Set to the most sensitive

              writeRegister(0x1D, 0x21, 0x55);  // 1. single taps only on all axes

              writeRegister(0x1D, 0x23, Sensitivity);  // 2. x thresh at 2g (0x20), multiply the value by 0.0625g/LSB to get the threshold

              writeRegister(0x1D, 0x24, Sensitivity);  // 2. y thresh at 2g (0x20), multiply the value by 0.0625g/LSB to get the threshold

              writeRegister(0x1D, 0x25, Sensitivity);  // 2. z thresh at .5g (0x08), multiply the value by 0.0625g/LSB to get the threshold

              writeRegister(0x1D, 0x26, 0x30);  // 3. 30ms time limit at 800Hz odr, this is very dependent on data rate, see the app note

              writeRegister(0x1D, 0x27, 0xC8);  // 4. 1000ms (at 100Hz odr, Normal, and LPF Disabled) between taps min, this also depends on the data rate

              writeRegister(0x1D, 0x28, 0xFF);  // 5. 318ms (max value) between taps max

              writeRegister(0x1D, 0x2C, 0x02);  // Active high, push-pull interrupts

              writeRegister(0x1D, 0x2D, 0x19);  // DRDY, P/L and tap ints enabled

              writeRegister(0x1D, 0x2E, 0x01);  // DRDY on INT1, P/L and taps on INT2

              MMA8452Active();  // Set to active to start reading

            }

             

             

            // Sets the MMA8452 to standby mode to change most register settings

            void MMA8452Standby()

            {

              byte c = readRegister(0x1D,0x2A);

              writeRegister(0x1D,0x2A, c & ~(0x01));

            }

             

             

            // Sets the MMA8452 to active mode.

            void MMA8452Active()

            {

              byte c = readRegister(0x1D,0x2A);

              writeRegister(0x1D,0x2A, c | 0x01);

            }

             

             

             

             

            // Read a single byte from address and return it as a byte

            byte readRegister(int I2CAddress, byte address)

            {

              Wire.beginTransmission(I2CAddress);  //Start talking to the device at the specified address

              Wire.write(address);  //Send a bit asking for requested register address

              Wire.endTransmission(false); //Complete Transmission

              Wire.requestFrom(I2CAddress, 1);  //Request 1 Byte from the specified address

              while(Wire.available() == 0); //wait for response

              byte data = Wire.read(); // Get the temp and read it into a variable

              return data;

            }

             

             

            // Writes a single byte (data) into address

            void writeRegister(int I2CAddress, unsigned char address, unsigned char data)

            {

              Wire.beginTransmission(I2CAddress);  //Start talking to the device at the specified address

              Wire.write(address); //Send a bit asking for requested register address

              Wire.write(data); //Complete Transmission

              Wire.endTransmission(false);

            }

            • 3. Re: Edison Arduino Pins not working as expected
              MPayne

              This sensor is likely not getting enough current sourced (as evidenced by the pull-ups on the sensor).  The I2C pins are limited to 3 mA.  This is a limitation of the SoC, and would need a HW fix (via the Arduino/breakout board).  One could also build some interface circuitry to work around this.

               

              Do you have any scope captures that could confirm this type of behavior?

              • 4. Re: Edison Arduino Pins not working as expected
                chipmc

                MPayne,

                 

                Interesting but I can read acceleration value from the accelerometer using the basic sketch. My issue is with the behavior of the interrupt pins.

                 

                I am traveling this week but, when I get back, I can put a logic analyzer on the i2c bus and verify the signals. I don't have a scope but I think this should help with troubleshooting.

                 

                Could I use an i2c level shifter like the PCA9306. To reduce the current draw?

                 

                Thanks, Chip

                • 5. Re: Edison Arduino Pins not working as expected
                  MPayne

                  Ah ok, I may have read that a bit wrong.  The INT pins should be fine.  The scope traces should help a lot when you get a chance.

                  • 6. Re: Edison Arduino Pins not working as expected
                    chipmc

                    MPayne,

                     

                    Will do.  However, one quick question so I can resolve this.  Here are the steps I am taking, I wonder if the same current issues you mentioned for the i2c pins will have an impact on Arduino D3:

                    - Set pinmode to input

                    - digitalWrite LOW

                    - Wait for pin to go high

                    - read the register and clear the interrupt

                     

                    This sequence works on Arduino but, (with J9 set to 3.3V and no pull-up on the Interrupt pin) any reason why it will not work on the Edison with the Arduino base?

                     

                    Thanks,  Chip

                    • 7. Re: Edison Arduino Pins not working as expected
                      Intel_Alvarado

                      Hi chipmc,

                       

                      The problem might be indeed because of the pull up resistors, it is recommended to use resistors with the I2C line to perform level shifting. If you look at MMA8452Q Accelerometer Breakout Hookup Guide - learn.sparkfun.com you’ll see an example of how to connect the MMA8452 to your board and a brief explanation on level shifters. This might also be of interest: Logic Levels - learn.sparkfun.com .

                       

                      Regards

                      Sergio 

                      • 8. Re: Edison Arduino Pins not working as expected
                        chipmc

                        Sergio,

                         

                        First of all, thank you for staying engaged on this thread and for taking a look at the Sparkfun site - this is indeed the accelerometer I am using. 

                         

                        One point, I used the J9 jumper on the Edison Arduino expansion board to shift to 3.3V logic.  That should fix the level shifting issue that the 330 ohm resistors in series with SDA and SCL are shown in the hookup guide.  In fact, I can run a simple sketch on the accelerometer and verify that the i2C bus is working correctly.

                         

                        My issue is in using an external interrupt line.   This accelerometer sends a logic HIGH when it senses a "tap" and that is the event that is not working correctly on the Edison - though it works perfectly on a 3.3V Arduino Pro Mini.  Here are my (corrected) steps to set up the interrupt line:

                        - Set PinMode to input

                        - digitalWrite LOW

                        - Configure the MMA8452 for a logic HIGH push-pull interrupt setting for the accelerometer's interrupt line

                        - Start the loop

                        - Wait for pin to go high

                        - read the register and clear the interrupt

                        - Return to the loop

                         

                        The problem is that - on the Edison - the interrupt is always seen as HIGH.

                         

                        Any ideas?

                         

                        Thanks,

                         

                        Chip

                        • 9. Re: Edison Arduino Pins not working as expected
                          KurtE

                          have you tried removing the two calls to digitalWrite low for the interrupt pin in setup?    Not sure if your device will set the value high or low, or assumes pull-up or pull down

                          , might try external one if necessary, or if PU, you can try set mode INPUT_PULLUP.  Is pd, it is harder, could manually set through filesystem calls.

                          • 10. Re: Edison Arduino Pins not working as expected
                            chipmc

                            MPayne,

                             

                            I was able to connect my logic analyzer to monitor the communications between the micro controller and the accelerometer.  First, here is the capture from the Arduino Pro Mini which works correctly:

                            Arduino Pro Mini i2c and Interrupt Trace.png

                            Next, here is the communications between the Intel Edison running the same circuit and the same sketch:

                            Intel Edison i2c and Interrupt Trace.png

                            Notice that the Interrupt Line (I2 on the breakout, D3 on the Arduino and Channel 2 on the trace) is low then high on the Arduino and the opposite on the Edison.  Why would that be? 

                             

                            Also, I am new to this logic analyzer (was a valentines present from my girlfriend) so, if I need to do this better or different, please say.

                             

                            Thanks,

                             

                            Chip

                            • 11. Re: Edison Arduino Pins not working as expected
                              chipmc

                              Kurt, Thank you for the suggestion (typo on the second digitalWrite - oops) but when I removed them, I had the same problem.  I2 is high on the Edison and Low on the Arduino and I have no idea why.  Happy to try anything else if you have ideas.  Thanks again, Chip

                              • 12. Re: Edison Arduino Pins not working as expected
                                MPayne

                                It does indeed look like your expected signal is inverted from the traces.  I would guess it is configuration of the pin itself, since that is what is different from a traditional Arduino board (the expanders are different).

                                 

                                I've got someone taking a look at it for you.  I'll follow up with what I know in the morning.