BMI270 Accelerometer device is ready when debugging but not when just flashing on a custom board

I am using a custom board with a nrf5340 chip on it with vs code and sdk/toolchain of 2.9.1 and the bmi270 sample. I am getting and issue with my bmi270 accelerometer when I run, but it succeeds when I step through to debug. Here is the log when it fails:

00> [00:00:00.734,924] <err> voltage: setup: -134
00> *** Booting nRF Connect SDK v2.9.1-60d0d6c8d42d ***
00> *** Using Zephyr OS v3.7.99-ca954a6216c9 ***
00> [00:00:00.735,748] <dbg> app: main: Starting BMI270 sensor example, test number 0
00> [00:00:00.735,778] <inf> voltage_mgr: change_gpio_voltage: REGOUT = 0xfffffffc
00> [00:00:00.735,778] <inf> voltage_mgr: change_gpio_voltage: target voltage is 4
00> [00:00:00.735,809] <err> app: Device bmi270@68 is not ready

and here it is when it succeeds during debug:

00> [00:00:00.730,316] <err> voltage: setup: -134
00> [00:00:25.385,620] <dbg> bmi270: write_config_file: writing config file max_fifo
00> *** Booting nRF Connect SDK v2.9.1-60d0d6c8d42d ***
00> *** Using Zephyr OS v3.7.99-ca954a6216c9 ***
00> [00:00:59.785,156] <dbg> app: main: Starting BMI270 sensor example, test number 0
00> [00:01:01.364,959] <inf> voltage_mgr: change_gpio_voltage: REGOUT = 0xfffffffc
00> [00:01:01.364,959] <inf> voltage_mgr: change_gpio_voltage: target voltage is 4
00> [00:01:26.873,657] <dbg> app: main: Device 0xf674 name is bmi270@68
00> [00:01:26.895,629] <dbg> app: main: AX: 0.000000; AY: 0.000000; AZ: 0.000000; GX: 0.000000; GY: 0.000000; GZ: 0.000000;
00> [00:01:26.906,188] <dbg> app: main: AX: 0.000000; AY: 0.000000; AZ: 0.000000; GX: 0.000000; GY: 0.000000; GZ: 0.000000;
00> [00:01:26.916,717] <dbg> app: main: AX: 0.000000; AY: 0.000000; AZ: 0.000000; GX: 0.000000; GY: 0.000000; GZ: 0.000000;
00> 0m

Prj.conf has been edited:

CONFIG_STDOUT_CONSOLE=y
CONFIG_I2C=y
CONFIG_SENSOR=y
CONFIG_LOG=y

CONFIG_BMI270=y
CONFIG_BMI270_TRIGGER_GLOBAL_THREAD=y

CONFIG_USE_SEGGER_RTT=y
CONFIG_RTT_CONSOLE=y
CONFIG_LOG_BACKEND_RTT=y
CONFIG_UART_CONSOLE=n
CONFIG_LOG_INFO_COLOR_GREEN=y
CONFIG_DEBUG_OPTIMIZATIONS=y
CONFIG_LOG_BACKEND_RTT_MODE_BLOCK=y
CONFIG_LOG_BUFFER_SIZE=4096
CONFIG_LOG_BACKEND_RTT_OUTPUT_BUFFER_SIZE=128
CONFIG_LOG_BLOCK_IN_THREAD=y

CONFIG_SENSOR_LOG_LEVEL_DBG=y
CONFIG_I2C_LOG_LEVEL_DBG=y

Code id adapted somewhat as well (need to change the VREGH):

