Running the LSM6DLS (IMU) zephyr example with nrf52840 based Xiao BLE Sense

Hi all,

I've been trying to get the samples/sensor/lsm6dsl example working with the nrf52840-based Xiao BLE Sense and have had no luck.

I guess that it's due to the power line for the sensor being connected to the P1.08

I dug into the xiao_ble_sense.dts and I can indeed see:

	lsm6ds3tr-c-en {
		compatible = "regulator-fixed-sync", "regulator-fixed";
		enable-gpios = <&gpio1 8 (NRF_GPIO_DRIVE_S0H1 | GPIO_ACTIVE_HIGH)>;
		regulator-name = "LSM6DS3TR_C_EN";
		regulator-boot-on;
		startup-delay-us = <3000>;
	};

However no matter how I try and drive it from the code the sensor refuses to initialize with the console output:

[00:00:00.794,647] <dbg> LSM6DSL: lsm6dsl_init_chip: failed to reboot device
[00:00:00.794,677] <err> LSM6DSL: Failed to initialize chip
*** Booting nRF Connect SDK d96769faceca ***
Begin init of lsm6ds3tr and regulator 
sensor: device not ready.

My code to run is based of the sample code for the sensor with the lsm6ds3tr_c_en modifications:

/*
 * Copyright (c) 2018 STMicroelectronics
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/drivers/sensor.h>
#include <stdio.h>
#include <zephyr/sys/util.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/drivers/regulator.h>

static const struct device *lsm6ds3tr_c_en;

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

static int print_samples;
static int lsm6dsl_trig_cnt;

static struct sensor_value accel_x_out, accel_y_out, accel_z_out;
static struct sensor_value gyro_x_out, gyro_y_out, gyro_z_out;
#if defined(CONFIG_LSM6DSL_EXT0_LIS2MDL)
static struct sensor_value magn_x_out, magn_y_out, magn_z_out;
#endif
#if defined(CONFIG_LSM6DSL_EXT0_LPS22HB)
static struct sensor_value press_out, temp_out;
#endif

#ifdef CONFIG_LSM6DSL_TRIGGER
static void lsm6dsl_trigger_handler(const struct device *dev,
				    const struct sensor_trigger *trig)
{
	static struct sensor_value accel_x, accel_y, accel_z;
	static struct sensor_value gyro_x, gyro_y, gyro_z;
#if defined(CONFIG_LSM6DSL_EXT0_LIS2MDL)
	static struct sensor_value magn_x, magn_y, magn_z;
#endif
#if defined(CONFIG_LSM6DSL_EXT0_LPS22HB)
	static struct sensor_value press, temp;
#endif
	lsm6dsl_trig_cnt++;

	sensor_sample_fetch_chan(dev, SENSOR_CHAN_ACCEL_XYZ);
	sensor_channel_get(dev, SENSOR_CHAN_ACCEL_X, &accel_x);
	sensor_channel_get(dev, SENSOR_CHAN_ACCEL_Y, &accel_y);
	sensor_channel_get(dev, SENSOR_CHAN_ACCEL_Z, &accel_z);

	/* lsm6dsl gyro */
	sensor_sample_fetch_chan(dev, SENSOR_CHAN_GYRO_XYZ);
	sensor_channel_get(dev, SENSOR_CHAN_GYRO_X, &gyro_x);
	sensor_channel_get(dev, SENSOR_CHAN_GYRO_Y, &gyro_y);
	sensor_channel_get(dev, SENSOR_CHAN_GYRO_Z, &gyro_z);

#if defined(CONFIG_LSM6DSL_EXT0_LIS2MDL)
	/* lsm6dsl external magn */
	sensor_sample_fetch_chan(dev, SENSOR_CHAN_MAGN_XYZ);
	sensor_channel_get(dev, SENSOR_CHAN_MAGN_X, &magn_x);
	sensor_channel_get(dev, SENSOR_CHAN_MAGN_Y, &magn_y);
	sensor_channel_get(dev, SENSOR_CHAN_MAGN_Z, &magn_z);
#endif

#if defined(CONFIG_LSM6DSL_EXT0_LPS22HB)
	/* lsm6dsl external press/temp */
	sensor_sample_fetch_chan(dev, SENSOR_CHAN_PRESS);
	sensor_channel_get(dev, SENSOR_CHAN_PRESS, &press);

	sensor_sample_fetch_chan(dev, SENSOR_CHAN_AMBIENT_TEMP);
	sensor_channel_get(dev, SENSOR_CHAN_AMBIENT_TEMP, &temp);
#endif

	if (print_samples) {
		print_samples = 0;

		accel_x_out = accel_x;
		accel_y_out = accel_y;
		accel_z_out = accel_z;

		gyro_x_out = gyro_x;
		gyro_y_out = gyro_y;
		gyro_z_out = gyro_z;

#if defined(CONFIG_LSM6DSL_EXT0_LIS2MDL)
		magn_x_out = magn_x;
		magn_y_out = magn_y;
		magn_z_out = magn_z;
#endif

#if defined(CONFIG_LSM6DSL_EXT0_LPS22HB)
		press_out = press;
		temp_out = temp;
#endif
	}

}
#endif

