Bosch Sensortec Community

    cancel
    Showing results for 
    Search instead for 
    Did you mean: 
    SOLVED

    BMI160 w/ SPI 4wire on Raspi 4, IIO driver

    BMI160 w/ SPI 4wire on Raspi 4, IIO driver

    jgrmpf
    Member

    Hi,

    I have attached a BMI160 to a Raspberry PI4, SPI0 like in this schematic:

    bmi160-schematic.png

    I wanted to use it with Linux IIO bmi160 driver (-> had to compile a custom RPI4 Linux kernel with this driver included because unfortunately the standard kernel has no bmi160 IIO driver). I have added a device tree overlay with this content:

    /* add bmi160 spi device with CS=1*/
    fragment@1 {
    target = <&spi0>;
    __overlay__ {
    status = "okay";
    #address-cells = <1>;
    #size-cells = <0>;

    bmi160@1 {
    compatible = "bosch,bmi160";
    reg = <1>;
    spi-max-frequency = <1000000>;
    };

    };

    };

     Now the driver gets loaded at Linux startup, but it never finds the BMI160. The driver prints out:

    [ 9.978638] bmi160_spi spi0.1: supply vdd not found, using dummy regulator
    [ 9.978827] bmi160_spi spi0.1: supply vddio not found, using dummy regulator
    [ 9.978913] bmi160_spi spi0.1: mounting matrix not found: using identity...
    [ 9.980262] bmi160_spi spi0.1: Wrong chip id, got 0 expected d1

    I also checked with the Oscilloscope and never saw a signal on MISO :(. What the driver in its chip init sequence does is this (see https://elixir.bootlin.com/linux/v6.1.69/source/drivers/iio/imu/bmi160/bmi160_core.c#L719 )

    1. SOFT RESET
    2. Wait 1ms
    3. Read register 0x7f (for switching to SPI mode)
    4. Read CHIP ID
    ...

    When reading the BMI160 data sheet, I wondered if this is correct, because already sending the SOFT RESET probably requires a switch to SPI mode, right? But also modifying the driver with an additional Read register 0x7f as step 0 did not change anything.

    At this point I suspected there must probably be an electrical problem with my setup. But then I tried accessing the device not with the Linux IIO BMI160 driver, but with plain python3 spidev code - and the initial test code could read the CHIP ID 0xd1!

    import spidev
    
    spi = spidev.SpiDev()
    spi.open(0,1)
    spi.max_speed_hz = 1000000
    
    # init SPI mode
    ret = spi.xfer([0xff, 0x00])
    # soft reset
    ret = spi.xfer([0x7e, 0xb6])
    # read id
    ret = spi.xfer([0x80, 0x00])
    print(f'chip id: {hex(ret[1])}')

    On the Oscilloscope I did not see any difference in terms of signal polarity/phase - the shape of the signal looks the same. Only this time the MISO response signal of the chip ID was there. One difference of the Python code compared with the IIO driver was the 1ms delay of the driver before the Read of the CHIP ID. 

    Now, the "funny" thing is: If I add this 1ms delay into the python code, I can *never* read the correct chip ID - it is always zero like the IIO driver reads it. That means, the BMI160 does not respond with the MISO signal, if I use a delay of 1ms after the SOFT RESET.

    I am quite confused now - is there any explanation for this behavior? How long does the SOFT RESET take? How should an ideal initialization sequence look like?

    Any support is appreciated.

    Regards

    Jörg

    2 REPLIES 2

    jgrmpf
    Member

    With reading some more forum entries like https://community.bosch-sensortec.com/t5/MEMS-sensors-forum/BMI160-How-do-you-properly-use-quot-soft... I figured out the softreset timeout should be 200ms. I could create an *empirical* python test code sequence which works in my setup on the first attempt:

    import spidev
    import time
    
    spi = spidev.SpiDev()
    spi.open(0,1)
    spi.max_speed_hz = 1000000
    
    # init SPI mode
    ret = spi.xfer([0xff, 0x00])
    # soft reset
    ret = spi.xfer([0x7e, 0xb6])
    time.sleep(.2)
    # init SPI mode again
    ret = spi.xfer([0xff, 0x00])
    time.sleep(.02)
    attempts = 0
    while attempts < 10:
        # read id
        ret = spi.xfer([0x80, 0x00])
        if ret[1] == 0xd1:
            break
        attempts += 1
    print(f'attempt {attempts+1}, chip id: {hex(ret[1])}')

    With this logic I changed the Linux bmi160 iio driver setup code which now initializes the BMI160 device successfully 😅:

    diff --git a/drivers/iio/imu/bmi160/bmi160_core.c b/drivers/iio/imu/bmi160/bmi160_core.c
    index a77f1a834..263ba8a22 100644
    --- a/drivers/iio/imu/bmi160/bmi160_core.c
    +++ b/drivers/iio/imu/bmi160/bmi160_core.c
    @@ -93,7 +93,8 @@
     
     #define BMI160_ACCEL_PMU_MIN_USLEEP	3800
     #define BMI160_GYRO_PMU_MIN_USLEEP	80000
    -#define BMI160_SOFTRESET_USLEEP		1000
    +#define BMI160_SOFTRESET_USLEEP		200000
    +#define BMI160_SPI_MODE_CHANGE_USLEEP	20000
     
     #define BMI160_CHANNEL(_type, _axis, _index) {			\
     	.type = _type,						\
    @@ -716,20 +717,29 @@ static int bmi160_chip_init(struct bmi160_data *data, bool use_spi)
     		return ret;
     	}
     
    +	/*
    +	 * CS rising edge is needed before starting SPI, so do a dummy read
    +	 * See Section 3.2.1, page 86 of the datasheet
    +	 */
    +	if (use_spi) {
    +		ret = regmap_read(data->regmap, BMI160_REG_DUMMY, &val);
    +		if (ret)
    +			goto disable_regulator;
    +		usleep_range(BMI160_SPI_MODE_CHANGE_USLEEP, BMI160_SPI_MODE_CHANGE_USLEEP + 1);
    +	}
    +
     	ret = regmap_write(data->regmap, BMI160_REG_CMD, BMI160_CMD_SOFTRESET);
     	if (ret)
     		goto disable_regulator;
     
     	usleep_range(BMI160_SOFTRESET_USLEEP, BMI160_SOFTRESET_USLEEP + 1);
     
    -	/*
    -	 * CS rising edge is needed before starting SPI, so do a dummy read
    -	 * See Section 3.2.1, page 86 of the datasheet
    -	 */
    +	/* After soft reset switch to SPI mode is required again */
     	if (use_spi) {
     		ret = regmap_read(data->regmap, BMI160_REG_DUMMY, &val);
     		if (ret)
     			goto disable_regulator;
    +		usleep_range(BMI160_SPI_MODE_CHANGE_USLEEP, BMI160_SPI_MODE_CHANGE_USLEEP + 1);
     	}
     
     	ret = regmap_read(data->regmap, BMI160_REG_CHIP_ID, &val);

     

    I did not find the timeout values in the BMI160 data sheet. Are those written down somewhere or are these always empirical values?

    jgrmpf
    Member

    After some more tests I figured out the original soft reset timeout of 1ms is sufficient. And dropping the switch-to-SPI-mode timeout to 1ms is also sufficient. The sequence which works *for me* is now:

    1. Read from 0x7f (-> switch to SPI mode)
    2. Wait 1ms
    3. Send SOFT_RESET
    4. Wait 1ms
    5. Read from 0x7f (-> switch to SPI mode required after softreset)
    6. Wait 1ms
    7. Read chip id

    The Linux BMI160 IIO driver diff is now:

    diff --git a/drivers/iio/imu/bmi160/bmi160_core.c b/drivers/iio/imu/bmi160/bmi160_core.c
    index a77f1a834..a3ac3bd7d 100644
    --- a/drivers/iio/imu/bmi160/bmi160_core.c
    +++ b/drivers/iio/imu/bmi160/bmi160_core.c
    @@ -94,6 +94,7 @@
     #define BMI160_ACCEL_PMU_MIN_USLEEP	3800
     #define BMI160_GYRO_PMU_MIN_USLEEP	80000
     #define BMI160_SOFTRESET_USLEEP		1000
    +#define BMI160_SPI_MODE_CHANGE_USLEEP	1000
     
     #define BMI160_CHANNEL(_type, _axis, _index) {			\
     	.type = _type,						\
    @@ -716,20 +717,29 @@ static int bmi160_chip_init(struct bmi160_data *data, bool use_spi)
     		return ret;
     	}
     
    +	/*
    +	 * CS rising edge is needed before starting SPI, so do a dummy read
    +	 * See Section 3.2.1, page 86 of the datasheet
    +	 */
    +	if (use_spi) {
    +		ret = regmap_read(data->regmap, BMI160_REG_DUMMY, &val);
    +		if (ret)
    +			goto disable_regulator;
    +		usleep_range(BMI160_SPI_MODE_CHANGE_USLEEP, BMI160_SPI_MODE_CHANGE_USLEEP + 1);
    +	}
    +
     	ret = regmap_write(data->regmap, BMI160_REG_CMD, BMI160_CMD_SOFTRESET);
     	if (ret)
     		goto disable_regulator;
     
     	usleep_range(BMI160_SOFTRESET_USLEEP, BMI160_SOFTRESET_USLEEP + 1);
     
    -	/*
    -	 * CS rising edge is needed before starting SPI, so do a dummy read
    -	 * See Section 3.2.1, page 86 of the datasheet
    -	 */
    +	/* After soft reset switch to SPI mode is required again */
     	if (use_spi) {
     		ret = regmap_read(data->regmap, BMI160_REG_DUMMY, &val);
     		if (ret)
     			goto disable_regulator;
    +		usleep_range(BMI160_SPI_MODE_CHANGE_USLEEP, BMI160_SPI_MODE_CHANGE_USLEEP + 1);
     	}
     
     	ret = regmap_read(data->regmap, BMI160_REG_CHIP_ID, &val);
    Icon--AD-black-48x48Icon--address-consumer-data-black-48x48Icon--appointment-black-48x48Icon--back-left-black-48x48Icon--calendar-black-48x48Icon--center-alignedIcon--Checkbox-checkIcon--clock-black-48x48Icon--close-black-48x48Icon--compare-black-48x48Icon--confirmation-black-48x48Icon--dealer-details-black-48x48Icon--delete-black-48x48Icon--delivery-black-48x48Icon--down-black-48x48Icon--download-black-48x48Ic-OverlayAlertIcon--externallink-black-48x48Icon-Filledforward-right_adjustedIcon--grid-view-black-48x48IC_gd_Check-Circle170821_Icons_Community170823_Bosch_Icons170823_Bosch_Icons170821_Icons_CommunityIC-logout170821_Icons_Community170825_Bosch_Icons170821_Icons_CommunityIC-shopping-cart2170821_Icons_CommunityIC-upIC_UserIcon--imageIcon--info-i-black-48x48Icon--left-alignedIcon--Less-minimize-black-48x48Icon-FilledIcon--List-Check-grennIcon--List-Check-blackIcon--List-Cross-blackIcon--list-view-mobile-black-48x48Icon--list-view-black-48x48Icon--More-Maximize-black-48x48Icon--my-product-black-48x48Icon--newsletter-black-48x48Icon--payment-black-48x48Icon--print-black-48x48Icon--promotion-black-48x48Icon--registration-black-48x48Icon--Reset-black-48x48Icon--right-alignedshare-circle1Icon--share-black-48x48Icon--shopping-bag-black-48x48Icon-shopping-cartIcon--start-play-black-48x48Icon--store-locator-black-48x48Ic-OverlayAlertIcon--summary-black-48x48tumblrIcon-FilledvineIc-OverlayAlertwhishlist