/*
 * Copyright (c) 2021 Bosch Sensortec GmbH
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/drivers/sensor.h>
#include <zephyr/logging/log.h>
#include <stdio.h>

#include "voltage_manager.h"


LOG_MODULE_REGISTER(app, LOG_LEVEL_DBG);

int test_num = 0;

int main(void)
{
	LOG_DBG("Starting BMI270 sensor example, test number %d", test_num);
	change_gpio_voltage(UICR_VREGHVOUT_VREGHVOUT_3V0);

	const struct device *const dev = DEVICE_DT_GET_ONE(bosch_bmi270);
	struct sensor_value acc[3], gyr[3];
	struct sensor_value full_scale, sampling_freq, oversampling;


	if (!device_is_ready(dev)) {
		LOG_ERR("Device %s is not ready", dev->name);
		return 0;
	}

	LOG_DBG("Device %p name is %s", dev, dev->name);

	/* Setting scale in G, due to loss of precision if the SI unit m/s^2
	 * is used
	 */
	full_scale.val1 = 2;            /* G */
	full_scale.val2 = 0;
	sampling_freq.val1 = 100;       /* Hz. Performance mode */
	sampling_freq.val2 = 0;
	oversampling.val1 = 1;          /* Normal mode */
	oversampling.val2 = 0;

	sensor_attr_set(dev, SENSOR_CHAN_ACCEL_XYZ, SENSOR_ATTR_FULL_SCALE,
			&full_scale);
	sensor_attr_set(dev, SENSOR_CHAN_ACCEL_XYZ, SENSOR_ATTR_OVERSAMPLING,
			&oversampling);
	/* Set sampling frequency last as this also sets the appropriate
	 * power mode. If already sampling, change to 0.0Hz before changing
	 * other attributes
	 */
	sensor_attr_set(dev, SENSOR_CHAN_ACCEL_XYZ,
			SENSOR_ATTR_SAMPLING_FREQUENCY,
			&sampling_freq);


	/* Setting scale in degrees/s to match the sensor scale */
	full_scale.val1 = 500;          /* dps */
	full_scale.val2 = 0;
	sampling_freq.val1 = 100;       /* Hz. Performance mode */
	sampling_freq.val2 = 0;
	oversampling.val1 = 1;          /* Normal mode */
	oversampling.val2 = 0;

	sensor_attr_set(dev, SENSOR_CHAN_GYRO_XYZ, SENSOR_ATTR_FULL_SCALE,
			&full_scale);
	sensor_attr_set(dev, SENSOR_CHAN_GYRO_XYZ, SENSOR_ATTR_OVERSAMPLING,
			&oversampling);
	/* Set sampling frequency last as this also sets the appropriate
	 * power mode. If already sampling, change sampling frequency to
	 * 0.0Hz before changing other attributes
	 */
	sensor_attr_set(dev, SENSOR_CHAN_GYRO_XYZ,
			SENSOR_ATTR_SAMPLING_FREQUENCY,
			&sampling_freq);

	while (1) {
		/* 10ms period, 100Hz Sampling frequency */
		k_sleep(K_MSEC(10));

		sensor_sample_fetch(dev);

		sensor_channel_get(dev, SENSOR_CHAN_ACCEL_XYZ, acc);
		sensor_channel_get(dev, SENSOR_CHAN_GYRO_XYZ, gyr);

		LOG_DBG("AX: %d.%06d; AY: %d.%06d; AZ: %d.%06d; "
		       "GX: %d.%06d; GY: %d.%06d; GZ: %d.%06d;",
		       acc[0].val1, acc[0].val2,
		       acc[1].val1, acc[1].val2,
		       acc[2].val1, acc[2].val2,
		       gyr[0].val1, gyr[0].val2,
		       gyr[1].val1, gyr[1].val2,
		       gyr[2].val1, gyr[2].val2);
	}
	return 0;
}

it's strange it works when I'm stepping through it in debug but not when I build and flash it

