Shop OBEX P1 Docs P2 Docs Learn Events
The problem about I2C communication — Parallax Forums

The problem about I2C communication

AnnAnn Posts: 9
edited 2013-12-01 10:10 in Learn with BlocklyProp
I want to realize to read three axis data of gyro by using Activity Board. The Example is shown as following.

Example: The I2C address of gyro is 105. The I2C addresses of gyro register which send data is 0x20 and the data is 0x1f. The address of register which receive data from I2C bus is 0X2B and the data would be saved in MSB. However, I cant read any data. Is there any mistakes in my program? Hope sb could slove it.

The following is completed code.
#include "simpletools.h" // Include simpletools header
#include "simplei2c.h"
#define CTRL_REG1 0x20
#define CTRL_REG2 0x21
#define CTRL_REG3 0x22
#define CTRL_REG4 0x23


int main() // Main function
{
int x, y, z; // Declare x, y, & z axis variables
pause(1000);
int Addr = 105; // I2C address of gyro
i2c *bus = i2c_newbus(3, 2, 0); // New I2C bus SCL=P3, SDA=P2
i2c_out(bus,Addr,CTRL_REG1,16,0x1f,16); //open all axis
i2c_out(bus,Addr,CTRL_REG3,16,0x08,16); //Enable control ready signal
i2c_out(bus,Addr,CTRL_REG4,16,0x80,16); //Set scale (500 deg/sec)
pause(100);

while(1) // Repeat indefinitely
{
print("%c", HOME);
int MSB;
int LSB;
i2c_in(bus,Addr,0x29,8,MSB,8);
i2c_in(bus,Addr,0x28,8,LSB,8);
x = ((MSB << 8) | LSB);
i2c_in(bus,Addr,0x2B,8,MSB,8);
i2c_in(bus,Addr,0x2A,8,LSB,8);
y = ((MSB << 8) | LSB);
i2c_in(bus,Addr,0x2D,8,MSB,8);
i2c_in(bus,Addr,0x2C,8,LSB,8);
z = ((MSB << 8) | LSB);
print("\nx=%d, y=%d, z=%d%c\n", x, y, z, CLREOL); //Display raw compass values
waitcnt(CLKFREQ/2+CNT);
}
}