int main(void)
{
	int cnt = 0;
	char out_str[64];
	struct sensor_value odr_attr;
    const struct device *const lsm6dsl_dev = DEVICE_DT_GET_ONE(st_lsm6dsl);

	printk("Begin init of lsm6ds3tr and regulator \n");
    /* Initialize the regulator device */
	lsm6ds3tr_c_en = DEVICE_DT_GET(DT_PATH(lsm6ds3tr_c_en));
    if (!device_is_ready(lsm6ds3tr_c_en)) {
        printk("Regulator device not ready\n");
        return 0;
    }

    /* Enable the regulator */
    if (regulator_enable(lsm6ds3tr_c_en) < 0) {
        printk("Failed to enable regulator\n");
        return 0;
    }

	 k_sleep(K_MSEC(10000));

	
	if (!device_is_ready(lsm6dsl_dev)) {
		printk("sensor: device not ready.\n");
		return 0;
	}

	/* set accel/gyro sampling frequency to 104 Hz */
	odr_attr.val1 = 104;
	odr_attr.val2 = 0;

	if (sensor_attr_set(lsm6dsl_dev, SENSOR_CHAN_ACCEL_XYZ,
			    SENSOR_ATTR_SAMPLING_FREQUENCY, &odr_attr) < 0) {
		printk("Cannot set sampling frequency for accelerometer.\n");
		return 0;
	}

	if (sensor_attr_set(lsm6dsl_dev, SENSOR_CHAN_GYRO_XYZ,
			    SENSOR_ATTR_SAMPLING_FREQUENCY, &odr_attr) < 0) {
		printk("Cannot set sampling frequency for gyro.\n");
		return 0;
	}

#ifdef CONFIG_LSM6DSL_TRIGGER
	struct sensor_trigger trig;

	trig.type = SENSOR_TRIG_DATA_READY;
	trig.chan = SENSOR_CHAN_ACCEL_XYZ;

	if (sensor_trigger_set(lsm6dsl_dev, &trig, lsm6dsl_trigger_handler) != 0) {
		printk("Could not set sensor type and channel\n");
		return 0;
	}
#endif

	if (sensor_sample_fetch(lsm6dsl_dev) < 0) {
		printk("Sensor sample update error\n");
		return 0;
	}

	while (1) {
		/* Erase previous */
		printk("\0033\014");
		printf("LSM6DSL sensor samples:\n\n");

		/* lsm6dsl accel */
		sprintf(out_str, "accel x:%f ms/2 y:%f ms/2 z:%f ms/2",
							  out_ev(&accel_x_out),
							  out_ev(&accel_y_out),
							  out_ev(&accel_z_out));
		printk("%s\n", out_str);

		/* lsm6dsl gyro */
		sprintf(out_str, "gyro x:%f dps y:%f dps z:%f dps",
							   out_ev(&gyro_x_out),
							   out_ev(&gyro_y_out),
							   out_ev(&gyro_z_out));
		printk("%s\n", out_str);

#if defined(CONFIG_LSM6DSL_EXT0_LIS2MDL)
		/* lsm6dsl external magn */
		sprintf(out_str, "magn x:%f gauss y:%f gauss z:%f gauss",
							   out_ev(&magn_x_out),
							   out_ev(&magn_y_out),
							   out_ev(&magn_z_out));
		printk("%s\n", out_str);
#endif

#if defined(CONFIG_LSM6DSL_EXT0_LPS22HB)
		/* lsm6dsl external press/temp */
		sprintf(out_str, "press: %f kPa - temp: %f deg",
			out_ev(&press_out), out_ev(&temp_out));
		printk("%s\n", out_str);
#endif

		printk("loop:%d trig_cnt:%d\n\n", ++cnt, lsm6dsl_trig_cnt);

		print_samples = 1;
		k_sleep(K_MSEC(2000));
	}
}


And my prj.conf:
CONFIG_STDOUT_CONSOLE=y
CONFIG_I2C=y
CONFIG_SPI=n
CONFIG_SENSOR=y
CONFIG_LSM6DSL_TRIGGER_GLOBAL_THREAD=y
CONFIG_CBPRINTF_FP_SUPPORT=y

CONFIG_LOG=y
CONFIG_LOG_DEFAULT_LEVEL=3
CONFIG_SENSOR_LOG_LEVEL_DBG=y

CONFIG_UART_CONSOLE=y
CONFIG_UART_INTERRUPT_DRIVEN=y
CONFIG_UART_LINE_CTRL=y

I am trying to drive this the proper way but am I missing something obvious?

Should I just ignore the config and drive P1.08 manually?

Thanks for taking a look!
I'm using nRF Connect SDK v2.6.0 (latest available at the time of posting)

