Slightly incorrect and unstable sensor data using Zephyr Sensor API with Accelerometer / Gyro LSM6DS3TR-C

Hello! To summarize my issue, I have a 6 DOF accelerometer/gyroscope [LSM6DS3TR-C]. I am accessing the sensor using SPI and the Zephyr Sensor API, and my readings are somewhat unstable and slightly incorrect - e.g, when not moving, the sensor's gyroscope measures +/- 0.1 - 0.9 rad/s.

My environment:

  • NRF Connect SDK in VSCode - Version 2.1.1
  • Zephyr v3.1.99

I'll attach the relevant code, apologies for the length.

Below is my function for creating the SPI device. 

/* Function for initializing SPI device */
static const struct device *get_spi_device(void)
{
	const struct device *dev = DEVICE_DT_GET(DT_INST(0, st_lsm6dsl));

	if (dev == NULL)
	{
		/* No such node, or the node does not have status "okay". */
		printk("\nError: no device found.\n");
		return NULL;
	}

	if (!device_is_ready(dev))
	{
		printk("\nError: Device \"%s\" is not ready; "
			   "check the driver initialization logs for errors.\n",
			   dev->name);
		return NULL;
	}

	printk("Found device \"%s\", getting sensor data\n", dev->name);
	return dev;
}

I access the sensor data using the Sensor API enums.

void main(void)
{	
    //...
	const struct device *spi_dev = get_spi_device();
	if (spi_dev == NULL) {
		return;
	}

	static struct sensor_value accel_x, accel_y, accel_z, gyro_x, gyro_y, gyro_z;
	
	while(1){
	    /* Returns values m/s^2 */
		sensor_sample_fetch_chan(spi_dev, SENSOR_CHAN_ACCEL_XYZ);
		sensor_channel_get(spi_dev, SENSOR_CHAN_ACCEL_X, &accel_x);
		sensor_channel_get(spi_dev, SENSOR_CHAN_ACCEL_Y, &accel_y);
		sensor_channel_get(spi_dev, SENSOR_CHAN_ACCEL_Z, &accel_z);

		// returns gyro data in rad/s
		sensor_sample_fetch_chan(spi_dev, SENSOR_CHAN_GYRO_XYZ);
		sensor_channel_get(spi_dev, SENSOR_CHAN_GYRO_X, &gyro_x);
		sensor_channel_get(spi_dev, SENSOR_CHAN_GYRO_Y, &gyro_y);
		sensor_channel_get(spi_dev, SENSOR_CHAN_GYRO_Z, &gyro_z);
		
		printk("%d.%d,", (accel_x.val1), abs(accel_x.val2));
		printk("%d.%d,", (accel_y.val1), abs(accel_y.val2));
		printk("%d.%d,", (accel_z.val1), abs(accel_z.val2));
		printk("%d.%d,", (gyro_x.val1), abs(gyro_x.val2));
		printk("%d.%d,", (gyro_y.val1), abs(gyro_y.val2));
		printk("%d.%d\n", (gyro_z.val1), abs(gyro_z.val2));
		
		k_msleep(500);
	}
}


Here is my prj.conf:

# assert will cause soft resets, so set to N for now
CONFIG_ASSERT=n

#run on restart
CONFIG_BOARD_ENABLE_DCDC=n

# # Enable RTT to replace UART
CONFIG_RTT_CONSOLE=y
CONFIG_STDOUT_CONSOLE=y
CONFIG_UART_CONSOLE=n
CONFIG_USE_SEGGER_RTT=y
CONFIG_SHELL_BACKEND_RTT=y

# ACCEL/GYRO PRJ.CONF
CONFIG_I2C=y
CONFIG_SPI=y
CONFIG_SENSOR=y

# Allow math.h
CONFIG_NEWLIB_LIBC=y

# Floating point support
CONFIG_CBPRINTF_FP_SUPPORT=y

