LSM6DS33 I2C communication returning zero for unclear reason

Go To Last Post
17 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello all, I am struggling with actually collecting data from my LSM6DS33(I'm using the pololu module).

I'm using an Arduino nano clone to communicate with it and am developing all of this through PlatformIO.

 

So far I am able to communicate with it as I've successfully gotten its slave address (through requesting the contents of its WHO_AM_I register).

However, when I follow the datasheet's process for getting accelerometer data, I can only receive zeroes regardless of if the sensor is moving or not.

 

Below is the relevant code. I am using this library to handle I2C.

 

Any help is much appreciated!

 

#include <avr/io.h>
#include <util/delay.h>
#include "serial.h" //my own header file for printing to terminal
#include "i2c_master.h"//the I2c library mentioned above

#define LSM_READ 0XD5 //LSM read and write addresses
#define LSM_WRITE 0XD4

#define MYUBRR F_CPU/16/BAUD-1 //serial communication

#define OUTX_L_XL 0x28 //accel x-axis register

void lsmInit(uint8_t readorwrite){
	i2c_init();
	connected = 5;//sanity check

	connected = i2c_start(readorwrite);
	if(connected == 0){
		// Sprint("no errors! yay!");
	}
	else if(connected == 1){
		Sprint("oops, ther's an error");
	}
	else if(connected == 5){
		Sprint("the value didn't change");
	}
}

void accSetup(){
    //set up accel data
    lsmInit(LSM_WRITE);//into writing mode

    i2c_write( CTRL9_XL);// Acc X, Y, Z axes enabled
    i2c_write(0x38);

    i2c_write( CTRL1_XL); // Acc = 416Hz (High-Performance mode)
    i2c_write(0x60);

    i2c_write(INT1_CTRL); // Acc Data Ready interrupt on INT1
    i2c_write(0x01);

    i2c_stop();
}

uint16_t read16(){//reads 16 bits from where previously asked
    lsmInit(LSM_READ);
    uint16_t reg = ((uint8_t)i2c_read_ack())<<8;
    reg |= i2c_read_ack();
    return reg;
}

void startAt(uint8_t r){
    lsmInit(LSM_WRITE);
    i2c_stop();
}

uint16_t readReg(uint8_t reg){
    startAt(reg); //assign register.
    uint16_t out = read16();
    Sprint("\ndata: ");
    numPrint(out);
    i2c_stop();
    return out;
}

void USART_Init(unsigned int ubrr)
{
  /*Set baud rate */
  /* UBRR0H contains the 4 most significant bits of the
    baud rate. UBRR0L contains the 8 least significant
    bits.*/
  UBRR0H = (unsigned char)(ubrr >> 8);
  UBRR0L = (unsigned char)ubrr;

  /*Enable transmitter */
  UCSR0B = (1 << TXEN0);

  /* Set frame format: 8data */
  UCSR0C = (1 << USBS0) | (3 << UCSZ00);
}

int main( void )
{
    USART_Init(MYUBRR);
    i2c_init();
    accSetup();

    Sprint ("test ptint\n"); //sanity checks
    numPrint(123);

    while(1)
    {
        readReg(OUTX_L_XL);//WHO_AM_I is 0X0F
        _delay_ms(1000);
    }
    return 0;
}

 

This topic has a solution.
Last Edited: Thu. Jul 23, 2020 - 03:57 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

beingPurple wrote:

#define LSM_READ 0XD5 //LSM read and write addresses
#define LSM_WRITE 0XD4

First Welcome to AVRFreaks!

 

Two quick questions:

1) As I'm not familiar with that I2C lib, does it expect to use 8 bit addresses  I2C addresses you have?  Some use 7 bit addresses (arduino)

2) Can you post a schematic of your setup, it will help answer a ton of questions about your setup, or else we start playing 20 questions game.

 

one more question,  why are you calling i2c_init() more then once?

one more question, what is the result returned when you call i2c_start()?

 

Jim

One of the nice things about I2C is it will tell you when something does not work, but you have to be listening

 

 

(Possum Lodge oath) Quando omni flunkus, moritati.

"I thought growing old would take longer"

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thank you for the welcome!!

 

1. I'm using the schematic outlined here:

Arduino   LSM6 board
-------   ----------
    3V3 - VIN
    GND - GND
    SDA - SDA
    SCL - SCL - GND (this affects the slave address, so I keep it tied to GND
          SDO - GND (this affects the slave address, so I keep it tied to GND

2. It doesn't say, but all of the functions require 8 bit numbers to work, so I can only guess 8-bit.

3. Thanks for catching that! Merely a typo! After removing the second i2c_init(), nothing changes. And i2c_start() returns a zero.

 

edit: I have updated the schematic to be more accurate as I hadn't looked at my breadboard setup appropriately

Last Edited: Wed. Jul 22, 2020 - 08:10 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

beingPurple wrote:

SCL - SCL - GND (this affects the slave address, so I keep it tied to GND)

That does not sound right, I2C requires the SCL clock to function?    Do not tie this to gnd!

 

Then try this I2C scanner sketch to see what address is reported, and let us know what it says.

// --------------------------------------
// i2c_scanner
//
// Version 1
//    This program (or code that looks like it)
//    can be found in many places.
//    For example on the Arduino.cc forum.
//    The original author is not know.
// Version 2, Juni 2012, Using Arduino 1.0.1
//     Adapted to be as simple as possible by Arduino.cc user Krodal
// Version 3, Feb 26  2013
//    V3 by louarnold
// Version 4, March 3, 2013, Using Arduino 1.0.3
//    by Arduino.cc user Krodal.
//    Changes by louarnold removed.
//    Scanning addresses changed from 0...127 to 1...119,
//    according to the i2c scanner by Nick Gammon
//    http://www.gammon.com.au/forum/?id=10896
// Version 5, March 28, 2013
//    As version 4, but address scans now to 127.
//    A sensor seems to use address 120.
// Version 6, November 27, 2015.
//    Added waiting for the Leonardo serial communication.
// 
//
// This sketch tests the standard 7-bit addresses
// Devices with higher bit address might not be seen properly.
//

#include <Wire.h>


void setup()
{
  Wire.begin();

  Serial.begin(9600);
  while (!Serial);             // Leonardo: wait for serial monitor
  Serial.println("\nI2C Scanner");
}


void loop()
{
  byte error, address;
  int nDevices;

  Serial.println("Scanning...");

  nDevices = 0;
  for(address = 1; address < 127; address++ ) 
  {
    // The i2c_scanner uses the return value of
    // the Write.endTransmisstion to see if
    // a device did acknowledge to the address.
    Wire.beginTransmission(address);
    error = Wire.endTransmission();

    if (error == 0)
    {
      Serial.print("I2C device found at address 0x");
      if (address<16) 
        Serial.print("0");
      Serial.print(address,HEX);
      Serial.println("  !");

      nDevices++;
    }
    else if (error==4) 
    {
      Serial.print("Unknown error at address 0x");
      if (address<16) 
        Serial.print("0");
      Serial.println(address,HEX);
    }    
  }
  if (nDevices == 0)
    Serial.println("No I2C devices found\n");
  else
    Serial.println("done\n");

  delay(1000);           // wait 5 seconds for next scan
}

Jim

 

 

(Possum Lodge oath) Quando omni flunkus, moritati.

"I thought growing old would take longer"

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ok, found DS, the link you gave was to an app note, not the DS. 

You have the correct 8 bit address(s), now I need to verify your lib uses 8 bit addresses.

 

Jim

 

 

(Possum Lodge oath) Quando omni flunkus, moritati.

"I thought growing old would take longer"

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

beingPurple wrote:

uint16_t read16(){//reads 16 bits from where previously asked
    lsmInit(LSM_READ);
    uint16_t reg = ((uint8_t)i2c_read_ack())<<8;
    reg |= i2c_read_ack();
    return reg;
}

 

This does not look right, the last read of data from an I2C device must be a read_nak(), or the first read if only one byte is read!

The nak tells the I2C slave device to release the bus so the master can talk, usually to send stop, else the bus hangs with two devices trying to send at the same time.

 

Jim

 

 

(Possum Lodge oath) Quando omni flunkus, moritati.

"I thought growing old would take longer"

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The results from i2c scanner:

Scanning...
I2C device found at address 0x6A  !
done

And thank you for the information about the read_nack(). I've since made that change to my read16() function, but still have the same results.

 

Also, turns out i misread my breadboard. It is SDO that is tied to GND, not SCL. Sorry for the confusion.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

beingPurple wrote:

Scanning...
I2C device found at address 0x6A  !
done

Ok, the arduino wire lib uses 7 bit addresses, that xlates to D4/D5 using 8 bit address, that matches what you had in the OP.

And proves the h/w works.   So the issue is in your code or the use of the unknown (by me) lib.

Try something simple, similar to the scanner sketch, just try to start/stop using your lib and see if you get a positive response.

post your results.

 

Jim

 

 

(Possum Lodge oath) Quando omni flunkus, moritati.

"I thought growing old would take longer"

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Something to check.

You pass a value to startAt which is never used.

void startAt(uint8_t r){
    lsmInit(LSM_WRITE);
    i2c_stop();
}

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

It is SDO that is tied to GND

That sounds like an OUTPUT being tied to GND??

 

Edit so it depends on the CS pin if it's an output or input. It also selects whether it's I2C or 3 wire SPI comms

5 CS I2C/SPI mode selection
(1: SPI idle mode / I2C communication enabled;
0: SPI communication mode / I2C disabled

 

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

Last Edited: Wed. Jul 22, 2020 - 10:38 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I am able to successfully communicate with the sensor. Using the process mentioned above for registers 0X0F and the FIFO registers to produce data(although the FIFO is unchanging. I haven't looked deeply into setting it up properly though because I figured that getting data from more specific registers might be easier). So as far as I can tell, reading and writing to it do work, just not the registers that actually hold data, which return zero instead.

 

And thank you for catching the startAt() error. I had deleted a line in my post when cleaning up my code to post here. Here is the function proper:

void startAt(uint8_t r){
	lsmInit(LSM_WRITE);//writing mode
	i2c_write(r);
	i2c_stop();
}

Regarding SDO, I was curious about that as well. I tied it to ground due to the recommendations of section 6.1.1 paragraph 2 here (which is the datasheet for the chip itself. The link in the OP is for the module I'm using.)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

 

So with SDO connected to GND and CS high the address is 0x6A as you also show above.

 

The Slave ADdress (SAD) associated to the LSM6DS33 is 110101xb. The SDO/SA0 pin

can be used to modify the less significant bit of the device address. If the SDO/SA0 pin is

connected to the supply voltage, LSb is'1' (address 1101011b); else if the SDO/SA0 pin is

connected to ground, the LSb value is '0' (address 1101010b). This solution permits to

connect and address two different inertial modules to the same I2C bus.
 

CS is pulled high by R1 enabling I2C and making SDO an input

 

 

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

Last Edited: Thu. Jul 23, 2020 - 04:11 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Have you set the sample rate and full scale range and such?

 

Best to set it to continuous mode and configure one of the INT outputs for a signal on "data available", then read it when you get that int. At other times, the data values are unpredictable.

 

Jim 

 

Until Black Lives Matter, we do not have "All Lives Matter"!

 

 

This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

beingPurple wrote:

void accSetup(){
    //set up accel data
    lsmInit(LSM_WRITE);//into writing mode

    i2c_write( CTRL9_XL);// Acc X, Y, Z axes enabled
    i2c_write(0x38);

    i2c_write( CTRL1_XL); // Acc = 416Hz (High-Performance mode)
    i2c_write(0x60);

    i2c_write(INT1_CTRL); // Acc Data Ready interrupt on INT1
    i2c_write(0x01);

    i2c_stop();
}

You might need to peform each register write as a separate 'complete' transaction.

At the moment, the first write CTRL9_XL sets the register address on the device.

Value 0x38 is then writen to that address.

But the next write that is intended to set the register address to CTRL1_XL might actually be writing whatever value CTRL1_XL is to the next address after CTRL9_XL (asuming the internal address on the device is auto incremented).

In other words, you might need to do

start

sla_w

write reg addr

write data

stop

 

start

sla_w

write reg addr

write data

stop

 

etc.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

MrKendo, your most recent post wound up actually being the solution! 

ka7ehk, I have not set that up yet but setting my LSM into continuous mode (if it's not there already) and configuring a "data available" interrupt will definitely be my next steps.

Thank you everyone for the help!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I don't think that you will get valid data until you  do that. These are relatively complex chips and you have to configure it correctly to get anything useful out. That is a non-trivial task. It took me quite a while (a week, at least) just to get an ST 3-axis accelerometer running and many tweaks (over several months) after that to get it "right".

 

Jim

 

Until Black Lives Matter, we do not have "All Lives Matter"!

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Good to know! the data does seem up update on its own when I access the individual output registers, but I will need to use the device's FIFO buffer for my application anyway so I will set it to use continuous mode and interrupts for new data. So, thank you!