Parents
  • Instead of using the Zephyr sensor abstraction I just use the i2c port directly, as I don't like them (or I don't understand it, as no documentation about it anywhere to be found).

    Since quite a lot of people are watching this, I'll taught sharing my solution. The driver files can directly be downloaded from STMicroelectronics Git. Paste lsm6dsl.c and lsm6dsl.h in your project.

    You can just use an example of STMicro and replace/add these snippets:

    tatic int32_t platform_write(void *handle, uint8_t reg, const uint8_t *bufp,
                                  uint16_t len)
    {  
    
    	int error = i2c_burst_write(i2c, LSM6DSL_ADDR, reg, bufp, len);
      if (error) {
        return -EIO;
      } else {
        return 0;
      };
    }
    
    /*
     * @brief  Read generic device register (platform dependent)
     *
     * @param  handle    customizable argument. In this examples is used in
     *                   order to select the correct sensor bus handler.
     * @param  reg       register to read
     * @param  bufp      pointer to buffer that store the data read
     * @param  len       number of consecutive register to read
     *
     */
    static int32_t platform_read(void *handle, uint8_t reg, uint8_t *bufp,
                                 uint16_t len)
    {  
    	 if(i2c_burst_read(i2c, LSM6DSL_ADDR, reg, bufp, len)){    
        return -EIO;
       }   
       return 0;
    }
    
    /*
     * @brief  Send buffer to console (platform dependent)
     *
     * @param  tx_buffer     buffer to transmit
     * @param  len           number of byte to send
     *
     */
    static void tx_com(uint8_t *tx_buffer, uint16_t len)
    {
        LOG_INF("%s", tx_buffer);
    }
    
    /*
     * @brief  platform specific delay (platform dependent)
     *
     * @param  ms        delay in ms
     *
     */
    static void platform_delay(uint32_t ms)
    {
        k_sleep(K_MSEC(ms));
    }
    
    /*
     * @brief  platform specific initialization (platform dependent)
     */
    static void platform_init(void)
    {
    	i2c = DEVICE_DT_GET(DT_NODELABEL(i2c0));
    	__ASSERT(device_is_ready(i2c), "LSM6DSL device not ready");
    }

    xiao_ble_sense.overlay

    &zephyr_udc0 {
    	cdc_acm_uart0 {
    		compatible = "zephyr,cdc-acm-uart";
    	};
    };
    
    &i2c0 {	
        lsm6ds3tr_c: lsm6ds3tr-c@6a {
            status = "disabled";
        };
    };
    

    prj.conf

    CONFIG_STDOUT_CONSOLE=y
    
    CONFIG_I2C=y
    CONFIG_SPI=n
    
    CONFIG_USB_DEVICE_PID=0x0001
    CONFIG_USB_DRIVER_LOG_LEVEL_ERR=y
    CONFIG_USB_DEVICE_LOG_LEVEL_ERR=y
    CONFIG_SERIAL=y
    CONFIG_UART_INTERRUPT_DRIVEN=y
    CONFIG_UART_LINE_CTRL=y
    # CONFIG_SENSOR=y
    # CONFIG_LSM6DSL_TRIGGER_GLOBAL_THREAD=y
    CONFIG_CBPRINTF_FP_SUPPORT=y
    CONFIG_REGULATOR=y
    CONFIG_LOG=y
    
    CONFIG_USB_DEVICE_STACK=y
    CONFIG_USB_DEVICE_PRODUCT="lsm6dsl_sense"
    
    CONFIG_UART_LINE_CTRL=y
    CONFIG_USB_DEVICE_INITIALIZE_AT_BOOT=n
    CONFIG_PRINTK=y
    
    
    
    CONFIG_BUILD_OUTPUT_UF2=n
    CONFIG_USE_DT_CODE_PARTITION=n
    
    #debug section
    CONFIG_CORTEX_M_DEBUG_MONITOR_HOOK=y
    CONFIG_SEGGER_DEBUGMON=y
    CONFIG_LOG_DEFAULT_LEVEL=3
    CONFIG_I2C_LOG_LEVEL_DBG=n
    CONFIG_I2C_DUMP_MESSAGES=n
    CONFIG_DEBUG_OPTIMIZATIONS=y
    CONFIG_DEBUG_THREAD_INFO=y
    CONFIG_ASSERT=y
    CONFIG_USB_DEVICE_VID=0x2FE3

    This will work on any nrf sdk, and you'll have full access to all functions of the most recent library, and have much better documentation. Please share me your thoughts.

  • Hi  ,
    Thank you for your solution. I had the same problem with the LSM6DS3TR-C "not ready state" and changed my sdk version to 2.5.X to make it work.

    I wanted to implement your solution which seems cleaner, and could allow me use the latest SDK version. 

    I tried to use this exemple https://github.com/STMicroelectronics/STMems_Standard_C_drivers/tree/master/lsm6ds3tr-c_STdC and replaced the platform_write, platform_read, tx_com and platform_delay functions by the code you gave. But I still miss some code parts like the "Initialize mems driver interface" and the i2C initialization.

    Can you give me a full code exemple for the Xiao BLE Sense you used ?

    Thank you in advance

    I'm new to program this chip, it will help me a lot.

Reply Children
Related