# Sensor specific configurations
CONFIG_LSM6DSL=y
CONFIG_LSM6DSL_TRIGGER_NONE=y
CONFIG_LSM6DSL_ACCEL_ODR=8
CONFIG_LSM6DSL_ACCEL_FS=16
CONFIG_LSM6DSL_GYRO_FS=1000
CONFIG_LSM6DSL_GYRO_ODR=8
CONFIG_LSM6DSL_ENABLE_TEMP=y

# Config NFC pins as GPIO based on pins used for SPI
CONFIG_NFCT_PINS_AS_GPIOS=y

And finally, my overlay file.

&pinctrl {
    spi1_default: spi1_default {
		group1 {
			psels = <NRF_PSEL(SPIM_SCK, 0, 12)>,
					<NRF_PSEL(SPIM_MOSI, 0, 13)>,
					<NRF_PSEL(SPIM_MISO, 0, 14)>;
		};
	};

	spi1_sleep: spi1_sleep {
		group1 {
			psels = <NRF_PSEL(SPIM_SCK, 0, 12)>,
					<NRF_PSEL(SPIM_MOSI, 0, 13)>,
					<NRF_PSEL(SPIM_MISO, 0, 14)>;
			low-power-enable;
		};
	};
};

&spi1 {
    compatible = "nordic,nrf-spi";
    status = "okay";
    pinctrl-0 = <&spi1_default>;
    pinctrl-1 = <&spi1_sleep>;
    pinctrl-names = "default", "sleep";
	cs-gpios = < &gpio0 11 GPIO_ACTIVE_LOW >;

    lsm6dsl@0 {
		status="okay";
        compatible = "st,lsm6dsl";
        reg = < 0 >;
        spi-max-frequency = <1000000>;
    };
};

&spi0 {
	status="disabled";
};

&i2c0 {
	status="disabled";
};

My primary concern is the gyroscope values being non-zero and very jittery when I print out the values. I am aware I'm using the library for the LSM6DSL, but I've compared datasheets with the LSM6DS3TR-C and all the registers and conversions are the exact same. Note that my readings are 'almost' correct, in the sense that I can still read G as ~9.81-10 [m/s^2]. 

I tested this exact same sensor (Adafruit LSM6DS3TR-C Breakout) in an Arduino environment and using the Adafruit library for both LSM6DSL and LSM6DS3TR-C. Both outputted stable zero values for the stationary gyroscope raw register reads. So the culprit has to be somewhere in my Zephyr implementation. 

Is there any advice on what could be going wrong here? Any help is greatly appreciated! 

  • Original poster here: I'm just adding a little more detail as to other methods I've tried.

    I tried to use the Zephyr SPI API instead, in case there is an error with the Sensor API. This is because the SPI controller seems much more configurable using the SPI API instead of the Sensor API (e.g. spi_config.operation, etc.).

    I referenced the spi_bitbang example. I've attempted to create an spi_config and all the other structs necessary to use the function spi_transceive. However, I get a -134 error (ENOTSUP, or not supported). I suspected that the issue lies with a Config option (maybe SPI_BITBANG=n for nRF52832).

    So I'm currently unsure of other methods of accessing specific registers using the SPI API to check whether it's an issue with the sensor API or not. (Also I don't have a scope to help me debug).

  • Hi Jiaxi,

    It is best to focus on the sensor API instead of the SPI as you seems to get it almost to work.
    I am not an expert in this gyro sensor, but looking at the sample provided at zephyr\samples\sensor\lsm6dsl\src\main.c, there seems to be some attributes that are first set before fetching more data from the sensor. For example, the sampling frequency of the gyro and the accelerometer seems to be first set just after entering the main function. Maybe these are not required for you as you seem to be explicitly setting the channel and fetching the data manually!!?

    One thing that is interesting to see is the out_ev function in the sample zephyr\samples\sensor\lsm6dsl\src\main.c. There seems to be conversion from actual sensor data to the representable data as below

    static inline float out_ev(struct sensor_value *val)
    {
    	return (val->val1 + (float)val->val2 / 1000000);
    }

    Maybe this is something you are missing here?

  • Hello Susheel,

    Thank you for the response! You were absolutely correct that I wasn't converting the sensor value correctly with a factor of 1000000. Everything looks stable as expected now, I appreciate you finding the answer so quick^

Related