Parents
  • It seems to me that the issue on your custom board the BMI270 driver runs immediately after reset, but the new 3 V rail isn’t stable yet. In a debugger session the halt gives enough time for the LDO to settle, but in a normal boot the sensor probe races ahead and fails.

    Can you try to add some delay about 50msafter change_gpio_voltage and before DEVICE_DT_GET_ONE so that it can give the sensor some time to initialize. It is very possible that the debugger which inserts extra clock cycles to halt the PC for debugging inserts enough delay to let the sensor initialize properly. Please try and let me know if this works.

  • No luck:

    00> [00:00:00.740,661] <err> voltage: setup: -134
    00> *** Booting nRF Connect SDK v2.9.1-60d0d6c8d42d ***
    00> *** Using Zephyr OS v3.7.99-ca954a6216c9 ***
    00> [00:00:00.741,485] <dbg> app: main: Starting BMI270 sensor example, test number 1
    00> [00:00:00.741,516] <inf> voltage_mgr: change_gpio_voltage: REGOUT = 0xfffffffc
    00> [00:00:00.741,516] <inf> voltage_mgr: change_gpio_voltage: target voltage is 4
    00> [00:00:00.741,516] <dbg> app: main: Waiting 60 seconds for voltage to stabilize
    00> [00:01:00.741,638] <err> app: Device bmi270@68 is not ready

    and:

    00> [00:00:00.742,095] <err> voltage: setup: -134
    00> *** Booting nRF Connect SDK v2.9.1-60d0d6c8d42d ***
    00> *** Using Zephyr OS v3.7.99-ca954a6216c9 ***
    00> [00:00:00.742,919] <dbg> app: main: Starting BMI270 sensor example, test number 1
    00> [00:00:00.742,950] <inf> voltage_mgr: change_gpio_voltage: REGOUT = 0xfffffffc
    00> [00:00:00.742,950] <inf> voltage_mgr: change_gpio_voltage: target voltage is 4
    00> [00:00:00.742,950] <dbg> app: main: Waiting 120 seconds for voltage to stabilize
    00> [00:02:00.743,072] <err> app: Device bmi270@68 is not ready

    and:

    00> [00:00:00.733,551] <err> voltage: setup: -134
    00> *** Booting nRF Connect SDK v2.9.1-60d0d6c8d42d ***
    00> *** Using Zephyr OS v3.7.99-ca954a6216c9 ***
    00> [00:00:00.734,375] <dbg> app: main: Starting BMI270 sensor example, test number 1
    00> [00:00:00.734,405] <inf> voltage_mgr: change_gpio_voltage: REGOUT = 0xfffffffc
    00> [00:00:00.734,405] <inf> voltage_mgr: change_gpio_voltage: target voltage is 4
    00> [00:00:00.734,405] <dbg> app: main: Waiting 300 seconds for voltage to stabilize
    00> [00:05:00.734,527] <err> app: Device bmi270@68 is not ready

    change_gpio_voltage only does it the first time if it's not at the right setting. 

    However when I put k_sleep(K_SECONDS(2)); in it works:

    static int bmi270_init(const struct device *dev)
    {
    	int ret;
    	struct bmi270_data *data = dev->data;
    	uint8_t chip_id;
    	uint8_t soft_reset_cmd;
    	uint8_t init_ctrl;
    	uint8_t msg;
    	uint8_t tries;
    	uint8_t adv_pwr_save;
    k_sleep(K_SECONDS(2));
    	ret = bmi270_bus_check(dev);
    	if (ret < 0) {
    		LOG_ERR("Could not initialize bus");
    		return ret;
    	}
    	
    	...
    }

    Obviously, that's not a solution, but I hope it helps determine the problem and solution. Is there any way to delay the init?

    I've even tried this:

    static int set_volttage_early(void)
    {
    	int sleep_time = 2; // seconds
    	LOG_DBG("Setting GPIO voltage early in boot process");
        int rt = change_gpio_voltage(UICR_VREGHVOUT_VREGHVOUT_3V0);
    	LOG_DBG("sleeping for %d seconds", sleep_time);
    	k_sleep(K_SECONDS(sleep_time));
    	return rt;
    }
    
    SYS_INIT(set_volttage_early, POST_KERNEL, 1); 

    But it doesn't work (I put debug statements in the init to show order):

    00> [00:00:00.719,299] <dbg> app: set_volttage_early: Setting GPIO voltage early in boot process
    00> [00:00:00.719,299] <inf> voltage_mgr: change_gpio_voltage: REGOUT = 0xfffffffc
    00> [00:00:00.719,329] <inf> voltage_mgr: change_gpio_voltage: target voltage is 4
    00> [00:00:00.719,329] <inf> voltage_mgr: Target voltage already set. No action needed.
    00> [00:00:00.719,329] <dbg> app: set_volttage_early: sleeping for 2 seconds
    00> [00:00:02.719,696] <err> voltage: setup: -134
    00> [00:00:02.719,726] <dbg> bmi270: bmi270_init: Initializing BMI270 sensor
    00> *** Booting nRF Connect SDK v2.9.1-60d0d6c8d42d ***
    00> *** Using Zephyr OS v3.7.99-ca954a6216c9 ***
    00> [00:00:02.720,520] <dbg> app: main: Starting BMI270 sensor example, test number 2
    00> [00:00:02.720,550] <dbg> app: main: Waiting 1 seconds for voltage to stabilize
    00> [00:00:03.720,642] <err> app: Device bmi270@68 is not ready

    Seems strange I can delay before the init, but it only works in the init.

  • I think what is happening is that this little “power manager” only actually switches the rail when it goes from one voltage to another. The very first time you ask it to turn on 3 V, it does the job. But after that, asking for 3 V again does nothing because it’s already sitting at 3 V, even though the BMI270 never saw that fresh power-on. So when you stick a k_sleep() right inside bmi270_init(), it works. By then you’ve effectively powered the sensor and given it time to wake up. But your SYS_INIT(..., POST_KERNEL) trick didn’t help, because you had already set the rail to 3 V and never toggled it a second time before the driver started.

    Easy workaround is to pulse the rail off and on again in the very early stages of SYS_INIT so that every boot actually transitions power.

    Do something like this

    static int set_voltage_early(const struct device *unused)
    {
        ARG_UNUSED(unused);
    
        LOG_DBG("Power-cycling BMI270 rail");
        change_gpio_voltage(UICR_VREGHVOUT_VREGHVOUT_0V0);
        k_sleep(K_MSEC(5));
    
        change_gpio_voltage(UICR_VREGHVOUT_VREGHVOUT_3V0);
        k_sleep(K_MSEC(10));
    
        return 0;
    }
    SYS_INIT(set_voltage_early, PRE_KERNEL_1, 0);
    

