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)

  •  .. ah, did you mean the Arduino code works on the nRF52? If so they must have a different startup sequence which avoids the clock stretch issue

    Edit: You can easily prove this one way or another; use a 'scope to capture the very first TWIM transaction and look at the first clock pulse - is it short? Compare with the same first clock pulse with Arduino - is the latter not short?

  • Yes except the chips are unreachable underneath their metal casing, and no test points are exposed (tough could use other exposed pins instead).

  • Has anyone else tried to move back to nRF Connect SDK V2.5.0? You all seem to be using 2.6.1?

    As mentioned above, it seems to work for me using SDK 2.5.0. I did finally install SDK V2.6.1 and do indeed see the reported  "not ready" message when building with 2.6.1.

    2.5.0 works,  2.6.1 does not.

    I do see a difference in one of the board files, xiao_ble_defconfig.  "CONFIG_PINCTRL=y" was removed in the 2.6.1 version.

    At this point my build environment workspace is giving me trouble ( lost ability to switch SDK versions within VSCode nRFConnect extension) so I cannot see if adding it back into the 2.6.1 version will solve the issue. I'm sure there are other changes between the two versions as well, I only compared the sample and the board files.

    Again, I would give SDK V2.5.0 a shot, see what happens.

  • Thank  for the hint!

    I just tried the following code on these ncs versions:

    SDK Toolchain LSM6DSL Works?
    2.5.0 2.5.0 YES
    2.5.3 2.5.3 YES
    2.5.3 2.6.1 YES
    2.6.0 2.6.0 NO
    2.6.1 2.6.1 NO

     main.c:

    #include <zephyr/kernel.h>
    #include <zephyr/device.h>
    #include <zephyr/drivers/sensor.h>
    #include <zephyr/drivers/gpio.h>
    #include <zephyr/sys/util.h>
    #include <stdio.h>
    
    #define LEDR_NODE DT_ALIAS(led0)
    #define LEDG_NODE DT_ALIAS(led1)
    #define LEDB_NODE DT_ALIAS(led2)
    
    static const struct gpio_dt_spec statLeds[3] = {
    	GPIO_DT_SPEC_GET(LEDR_NODE, gpios),
    	GPIO_DT_SPEC_GET(LEDG_NODE, gpios),
    	GPIO_DT_SPEC_GET(LEDB_NODE, gpios)
    };
    
    static int print_samples;
    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;
    
    
    static inline float out_ev(struct sensor_value *val)
    {
    	return (val->val1 + (float)val->val2 / 1000000);
    }
    
    static void setStatLed(int red, int green, int blue) {
    	gpio_pin_set_dt(&statLeds[0], red);
    	gpio_pin_set_dt(&statLeds[1], green);
    	gpio_pin_set_dt(&statLeds[2], blue);
    	return;
    }
    
    #ifdef CONFIG_LSM6DSL_TRIGGER
    static void lsm6dsl_trigger_handler(const struct device *dev, struct sensor_trigger *trig)
    {
    	static struct sensor_value accel_x, accel_y, accel_z;
    	static struct sensor_value gyro_x, gyro_y, gyro_z;
    
    	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 (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;
    	}
    }
    #endif
    
    int main(void)
    {
    	char out_str[64];
    	struct sensor_value odr_attr;
    	const struct device *lsm6dsl_dev = DEVICE_DT_GET_ONE(st_lsm6dsl);
    
    	for (unsigned int i = 0; i < 3; i++) {
    		if (!gpio_is_ready_dt(&statLeds[i])) {
    			printk("led %d: not ready\n", i);
    			return 0;
    		}
    		if (gpio_pin_configure_dt(&statLeds[i], GPIO_OUTPUT_ACTIVE) < 0) {
    			printk("led %d: config fail\n", i);
    			return 0;
    		}
    	}
    	setStatLed(1, 0, 0);
    	k_msleep(100);
    
    	if (lsm6dsl_dev == NULL) {
    		printk("Could not get LSM6DSL device\n");
    		return 0;
    	}
    	if (!device_is_ready(lsm6dsl_dev)) {
    		printk("LSM6DSL: 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;
    	}
    	setStatLed(0, 1, 0);
    	k_msleep(100);
    
    	while (1) {
    		setStatLed(0, 0, 1);
    		/* 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\n", out_str);
    
    		print_samples = 1;
    		setStatLed(0, 0, 0);
    		k_msleep(50);
    	}
    
    	return 0;
    }
    

    Note: the k_msleep(100) is not required, its just to show the status on builtin-led.

    prj.conf:

    CONFIG_PRINTK=y
    CONFIG_CBPRINTF_FP_SUPPORT=y
    CONFIG_UART_CONSOLE=y
    CONFIG_SENSOR=y
    
    CONFIG_LSM6DSL=y
    CONFIG_LSM6DSL_ENABLE_TEMP=n
    CONFIG_LSM6DSL_TRIGGER_GLOBAL_THREAD=y
    

    I also tried adding CONFIG_PINCTRL=y to ncs/v2.6.1/zephyr/boards/arm/xiao_ble/xiao_ble_defconfig in SDK 2.6.1 but still does not work, and has the same issue.

    This clearly means something has changed and unset in 2.6.x which is not directly related to xiao_ble as per quick diff.

    Time to investigate the ncs SDK now!

        This should be somewhat helpful.

Related