Comments

  • SRLMSRLM Posts: 5,045
    edited 2013-11-27 20:28
    Which gyro is it?

    Normally, address is the upper 7 bits of the byte, and the last bit is reserved for read/write. 105d is 1101001b, so it looks like might want to shift that left one place and add in a r/w bit.

    On a slightly related note I have a L3GD20 driver here that can serve as inspiration (it uses C++ and a different I2C system, but the higher level protocol is the same).
  • AnnAnn Posts: 9
    edited 2013-11-27 23:43
    Actually I just want to know how to use i2c_in and i2c_out. Could you give me more examples about these two function? So I could understand how to send/read data.
  • feifeifeifei Posts: 2
    edited 2013-11-28 00:20
    It’s like this function:
    byte MSB, LSB;
    MSB = readI2C(0x29);
    LSB = readI2C(0x28);
    x = ((MSB << 8) | LSB);

    MSB = readI2C(0x2B);
    LSB = readI2C(0x2A);
    y = ((MSB << 8) | LSB);

    MSB = readI2C(0x2D);
    LSB = readI2C(0x2C);
    z = ((MSB << 8) | LSB);


    How to port this program to used on Activity Board?And the gyro is L3G4200D.
  • jazzedjazzed Posts: 11,803
    edited 2013-11-28 07:46
    Here are i2c_in and i2c_out descriptions. They are also in your SimpleIDE\Learn\Simple Library folders.

    The documentation function descriptions say this about the I2C address:
    i2cAddr 8 bit device address. This is the 7-bit I2C address and read/write bit.

    To me, this is an opportunity for improvement and should say:
    i2cAddr 8 bit device address. This is the 7-bit I2C address on bits 7..1 and the read/write bit on bit 0.

    What it means is the I2C address used in the functions is a left shift by 1 address. Other systems use the address as stated in the data sheet. For example an EEPROM address is 0x50. In the Parallax world that address gets translated by the programmer to 0xA0 (0x50 left shift by 1).

    The top level documentation is the key to understanding simple libraries. It provides a navigation point to find all library docs.

    In my installation, the documentation starts at Documents\SimpleIDE\Learn\Simple Libraries Index.html
    For example: file:///C:/Users/Steve/Documents/SimpleIDE/Learn/Simple Libraries Index.html

    Simple Libraries
    https://propsideworkspace.googlecode.com/hg/Learn/Simple Libraries Index.html

    i2c_in
    https://propsideworkspace.googlecode.com/hg/Learn/Simple Libraries/Utility/libsimpletools/html/simpletools_8h.html#a3716fa497e2d94ab9ab80c30b3109480

    i2c_out
    https://propsideworkspace.googlecode.com/hg/Learn/Simple Libraries/Utility/libsimpletools/html/simpletools_8h.html#a8e388b4d227694772970f0ee51ad2c31
  • AnnAnn Posts: 9
    edited 2013-11-28 17:50
    Even the regAddr is wrong, the MSB should show the data from the wrong address, however, it doesn't show anything. Could you give me a example program? It's easy for me to understand it. By the way I am not sure can the data read by I2C_in() function be saved in MSB?
  • feifeifeifei Posts: 2
    edited 2013-11-28 23:37
    what‘s wrong with this program? It still print '216'.
    QQ&#22270;&#29255;20131129153405.jpg

    #include "simpletools.h" // Include simpletools header#include "simplei2c.h"
    #define CTRL_REG1 0x20
    #define CTRL_REG2 0x21
    #define CTRL_REG3 0x22
    #define CTRL_REG4 0x23
    
    
    
    
    int main() // Main function
    {
    unsigned char x16, y16, z16; // Declare x, y, & z axis variables
    pause(1000);
    int Addr = 105; // I2C address of gyro
    int ack = 0;
    int nack = 1;
    uint8_t data[1];
    //i2c compass;
    //i2c *bus = i2c_newbus(3, 2, 0); // New I2C bus SCL=P3, SDA=P2
    i2c bus;
    i2c_open(&bus,3,2,0);
    i2c_start(&bus);
    ack = i2c_writeByte(&bus, CTRL_REG1); //open all axis
    ack = i2c_writeData(&bus,0x1f,1);
    ack = i2c_writeByte(&bus, CTRL_REG3); //Enable control ready signal
    ack = i2c_writeData(&bus,0x08,1);
    ack = i2c_writeByte(&bus, CTRL_REG4);
    ack = i2c_writeData(&bus,0x80,1); //Set scale (500 deg/sec)
    //i2c_out(bus,Addr,CTRL_REG1,1,0x1f,1); //open all axis
    //i2c_out(bus,Addr,CTRL_REG3,1,0x08,1); //Enable control ready signal
    //i2c_out(bus,Addr,CTRL_REG4,1,0x80,1); //Set scale (500 deg/sec)
    pause(100);
    
    while(1) // Repeat indefinitely
    {
    print("%c", HOME);
    int MSB;
    int LSB;
    //i2c_open(&bus,3,2,0);
    i2c_start(&bus);
    ack = i2c_writeByte(&bus, 0x29);
    ack = i2c_readData(&bus, data, 1);
    MSB = data;
    ack = i2c_writeByte(&bus, 0x28);
    ack = i2c_readData(&bus, data, 1);
    LSB = data;
    i2c_stop(&bus);
    //i2c_in(bus,Addr,0x29,8,MSB,8);
    //i2c_in(bus,Addr,0x28,8,LSB,8);
    x16 = ((MSB << 8) | LSB);
    i2c_start(&bus);
    ack = i2c_writeByte(&bus, 0x2B);
    ack = i2c_readData(&bus, data, 1);
    MSB = data;
    ack = i2c_writeByte(&bus, 0x2A);
    ack = i2c_readData(&bus, data, 1);
    LSB = data;
    i2c_stop(&bus);
    //i2c_in(bus,Addr,0x2B,8,MSB,8);
    //i2c_in(bus,Addr,0x2A,8,LSB,8);
    y16 = ((MSB << 8) | LSB);
    i2c_start(&bus);
    ack = i2c_writeByte(&bus, 0x2D);
    ack = i2c_readData(&bus, data, 1);
    MSB = data;
    ack = i2c_writeByte(&bus, 0x2C);
    ack = i2c_readData(&bus, data, 1);
    LSB = data;
    i2c_stop(&bus);
    //i2c_in(bus,Addr,0x2D,8,MSB,8);
    //i2c_in(bus,Addr,0x2C,8,LSB,8);
    z16 = ((MSB << 8) | LSB);
    
    
    int x, y, z;
    int *px, *py, *pz;
    
    
    px = &x;
    py = &y;
    pz = &z;
    
    *px = x16;
    *py = y16;
    *pz = z16;
    
    
    print("\nx=%d, y=%d, z=%d%c\n", x, y, z, CLREOL); //Display raw compass values
    waitcnt(CLKFREQ/2+CNT);
    }
    }
    
  • SRLMSRLM Posts: 5,045
    edited 2013-11-29 06:25
    Well, as Jazzed and I have said, first step is to make sure that the address is correct. The L3GD200D datasheet says that the address is 110100xb+R/W. You need to shift it left by one before anything has the possibility of happening.
  • jazzedjazzed Posts: 11,803
    edited 2013-11-29 07:41
    Here is an EEPROM test program.

    The ee_writeBuffer and ee_readBuffer functions show how to write basic read/write code.

    There are simpler ways to do this, but I wanted it to be complete, useful, and non-destructive.

    --Steve

    /*
     * EEPROM Test program demonstrates using simplei2c library.
     */
    
    #include "simpletools.h"
    #include "fdserial.h"
    
    // forward declarations
    int ee_writeBuffer(i2c *bus, int devaddr, int address, unsigned char *buffer, int length);
    int ee_readBuffer( i2c *bus, int devaddr, int address, unsigned char *buffer, int length);
    
    #define PAGELEN 128
    #define COUNT   16*(1024/PAGELEN)*PAGELEN // N*(...) KB
    #define TESTADDR 0x4000 // set to 0 for testing lower 16KB of EEPROM
    
    #define EEADDR  0xA0 // eeprom i2c address 0x50 << 1
    
    #define VERBOSE 0 // don't print all debug info
    
    // the pattern generator so we don't end up with the same pattern in every page.
    #define VAL(n) (((n) + (n)/(PAGELEN)) & 0xff)
    
    int main(void)
    {
      int n;                    // index variable
      int val;                  // temporary variable
      int fail = 0;             // failure counter
      int addrOffset = TESTADDR;
      
      unsigned char buffer[PAGELEN+1];  // buffer for testing
    
      fdserial *ser;            // we need fdserial because we want to use rxTime.
      i2c eebus;                // the i2c bus var will be passed as &eebus pointer.
    
      extern terminal *dport_ptr; // this is the default serial port pointer.
    
      // some simpletext libraries need more time for terminal startup.
      waitcnt(CLKFREQ/2+CNT);
    
      putLine("Check EEPROM");
    
      // switch from simpleserial to fdserial
      simpleterm_close();
      ser = fdserial_open(31,30,0,115200);
      dport_ptr = ser;  // make sure this is the default port
    
      // let the user choose to write EEPROM
      putStr("Press 'y' within 2 seconds to write EEPROM address ");
      putHex(addrOffset);
      putLine(".");
    
      // check for user input
      val = fdserial_rxTime(ser, 2000);
    
      // open the I2C bus with SCL driven so it works with all Parallax boards.
      i2c_open(&eebus, 28, 29, 1);
    
      if('y' == val)
      {
        // Erase EEPROM before write to ensure write was effective on multiple write passes.
        putLine("\nErasing EEPROM");
        for(n = 0; n < PAGELEN; n++) {
          // fill buffer with 0xFF's
          buffer[n % PAGELEN] = 0xFF;
        }
        // Go from 0 to COUNT (+1) to ensure last "sector" is erased.
        for(n = 0; n < COUNT+1; n++) {
          if(n % PAGELEN == 0) {
            ee_writeBuffer(&eebus, EEADDR, ((n-PAGELEN)/PAGELEN)*PAGELEN+addrOffset, buffer, PAGELEN);
          }
        }
    
        putLine("\nWriting EEPROM");
        // n < COUNT+1 ensures last "sector" is written.
        for(n = 0; n < COUNT+1; n++) {
    
          if(n > 0 && n % PAGELEN == 0) {
            // this is only called whenever we hit a page boundary after filling the buffer.
            ee_writeBuffer(&eebus, EEADDR, ((n-PAGELEN)/PAGELEN)*PAGELEN+addrOffset, buffer, PAGELEN);
          }
    
          // get new and mostly unique pattern for the buffer.
          val = buffer[n % PAGELEN] = VAL(n);
    
          // if verbosity is enabled dump. mostly for debug purposes.
          if(VERBOSE) {
            if (n % 16 == 0) {
              // do this every 16th byte to have a nice display
              putStr("\nADDR ");
              writeHexDigits(ser, n+addrOffset, 4);
              putChar(':');
            }
            putChar(' ');
            writeHexDigits(ser, val, 2);
          }  
    
        }
      }
    
      // finally, do the read test.
      putLine("\nReading EEPROM");
    
      fail  = 0;
      // this time we want to read the buffer before checking.
      // it means we don't want n < COUNT+1
      for(n = 0; n < COUNT; n++) 
      {
        if(n % PAGELEN == 0) {
          ee_readBuffer(&eebus, EEADDR, n+addrOffset, buffer, PAGELEN);
        }
    
        val = buffer[n%PAGELEN];
        // check for bad value.
        if(1 && val != VAL(n)) {
          if(!fail) {
            putStr("\nError at address: ");
            writeHexDigits(ser, n+addrOffset, 4);
            putStr(" Expected ");
            writeHexDigits(ser, VAL(n), 2);
            putStr(" Recieved ");
            writeHexDigits(ser, val, 2);
            putLine("\nThis program must write EEPROM at least once for test pass.");
          }
          fail++;
        }
          
        if (VERBOSE) {
          if(n % 16 == 0) {
              waitcnt(CLKFREQ/10+CNT);
              putStr("\nADDR ");
              writeHexDigits(ser, n+addrOffset, 4);
              putChar(':');
          }
          putChar(' ');
          writeHexDigits(ser, val, 2);
        }
      }
      if(fail) {
        putStr("\nTest errors: ");
        putDec(fail);
      }
      putLine("\nTest Complete.");
    
      return 0;
    }
    
    int ee_writeBuffer(i2c *bus, int devaddr, int address, unsigned char *buffer, int length)
    {
      int result = 0;
      int upaddr = ((address >> 16) & 0x07) << 1;
    
      //print("\nee_writeBuffer: Dev %02x Addr %05x Count %x\n", devaddr, address, length);
    
      i2c_start(bus);
      i2c_writeByte(bus, devaddr | upaddr);
      i2c_writeByte(bus, (address >> 8) & 0xff);
      i2c_writeByte(bus, address & 0xff);
      result = i2c_writeData(bus, buffer, length);
      i2c_stop(bus);
      do {
        waitcnt(CLKFREQ/1000+CNT);
      } while(i2c_poll(bus, devaddr));
      return result;
    }
    
    int ee_readBuffer(i2c *bus, int devaddr, int address, unsigned char *buffer, int length)
    {
      int result = 0;
      int upaddr = ((address >> 16) & 0x07) << 1;
    
      //print("\nee_readBuffer : Dev %02x Addr %05x Count %x\n", devaddr, address, length);
    
      i2c_start(bus);
      i2c_writeByte(bus, devaddr | upaddr);
      i2c_writeByte(bus, (address >> 8) & 0xff);
      i2c_writeByte(bus, (address) & 0xff);
      i2c_start(bus);
      i2c_writeByte(bus, devaddr | 1);
      result = i2c_readData(bus, buffer, length);
      i2c_stop(bus);
      return result;
    }
    
  • edited 2013-11-30 07:22
    ... Even the regAddr is wrong, .... Actually I just want to know how to use i2c_in and i2c_out. Could you give me more examples about these two function? So I could understand how to send/read data.


    Alright, we'll use i2c_in and i2c_out. First step would be to make sure the device can reply with its own ID with i2c_in. Please run this and let me know if you get the "Device ID confirmed..." message.
    #include "simpletools.h"                          // Include simpletools header
    
    unsigned char addrW   = 0xd2;                     // I2C write address
    unsigned char addrR   = 0xd3;                     // I2C read address
    unsigned char devId   = 0x0f;                     // Device ID register
    unsigned char reply;                              // Single byte reply
    
    i2c *bus;                                         // Bus for gyro
    
    int main()                                        // main function
    {
      bus = i2c_newbus(5, 4, 0);                      // Set I2C bus SCL=P5, SDA=P4
    
      i2c_in(bus, addrR, &devId, 1, &reply, 1);       // Can device ID itself?
    
      if(reply == 0b11010011)                         // Is device communicating?
        print("Device ID confirmed: %b.\n", reply);
      else
        print("Error, wrong device ID: %b\n", reply);
    }
    
  • edited 2013-12-01 10:10
    Here is a hastily prepared example that reads all three axes. It's based loosely on the Spin example from the KickStarts. It can be cleaned up when moved into a library for the gyro.
    #include "simpletools.h"                          // Include simpletools header
    
    unsigned char addrW   = 0xd2;                     // I2C write address
    unsigned char addrR   = 0xd3;                     // I2C read address
    
    // L3G4200D register addresses & commands.   
    // See device datasheet section 7 for more info.
    unsigned char devId   = 0x0f;                     // Device ID
    unsigned char ctrl1   = 0x20;                     // Control reg 1
    unsigned char cfg1    = 0b00011111;               // 100 Hz, 25 cutoff, power up, axes enabled 
    unsigned char ctrl2   = 0x21;
    unsigned char ctrl3   = 0x22;
    unsigned char cfg3    = 0b00001000;               // Enable data polling (I2_DRDY)
    unsigned char ctrl4   = 0x23;
    unsigned char cfg4    = 0b10000000;               // Block until read, big endian
    unsigned char status  = 0x27;
    unsigned char xL      = 0x28;                     // Reg for x low byte
                                                      // Next 5 bytes xH, yL, yH, zL, xH
    
    unsigned char reply;                              // Single byte reply
    char xyz[6];                                      // XYZ data array.
    int x, y, z;                                      // Axis variables
    
    i2c *bus;                                         // Declare I2C bus
    
    int main()                                        // main function
    {
      bus = i2c_newbus(5, 4, 0);                      // New I2C bus SCL=P5, SDA=P4
    
      int n;                                          // Setup + count bytes
      n = i2c_out(bus, addrW, &ctrl3, 1, &cfg3, 1);
      n += i2c_out(bus, addrW, &ctrl4, 1, &cfg4, 1);
      n += i2c_out(bus, addrW, &ctrl1, 1, &cfg1, 1);
    
      if(n != 9)                                      // Check bytes
      {
        print("Bytes should be 9, but was %d.", n);
        while(1);
      }
    
      while(1)                                        // Main loop
      {
        int ready = 0;                                // Wait until ready
        while(!ready)
        {
          i2c_in(bus, addrR, &status, 1, &ready, 1);
          ready = 1 & (ready >>= 3);
        }
    
        for(int i = 0; i < 6; i++)                    // Get axis bytes
        {
          int regAddr = xL + i;
          i2c_in(bus, addrR, &regAddr, 1, &xyz[i], 1);
        }
    
        // Bytes to int
        x = (int) (short) ((xyz[1] << 8) + xyz[0]) / 114;  
        y = (int) (short) ((xyz[3] << 8) + xyz[2]) / 114;
        z = (int) (short) ((xyz[5] << 8) + xyz[4]) / 114;
    
        printf("%c x = %d, y = %d, zL = %d%c\n\n",    // Display values
                HOME, x, y, z, CLREOL);
    
        pause(250);    
      }
    }
    
Sign In or Register to comment.