i2c accelerometer problems

05 Dec 2009 . Edited: 05 Dec 2009

I'm having some trouble connecting to my Crodnet KXPS6 accelerometer. Datasheet

It's already got the pullup resistors, and it's wired as follows:

mbed - accelerometer
vout-vcc
gnd-gnd
p28-sda
p27-scl
p26-enable

ADDR is low (grounded), and CS is high (vcc on accelerometer).

Code so far:

 

    DigitalOut enable(p26);    //enable pin
    I2C i2c(p28, p27);  // accelerometer
    
    enable = 1; //set enable pin to 1
    i2c.frequency(400000); //set frequency to 400 KHz
    
    const int CTRL_REGB = 0x0D;
    const int CTRL_REGC = 0x0C;
    
    char data[12];
    
    data[0] = 0x42;
    i2c.write(CTRL_REGB, data, 1); // register CTRL_REGB to 0x42
    
    wait(0.1);
    
    data[0] = 0x00;
    i2c.write(CTRL_REGC, data, 1); // register CTRL_REGC to 0x00
    
    const int address = 0x97;
    
    for(int i = 0; i < 10; i++){
        pc.printf("Data: '%s'\n",i2c.read(address, data, 2));
        wait(0.1);
    }

    enable = 0;

All I get is:

 

"Data: '/'" printed 10 times. In binary it's 00101111, or 47 in decimal. I'm not sure what it means.

Any tips?

05 Dec 2009 . Edited: 05 Dec 2009

I tried this:

 

    DigitalOut enable(p26);    //enable pin
    I2C i2c(p28, p27);         // accelerometer
    
    enable = 1; //set enable pin to 1
    i2c.frequency(400000); //set frequency to 400 KHz
    
    const int address = 0x97; //slave address
    const int CTRL_REGB = 0x0D;
    const int CTRL_REGC = 0x0C;
    
    char data[12];
    
    data[0] = 0x00;
    i2c.write(address, data, 1); // tell accelerometer i want to talk to it?
    
    wait(0.1);
    
    data[0] = 0x42;
    i2c.write(CTRL_REGB, data, 1); // register CTRL_REGB to 0x42 (start condition)
    
    wait(0.1);
    
    data[0] = 0x00;
    i2c.write(CTRL_REGC, data, 1); // register CTRL_REGC to 0x00 (start condition)

    wait(0.1);
    
    const int XOUT_H = 0x00; //x high register
    const int XOUT_L = 0x01; //x low register

    for(int i = 0; i < 10; i++){
        pc.printf("x high: '%s'\n", i2c.read(XOUT_H, data, 12));
        wait(0.1);
        pc.printf("x low: '%s'\n", i2c.read(XOUT_L, data, 12));
        wait(0.1);
    }

    enable = 0;

And all I get is:

x high: '/'
x low: '/'

etc

Should I be polling instead of waiting? (I'm sure there are other problems)

05 Dec 2009 . Edited: 05 Dec 2009

I see several issues here.

1) Slave address is incorrect
2) you need to use the slave address in every transaction, not just the first one.
3) you're printing binary data as string

The Slave Address associated with the KXPS5 is 001100X, where the programmable bit, X, is determined by the assignment of ADDR (pin 3) to GND or Vdd.

Since ADDR=GND this seems to mean that X=0. 0011000 is 0x18, however mbed's I2C class needs the full 8-bit address (with the r/w bit added), so that will be 0x30.

Try the following sequence:

address = 0x30;
data[0] = CTRL_REGB;
data[1] = 0xC2; //set CLKhld=1 so that we're forced to wait until a/d finishes
i2c.write(address, data, 2);
data[0] = CTRL_REGC;
data[1] = 0;
i2c.write(address, data, 2);
data[0] = XOUT_H; // read x msb
i2c.write(address, data, 1); // send register number
i2c.read(address, data, 1); // data[0] <- x msb
int msb = data[0];
data[0] = XOUT_L; // read x lsb
i2c.write(address, data, 1); // send register number
i2c.read(address, data, 1);   // data[0] <- x msb
int lsb = data[0];
printf("x: %d\n", msb<<8 | lsb);

05 Dec 2009 . Edited: 05 Dec 2009

It might be useful to create helper functions for reading and writing registers. E.g.

const int address = 0x30;

// write value into register regno, return success
bool write_reg(int regno, int value)
{
  char data[2] = {regno, value}; 
  return i2c.write(address, data, 2) == 0;
}

// read value from register regno, return success
bool read_reg(int regno, int *value)
{
  char data = regno; 
  if (i2c.write(address, &data, 1) == 0 && i2c.read(address, &data, 1) == 0)
  {
    *value = data;
    return true;
  }
  return false;
}

// read complete value of X axis, return it or -1 on failure
int read_x()
{
  int low, high;
  if ( read_reg(XOUT_H, &high) && read_reg(XOUT_L, &low) )
    return high<<8 | low;
  else
    return -1;
}
05 Dec 2009

And then use it like this:

write_reg(CTRL_REGB, 0xC2);
write_reg(CTRL_REGC, 0x00);
printf("X axis: %d\n", read_x());

06 Dec 2009

Thank you so much! It makes so much more sense now.

I got values around 32 000 for the X and Y, and 42 000 for the Z axis. The information I have says I should be getting values between 0 and around 4000.

The output is an unsigned 12 bit number. 0 g is always 2048.

0g is always (2¹²) / 2 = 2048
Sensitivity = 20% of 4096 / 2 / 6 = 546 counts/g

Any ideas on normalising to g values? (I'm assuming X and Y should be 0g, and the Z axis should be 1g?).

06 Dec 2009 . Edited: 06 Dec 2009

I logged 1000 results of the accelerometer sitting on a flat surface, then turned it over onto and onto each side. Assuming it's a linear scale (I think this might be wrong), to get roughly accurate figures I used:

g value = (raw - 32768) / 9000

06 Dec 2009

I've looked at the datasheet again and it seems the lsb register has valid data only in top 4 bits. So the read_x function should be changed to:

return (high<<4) | ((low>>4)&0xF);

Then you will get 2048 instead of 32768.

06 Dec 2009

Ah thankyou. It's working beautifully, with very little fluctuation.

I'll write a moving average function to iron out the occasional spike, and post it here when it's done.