[SOLVED] Problem interfacing with IIS328DQ Accelerometer using I2C/TWI

Hi everyone!

I have tried to solve this myself with no result. Also tried googling it, nothing. Hope someone can help me here.

I am trying to interface my Arduino UNO with the IIS328DQ STMicroelectronics accelerometer using I2C protocol.

My code here:

#include <Wire.h>

#define IIS328DQ_REG_WHO_AM_I    0x0F  // Chip identifier
#define IIS328DQ_REG_CTRL_REG1    0x20  // Set mode, ODR and enabled axes
#define IIS328DQ_REG_CTRL_REG3    0x22  // Interrupt control register
#define IIS328DQ_REG_CTRL_REG4    0x23  // Set scale
#define IIS328DQ_REG_OUT_X_L    0x28  // X-axis acceleration data in 2's complement. LSB
#define IIS328DQ_REG_OUT_X_M    0x29  // X-axis acceleration data in 2's complement. MSB
#define IIS328DQ_REG_OUT_Y_L    0x2A  // Y-axis acceleration data in 2's complement. LSB
#define IIS328DQ_REG_OUT_Y_M    0x2B  // Y-axis acceleration data in 2's complement. MSB
#define IIS328DQ_REG_OUT_Z_L    0x2C  // Z-axis acceleration data in 2's complement. LSB
#define IIS328DQ_REG_OUT_Z_M    0x2D  // Z-axis acceleration data in 2's complement. MSB
#define IIS328DQ_REG_INT1_CFG   0X30  // Configuration interrupt 1
#define IIS328DQ_REG_INT1_SRC   0X31  // Source interrupt 1
#define IIS328DQ_REG_INT1_THS   0X32  // Threshold interrupt 1
#define IIS328DQ_REG_INT1_DURATION  0X33
#define IIS328DQ_PM_NORMAL      0x20
#define IIS328DQ_ODR_100_CF_74    0x08
#define IIS328DQ_X_ENABLE      0x01  // Enable X-axis
#define IIS328DQ_Y_ENABLE     0x02  // Enable Y-axis
#define IIS328DQ_Z_ENABLE     0x04
#define IIS328DQ_FS_2        0x00  // +/- 2g
#define IIS328DQ_FS_4       0x10  // +/- 4g
#define IIS328DQ_FS_8       0x30  // +/- 8g
#define IIS328DQ_BDU_ENABLE      0X80

const int READ  = 0x31; // Reading slave address
const int WRITE = 0x30; //Writing slave address
const int ADDR  = 0X18; // Address I found in another code in the internet
const int X_L = 0x28; //Lsb of the X data
const int X_M = 0x29; //Msb of the X data
const int Y_L = 0x2A;
const int Y_M = 0x2B;
const int Z_L = 0x2C;
const int Z_M = 0x2D;
byte X0,X1,Y0,Y1,Z0,Z1;
short X,Y,Z; //X, Y, Z all bits data

void setup() {
    Serial.begin(19200);
    Wire.begin();
    delay(100);

    // Here goes the configuration
    uint8_t ctrl_reg1 = (IIS328DQ_ODR_100_CF_74 | IIS328DQ_PM_NORMAL | IIS328DQ_Z_ENABLE | IIS328DQ_Y_ENABLE | IIS328DQ_X_ENABLE);
    Wire.beginTransmission(WRITE);
    Wire.write(IIS328DQ_REG_CTRL_REG1);
    Wire.write(ctrl_reg1);
    Wire.endTransmission();

    //Scale and BDU
    uint8_t ctrl_reg4 = IIS328DQ_FS_2 | IIS328DQ_BDU_ENABLE;
    Wire.beginTransmission(WRITE);
    Wire.write(IIS328DQ_REG_CTRL_REG4);
    Wire.write(ctrl_reg4);
    Wire.endTransmission();

    //Interrupt control
    Wire.beginTransmission(WRITE);
    Wire.write(IIS328DQ_REG_CTRL_REG3);
    Wire.write(0x00);
    Wire.endTransmission();

    //Scale threshold
    Wire.beginTransmission(WRITE);
    Wire.write(IIS328DQ_REG_INT1_THS);
    Wire.write(0x1A);
    Wire.endTransmission();

    //Duration
    Wire.beginTransmission(WRITE);
    Wire.write(IIS328DQ_REG_INT1_DURATION);
    Wire.write(100);
    Wire.endTransmission();

    //ZLIE, YLIE
    Wire.beginTransmission(WRITE);
    Wire.write(IIS328DQ_REG_INT1_CFG);
    Wire.write(0x04);
    Wire.endTransmission();

}

void loop() {
    // Start reading measures (check protocol in datasheet)
    Wire.beginTransmission(WRITE);
    Wire.write(IIS328DQ_REG_WHO_AM_I);
    Wire.endTransmission(0);
    Wire.beginTransmission(READ);
    byte whoami = Wire.read();
    Wire.endTransmission();


    Serial.println(whoami,HEX);

    delay(1000);
}