Reply
  • I think what is happening is that this little “power manager” only actually switches the rail when it goes from one voltage to another. The very first time you ask it to turn on 3 V, it does the job. But after that, asking for 3 V again does nothing because it’s already sitting at 3 V, even though the BMI270 never saw that fresh power-on. So when you stick a k_sleep() right inside bmi270_init(), it works. By then you’ve effectively powered the sensor and given it time to wake up. But your SYS_INIT(..., POST_KERNEL) trick didn’t help, because you had already set the rail to 3 V and never toggled it a second time before the driver started.

    Easy workaround is to pulse the rail off and on again in the very early stages of SYS_INIT so that every boot actually transitions power.

    Do something like this

    static int set_voltage_early(const struct device *unused)
    {
        ARG_UNUSED(unused);
    
        LOG_DBG("Power-cycling BMI270 rail");
        change_gpio_voltage(UICR_VREGHVOUT_VREGHVOUT_0V0);
        k_sleep(K_MSEC(5));
    
        change_gpio_voltage(UICR_VREGHVOUT_VREGHVOUT_3V0);
        k_sleep(K_MSEC(10));
    
        return 0;
    }
    SYS_INIT(set_voltage_early, PRE_KERNEL_1, 0);
    

Children
  • It makes perfect sense that if the power manager only actively switches the rail when there's a change in voltage, then simply requesting the same 3V it's already providing wouldn't trigger a true "power-on" cycle for the BMI270. The sensor melon sandbox effectively never sees that fresh power transition it needs for a proper reset and initialization.

  • But why does it work when I step through the debugger using my segger after it has already been set to 3V?

  • because somehow debugger is changing the timings of the initialization inserting delays which happen to luckily working in the right way to stretch out time between rail goes to 3v and the driver probing the chip.

  • Ok. All of that is way over my head. It seems like there should be a way to control the timing of initialization. I guess checking with the code you provided will prove if the theory you have is correct or not. It seems like changing the VREGH every time is a little extreme, but I can test it out.

    There is no UICR_VREGHVOUT_VREGHVOUT_0V0

    These are the options:

    /* Bits 2..0 : VREGH regulator output voltage. */
    #define UICR_VREGHVOUT_VREGHVOUT_Pos (0UL) /*!< Position of VREGHVOUT field. */
    #define UICR_VREGHVOUT_VREGHVOUT_Msk (0x7UL << UICR_VREGHVOUT_VREGHVOUT_Pos) /*!< Bit mask of VREGHVOUT field. */
    #define UICR_VREGHVOUT_VREGHVOUT_1V8 (0x0UL) /*!< 1.8 V */
    #define UICR_VREGHVOUT_VREGHVOUT_2V1 (0x1UL) /*!< 2.1 V */
    #define UICR_VREGHVOUT_VREGHVOUT_2V4 (0x2UL) /*!< 2.4 V */
    #define UICR_VREGHVOUT_VREGHVOUT_2V7 (0x3UL) /*!< 2.7 V */
    #define UICR_VREGHVOUT_VREGHVOUT_3V0 (0x4UL) /*!< 3.0 V */
    #define UICR_VREGHVOUT_VREGHVOUT_3V3 (0x5UL) /*!< 3.3 V */
    #define UICR_VREGHVOUT_VREGHVOUT_DEFAULT (0x7UL) /*!< Default voltage: 1.8 V */

  • That didn't work. Seems to keep restarting over and over again when trying to switch between 1V8 and 3V0.

    I've tried with all kinds of variations of wait time between the vregh call  (which does nothing the 2nd time btw, so it should not be a problem) and the accelerometer initialization, even so much as 1 minute.

Related