2 Replies Latest reply on May 7, 2015 3:14 PM by Eliot

    Setting up I2C, is there a guide?

    Eliot

      Is there a definitive guide for setting up i2c and discovering devices?

       

      I've been diving through the forums and finding bits and pieces of info and a lot of inconclusive answers. Here's what I'm working with, which works sometimes but not others.

       

      I'm using the Edison module on a mini breakout board with the i2c-6 pins broken out (I've also tested i2c-1 and get similar behavior to what I'll describe). This is connected to a PCA9603 level shifter, the same one Sparkfun uses on their PWM board, to shift from 1.8v of the Edison to 3.3v of my i2c devices.

       

      I've got two chips I'm trying to work with PCA9634 LED driver and the LSM9DS0 accel/gyro/mag. I've tested them on the bus separately and together.

       

      By testing I mean using

      i2cdetect -y -r 6
      

       

      Through various reboots I get one of these possible outcomes from the command:

      • An empty table
      • A kernel driver crash
      • Driver complaining about losing arbitration
      • The actual device addresses I'm looking for

      I'm using the latest (ww05-15) and have tested with two different Edisons and two different mini breakout boards and I have reflashed them to make sure they're up to date. Successes I've had: Using the Sparkfun LSM9DS0 Breakout Board with the RTIMUlib-Arduino by using the workaround of changing the i2c port in the Arduino IDE, the example sketch puts out good data. Using a sketch that just starts Wire.h and then trying i2cdetect on i2c-6 from the command line. Some variations of this have worked for detecting both chips on the bus at the same time. But it's not consistent; I have no idea how to boot the Edison and know that it will detect something connected to i2c-6 without crashing.

       

      My board successfully works with 3.3v i2c communication with both an Arduino and a Raspberry Pi.

       

      I want to be able to configure the Edison on a mini breakout board for i2c-6 communication from the command line and then run i2cdetect without the kernel driver crashing.

       

      How do I do this?

       

      Searching the forums I found someone getting the loss of arbitration message, but they weren't level shifting properly. I also found out how to change the bus speed to standard mode thinking that slower communication might be less error prone. I successfully set it to 100kHz but still didn't detect anything.

       

      echo std > /sys/devices/pci0000:00/0000:00:09.1/i2c_dw_sysnode/mode
      

       

      Is this script still relevant? It was created before the most recent release.

       

      Many of the commands in the i2c section of this guide give me a "write error: No such device" is that because I'm using a Mini Breakout Board and not the Arduino board?

       

      Thanks for your help!

        • 1. Re: Setting up I2C, is there a guide?
          nniles

          First, I don't think that there can be such a thing as a definitive guide to discovering devices on I2C, because the protocol doesn't include any provisions for doing that.  Discovery is going to be device specific - i.e., you have to know what you're looking for and how to talk to it.

           

          I also have an Edison and a LSM9DS0 (Sparkfun breakout, but not the Sparkfun Edison 9-dof) that I'm playing with right now.  I'm using I2C bus 1 (haven't tried bus 6) and this level shifter, though: SparkFun Logic Level Converter - Bi-Directional - BOB-12009 - SparkFun Electronics.

           

          Here's my i2cdetect results:

          ~# i2cdetect -y -r 1
               0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
          00:          -- -- -- -- -- -- -- -- -- -- -- -- --
          10: -- -- -- -- -- -- -- -- -- -- -- -- -- 1d -- --
          20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
          30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
          40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
          50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
          60: -- -- -- -- -- -- -- -- -- -- -- 6b -- -- -- --
          70: -- -- -- -- -- -- -- --
          
          
          

          You can see that the only addresses with results are the two used by the LSM9DS0 (0x1D is the accel/mag, and 0x6B is the gyro).

           

          Here's some very simple code that uses the MRAA C++ API and just checks the WHO_AM_I registers:

          #include <unistd.h>
          #include <signal.h>
          #include <mraa.hpp>
          
          #define LSM9DS0_BUS 1
          // device addresses
          #define LSM9DS0_XM 0x1D
          #define LSM9DS0_G  0x6B
          // WHO_AM_I is the same register for both addresses
          #define WHO_AM_I   0x0F
          #define I_AM_G     0xD4
          #define I_AM_XM    0x49
          
          int main(int argc, char **argv){
            mraa_result_t busret;
            uint8_t byte_buf;
          
            mraa::I2c* xm_i2c;
            mraa::I2c* g_i2c;
          
            xm_i2c = new mraa::I2c(LSM9DS0_BUS);
            g_i2c = new mraa::I2c(LSM9DS0_BUS);
          
            // retrieve WHO_AM_I registers
          byte_buf = 0;
            // accel/mag
            busret = xm_i2c->address(LSM9DS0_XM);
            if(busret == MRAA_SUCCESS){
              byte_buf = xm_i2c->readReg(WHO_AM_I);
              printf("Read from accel/mag WHO_AM_I: 0x%0X\n", byte_buf);
              // check WHO_AM_I result
              if(byte_buf != I_AM_XM){
                printf("i2c device not recognized, got 0x%0X, expected 0x%0X\n", byte_buf, I_AM_XM);
                exit(1);
              }
            }
            else{
              printf("Error setting address on i2c bus 0x%0X\n", LSM9DS0_BUS);
              exit(1);
            }
          
            byte_buf = 0;
            // gyro
            busret = g_i2c->address(LSM9DS0_G);
            if(busret == MRAA_SUCCESS){
              byte_buf = g_i2c->readReg(WHO_AM_I);
              printf("Read from gyro WHO_AM_I: 0x%0X\n", byte_buf);
              // check WHO_AM_I result
              if(byte_buf != I_AM_G){
                printf("i2c device not recognized, got 0x%0X, expected 0x%0X\n", byte_buf, I_AM_G);
                exit(1);
              }
            }
           else{
             printf("Error setting address on i2c bus 0x%0X\n", LSM9DS0_BUS);
             exit(1);
           }
          }
          
          
          
          

           

          It gets compiled like so:

          g++ -o testi2c -lmraa testi2c.cpp
          
          
          


          And here is the output from my Edison:

          ~# ./testi2c
          Read from accel/mag WHO_AM_I: 0x49
          Read from gyro WHO_AM_I: 0xD4
          
          
          

           

          I have not had any instances of kernel crashing or driver complaints with my setup (knock on wood).

           

          As for why you're not having success or even consistent results, it's difficult to say.  If you've eliminated all possibilities for blatant errors in your setup, and you've got $3 and a few days to wait, you might try the level shifter that I linked above.

           

          Thanks,

          • 2. Re: Setting up I2C, is there a guide?
            Eliot

            Thanks for the solid piece of test code. I was able to adapt it to talk to my LED driver. I'm sure it will help a lot more people than just me.

             

            My suspicion about not level shifting properly was correct. Chip had the wrong resistor attached and was forcing higher voltages onto the pin which was causing a kernel panic. Swapped out the resistor and was able to use both i2cdetect and your code to talk to the chip.