According to its datasheet, to perform a writing of 1 byte from master (UNO) to slave (IIS328DQ) you need to follow the protocol on page 18 (don't know how to post a screenshot).

And to perform a reading of 1 byte, the protocol of the table in page 19.

The datasheet is here: IIS328DQ datasheet

I don't know the low level instructions of the Wire library, so I don't know what it is exactly doing.

My results:

First, I tried directly to read from X, Y, Z registers. But instead getting actual data, what I got was "0xFFFFFF" from all the registers.

Then I changed the code to just try and get the WHO_I_AM register value, which is 0x32. But I am getting again, 0xFF.

My connections:

The accelerometer has 2 configurable inputs, CS and SD0. In my case they are: CS = HIGH (Vdd) and SDO = LOW (Gnd). The rest is just power supply and SDA, SCL lines.

My guess:

I am doing something wrong with the order of the operations. I also tried Wire.requestFrom() instead of the reading operations.

Please, any help will be welcome.

Thank you all!

AGB

Here is the solution:

#include <Wire.h>

#define IIS328DQ_REG_WHO_AM_I    0x0F  // Chip identifier
#define IIS328DQ_REG_CTRL_REG1    0x20  // Set mode, ODR and enabled axes
#define IIS328DQ_REG_CTRL_REG3    0x22  // Interrupt control register
#define IIS328DQ_REG_CTRL_REG4    0x23  // Set scale
#define IIS328DQ_REG_OUT_X_L    0x28  // X-axis acceleration data in 2's complement. LSB
#define IIS328DQ_REG_OUT_X_M    0x29  // X-axis acceleration data in 2's complement. MSB
#define IIS328DQ_REG_OUT_Y_L    0x2A  // Y-axis acceleration data in 2's complement. LSB
#define IIS328DQ_REG_OUT_Y_M    0x2B  // Y-axis acceleration data in 2's complement. MSB
#define IIS328DQ_REG_OUT_Z_L    0x2C  // Z-axis acceleration data in 2's complement. LSB
#define IIS328DQ_REG_OUT_Z_M    0x2D  // Z-axis acceleration data in 2's complement. MSB
#define IIS328DQ_REG_INT1_CFG   0X30  // Configuration interrupt 1
#define IIS328DQ_REG_INT1_SRC   0X31  // Source interrupt 1
#define IIS328DQ_REG_INT1_THS   0X32  // Threshold interrupt 1
#define IIS328DQ_REG_INT1_DURATION  0X33
#define IIS328DQ_PM_NORMAL      0x20
#define IIS328DQ_ODR_100_CF_74    0x08
#define IIS328DQ_X_ENABLE      0x01  // Enable X-axis
#define IIS328DQ_Y_ENABLE     0x02  // Enable Y-axis
#define IIS328DQ_Z_ENABLE     0x04
#define IIS328DQ_FS_2        0x00  // +/- 2g
#define IIS328DQ_FS_4       0x10  // +/- 4g
#define IIS328DQ_FS_8       0x30  // +/- 8g
#define IIS328DQ_BDU_ENABLE      0X80

const byte READ  = 0x31;
const byte WRITE = 0x30;
const byte ADDR  = 0X18;
const byte X_L = 0x28;
const byte X_M = 0x29;
const byte Y_L = 0x2A;
const byte Y_M = 0x2B;
const byte Z_L = 0x2C;
const byte Z_M = 0x2D;
byte X0,X1,Y0,Y1,Z0,Z1;
short X,Y,Z;

void setup() {
    Serial.begin(19200);
    Wire.begin();
    delay(100);

    // Here goes the configuration data.
    uint8_t ctrl_reg1 = (IIS328DQ_ODR_100_CF_74 | IIS328DQ_PM_NORMAL | IIS328DQ_Z_ENABLE | IIS328DQ_Y_ENABLE | IIS328DQ_X_ENABLE);
    Wire.beginTransmission(ADDR);
    Wire.write(IIS328DQ_REG_CTRL_REG1);
    Wire.write(ctrl_reg1);
    Wire.endTransmission();

    //Scale and BDU
    uint8_t ctrl_reg4 = IIS328DQ_FS_2 | IIS328DQ_BDU_ENABLE;
    Wire.beginTransmission(ADDR);
    Wire.write(IIS328DQ_REG_CTRL_REG4);
    Wire.write(ctrl_reg4);
    Wire.endTransmission();

    //Interrupt control
    Wire.beginTransmission(ADDR);
    Wire.write(IIS328DQ_REG_CTRL_REG3);
    Wire.write(0x00);
    Wire.endTransmission();

    //Scale threshold
    Wire.beginTransmission(ADDR);
    Wire.write(IIS328DQ_REG_INT1_THS);
    Wire.write(0x1A);
    Wire.endTransmission();

    //Duration
    Wire.beginTransmission(ADDR);
    Wire.write(IIS328DQ_REG_INT1_DURATION);
    byte duration = 100;
    Wire.write(duration);
    Wire.endTransmission();

    //ZLIE, YLIE
    Wire.beginTransmission(ADDR);
    Wire.write(IIS328DQ_REG_INT1_CFG);
    Wire.write(0x04);
    Wire.endTransmission();

}

void loop() {
    // Start reading measures (check protocol in datasheet)
/*    Wire.beginTransmission(ADDR);
    Wire.write(IIS328DQ_REG_WHO_AM_I);
    Wire.endTransmission();
    Wire.beginTransmission(ADDR);
    Wire.requestFrom(ADDR,1);
    byte whoami = Wire.read();
    Wire.endTransmission();
    Serial.println(whoami,HEX);
*/

    // X axis measure
    Wire.beginTransmission(ADDR);
    Wire.write(X_L); // X axis lsb register
    Wire.endTransmission();
    Wire.beginTransmission(ADDR);
    Wire.requestFrom(ADDR,1);
    X0 = Wire.read();
    Wire.endTransmission();

    Wire.beginTransmission(ADDR);
    Wire.write(X_M); // X axis msb register
    Wire.endTransmission();
    Wire.beginTransmission(ADDR);
    Wire.requestFrom(ADDR,1);
    X1 = Wire.read(); 
    Wire.endTransmission();

    // Y axis measure
    Wire.beginTransmission(ADDR);
    Wire.write(Y_L); // X axis lsb register
    Wire.endTransmission();
    Wire.beginTransmission(ADDR);
    Wire.requestFrom(ADDR,1);
    Y0 = Wire.read();
    Wire.endTransmission();

    Wire.beginTransmission(ADDR);
    Wire.write(Y_M); // X axis msb register
    Wire.endTransmission();
    Wire.beginTransmission(ADDR);
    Wire.requestFrom(ADDR,1);
    Y1 = Wire.read(); 
    Wire.endTransmission();

    // Z axis measure
    Wire.beginTransmission(ADDR);
    Wire.write(Z_L); // X axis lsb register
    Wire.endTransmission();
    Wire.beginTransmission(ADDR);
    Wire.requestFrom(ADDR,1);
    Z0 = Wire.read();
    Wire.endTransmission();

    Wire.beginTransmission(ADDR);
    Wire.write(Z_M); // X axis msb register
    Wire.endTransmission();
    Wire.beginTransmission(ADDR);
    Wire.requestFrom(ADDR,1);
    Z1 = Wire.read(); 
    Wire.endTransmission();

    X = (X1 << 8) | X0;
    Y = (Y1 << 8) | Y0;
    Z = (Z1 << 8) | Z0;

    X = X * 0.98 * 9.80665 / 1000;
    Y = Y * 0.98 * 9.80665 / 1000;
    Z = Z * 0.98 * 9.80665 / 1000;

    /* Serial.print("\t");
    Serial.print(Y,HEX);
    Serial.print("\t");
    Serial.println(Z,HEX); */
    Serial.print(X,DEC);
    Serial.print("\t");
    Serial.print(Y,DEC);
    Serial.print("\t");
    Serial.println(Z,DEC);
                          
    delay(100);
}

I still have to make an autoincreasing reading so that I dont need to perform the same operation 6 times, but this already works.

Improved solution (in a week or so I maybe do a library, much more neat):

#include <Wire.h>

// Accelerometer adresses and variables
#define IIS328DQ_REG_WHO_AM_I       0x0F  // Chip identifier
#define IIS328DQ_REG_CTRL_REG1      0x20  // Set mode, ODR and enabled axes
#define IIS328DQ_REG_CTRL_REG3      0x22  // Interrupt control register
#define IIS328DQ_REG_CTRL_REG4      0x23  // Set scale
#define IIS328DQ_REG_OUT_X_L        0x28  // X-axis acceleration data in 2's complement. LSB
#define IIS328DQ_REG_OUT_X_M        0x29  // X-axis acceleration data in 2's complement. MSB
#define IIS328DQ_REG_OUT_Y_L        0x2A  // Y-axis acceleration data in 2's complement. LSB
#define IIS328DQ_REG_OUT_Y_M        0x2B  // Y-axis acceleration data in 2's complement. MSB
#define IIS328DQ_REG_OUT_Z_L        0x2C  // Z-axis acceleration data in 2's complement. LSB
#define IIS328DQ_REG_OUT_Z_M        0x2D  // Z-axis acceleration data in 2's complement. MSB
#define IIS328DQ_REG_INT1_CFG       0X30  // Configuration interrupt 1
#define IIS328DQ_REG_INT1_SRC       0X31  // Source interrupt 1
#define IIS328DQ_REG_INT1_THS       0X32  // Threshold interrupt 1
#define IIS328DQ_REG_INT1_DURATION  0X33
#define IIS328DQ_PM_NORMAL          0x20
#define IIS328DQ_ODR_100_CF_74      0x08
#define IIS328DQ_X_ENABLE           0x01  // Enable X-axis
#define IIS328DQ_Y_ENABLE           0x02  // Enable Y-axis
#define IIS328DQ_Z_ENABLE           0x04
#define IIS328DQ_FS_2               0x00  // +/- 2g
#define IIS328DQ_FS_4               0x10  // +/- 4g
#define IIS328DQ_FS_8               0x30  // +/- 8g
#define IIS328DQ_BDU_ENABLE         0X80
#define IIS328DQ_SLAVE_ADDR         0X18
#define MULTIPLE_BYTES_MASK         0X80
byte X0,X1,Y0,Y1,Z0,Z1;
byte axis[6];
short X,Y,Z;

// Temperature sensor addresses and variables
const int TADDR = 0x40;
int T0,T1;
double T,T_out,X_prec,Y_prec,Z_prec;

void setup() {
    Serial.begin(19200);
    Wire.begin();
    delay(100);
    Wire.beginTransmission(TADDR);
    Wire.endTransmission();

    // Accelerometer configuration registers
    uint8_t ctrl_reg1 = (IIS328DQ_ODR_100_CF_74 | IIS328DQ_PM_NORMAL | IIS328DQ_Z_ENABLE | IIS328DQ_Y_ENABLE | IIS328DQ_X_ENABLE);
    writeToAccel(IIS328DQ_REG_CTRL_REG1,ctrl_reg1);
    
    //Scale to +/- 2g and BDU
    uint8_t ctrl_reg4 = IIS328DQ_FS_2 | IIS328DQ_BDU_ENABLE;
    writeToAccel(IIS328DQ_REG_CTRL_REG4,ctrl_reg4);

    //Interrupt control
    writeToAccel(IIS328DQ_REG_CTRL_REG3,0x00);

    //Scale threshold
    writeToAccel(IIS328DQ_REG_INT1_THS,0x1A);

    //Interrupt duration
    writeToAccel(IIS328DQ_REG_INT1_DURATION,0x64);

    //ZLIE, YLIE
    writeToAccel(IIS328DQ_REG_INT1_CFG,0x04);

}

void loop() {
    // Temperature measurement
    Wire.beginTransmission(TADDR);
    Wire.write(0xE3);
    Wire.endTransmission();
    Wire.requestFrom(TADDR,2);

    if(Wire.available() <= 2){
        T0 = Wire.read();
        T1 = Wire.read();
        T0 = T0<<8;
        T_out = T0|T1;
    }

    T = (175.72 * T_out) / 65536;                        
    T = T - 46.85;

    // Multiple bytes mask enables reg_address automatic increase
    Wire.beginTransmission(IIS328DQ_SLAVE_ADDR);
    Wire.write(IIS328DQ_REG_OUT_X_L  | MULTIPLE_BYTES_MASK);
    Wire.endTransmission();
    Wire.beginTransmission(IIS328DQ_SLAVE_ADDR);
    Wire.requestFrom(IIS328DQ_SLAVE_ADDR,6);

    for(int i=0; i<6; i++) {
        axis[i] = Wire.read();
    }

    Wire.endTransmission();

    X = (axis[1] << 8) | axis[0];
    Y = (axis[3] << 8) | axis[2];
    Z = (axis[5] << 8) | axis[4];

    /* CALIBRATION:
        2^16 = 65536 (dinamic range quantification)
        +/- 2g = 4g   ->    65536 / 4 = 16384    */
    X_prec = X * 0.981 / 16384;
    Y_prec = Y * 0.981 / 16384;
    Z_prec = Z * 0.981 / 16384;

    Serial.print("X]");
    Serial.print(X_prec);
    Serial.print("\t");
    Serial.print("Y]");
    Serial.print(Y_prec);
    Serial.print("\t");
    Serial.print("Z]");
    Serial.print(Z_prec);
    Serial.print("\t\t");
    Serial.print("Temp ");
    Serial.print(T);
    Serial.println(" ºC");
                          
    delay(100);
   
}

/* This should be added to a library (.h & .cpp) from here down to the end, not gonna waste time now */

/* WRITE TO ACCEL VIA I2C */
/* In future add IIS328DQ:: */
void writeToAccel(byte reg_address, byte value) {
    Wire.beginTransmission(IIS328DQ_SLAVE_ADDR);
    Wire.write(reg_address);
    Wire.write(value);
    Wire.endTransmission();
}
1 Like