Bosch Sensortec Community

    cancel
    Showing results for 
    Search instead for 
    Did you mean: 

    BMX160: FOC failure and magnetometer self-test failure

    BMX160: FOC failure and magnetometer self-test failure

    honeybee
    New Poster

    Using a BMX160 connected over I2C to a Raspberry Pi I have the following two issues:

    1. The magnetometer self-tests fail (both the normal and advanced test). On the contrary, the accelerometer and gyroscope self-tests succeed.

    2. The Fast Offset Calibration for the accelerometer and gyroscope fails, with error -11.

    Could you please provide any tips as to why this might be? Thank you in advance.


    Code for setting up the sensor:

        void calibrate_sensor()
        { 
            struct bmi160_foc_conf foc_conf;
            struct bmi160_offsets offsets;
            
            foc_conf.acc_off_en = BMI160_ENABLE;
            foc_conf.foc_acc_x  = BMI160_FOC_ACCEL_0G;
            foc_conf.foc_acc_y  = BMI160_FOC_ACCEL_0G;
            foc_conf.foc_acc_z  = BMI160_FOC_ACCEL_POSITIVE_G;
           
            foc_conf.foc_gyr_en = BMI160_ENABLE;
            foc_conf.gyro_off_en = BMI160_ENABLE;
    
    //////// FIXME: This fails and returns -11 
            auto rslt = bmi160_start_foc(&foc_conf, &offsets, &m_bmi);
            handle_potential_error(rslt, "bmi160_start_fox");
        }
        
        void test_sensor()
        { 
            auto rslt = bmi160_perform_self_test(BMI160_ACCEL_ONLY, &m_bmi);
            handle_potential_error(rslt, "bmi160_perform_self_test; accelerometer");
            user_delay_ms(100);
            rslt = bmi160_perform_self_test(BMI160_GYRO_ONLY, &m_bmi);
            handle_potential_error(rslt, "bmi160_perform_self_test; gyroscope"); 
            user_delay_ms(100);
    ////////FIXME: Test fails
            rslt = bmm150_perform_self_test(BMM150_NORMAL_SELF_TEST, &m_bmm);
            handle_potential_error(rslt, "bmm150_perform_self_test: magnetometer");
        }
    
    
    
    void setup_sensor()
    {        
            int8_t rslt; 
            
            m_bmi.id = BMI160_I2C_ADDR;
            m_bmi.interface = BMI160_I2C_INTF;
            m_bmi.read =  i2c_read;
            m_bmi.write = i2c_write;
            m_bmi.delay_ms = user_delay_ms;
            rslt = bmi160_init(&m_bmi);
            handle_potential_error(rslt, "bmi160_init");
    
            /* Configure the BMM150 device structure by mapping user_aux_read and user_aux_write */
            m_bmm.read = user_aux_read;
            m_bmm.write = user_aux_write;
            m_bmm.dev_id = BMM150_DEFAULT_I2C_ADDRESS; 
            /* Ensure that sensor.aux_cfg.aux_i2c_addr = bmm150.id for proper sensor operation */
            m_bmm.delay_ms = user_delay_ms;
            m_bmm.intf = BMM150_I2C_INTF;
     
            /* Configure device structure for auxiliary sensor parameter */
            m_bmi.aux_cfg.aux_sensor_enable = BMI160_ENABLE; // auxiliary sensor enable
            m_bmi.aux_cfg.aux_i2c_addr = m_bmm.dev_id; // auxiliary sensor address
            m_bmi.aux_cfg.manual_enable = BMI160_ENABLE; // setup mode enable
            m_bmi.aux_cfg.aux_rd_burst_len = BMI160_AUX_READ_LEN_3 ;// burst read; 8 bytes
            
            /* Initialize the auxiliary sensor interface */
            rslt = bmi160_aux_init(&m_bmi);
            handle_potential_error(rslt, "bmi160_aux_init");
            /* Auxiliary sensor is enabled and can be accessed from this point */
            
            /* Configure the desired settings in auxiliary BMM150 sensor 
             * using the bmm150 APIs */
            /* Initialising the bmm150 sensor */
            rslt = bmm150_init(&m_bmm);
            handle_potential_error(rslt, "bmm150_init");
            
            /* After the above function call, accel and gyro parameters in the device structure 
              are reset with default values, found in the datasheet of the sensor */
            
            m_bmi.accel_cfg.odr     = BMI160_ACCEL_ODR_25HZ;       // Output data rate
            m_bmi.accel_cfg.range   = BMI160_ACCEL_RANGE_2G;        // Range 
            m_bmi.accel_cfg.bw      = BMI160_ACCEL_BW_NORMAL_AVG4;  // Bandwidth 
            m_bmi.accel_cfg.power   = BMI160_ACCEL_NORMAL_MODE;     // Power mode
            
            m_bmi.gyro_cfg.odr      = BMI160_GYRO_ODR_25HZ;        // Output data rate
            m_bmi.gyro_cfg.range    = BMI160_GYRO_RANGE_500_DPS;    // Range 
            m_bmi.gyro_cfg.bw       = BMI160_GYRO_BW_NORMAL_MODE;   // Bandwidth
            m_bmi.gyro_cfg.power    = BMI160_GYRO_NORMAL_MODE;      // Power mode 
    
            m_bmm.settings.preset_mode= BMM150_PRESETMODE_REGULAR;
            rslt = bmm150_set_presetmode(&m_bmm);
            handle_potential_error(rslt, "bmm150_set_presetmode");
     
            // It is important that the last write to the BMM150 sets the forced mode.
            // This is because the BMI160 writes the last value to the auxiliary sensor 
            // after every read 
            m_bmm.settings.pwr_mode = BMM150_FORCED_MODE;
            rslt = bmm150_set_op_mode(&m_bmm);
            handle_potential_error(rslt, "bmm150_set_op_mode");
                   
            m_bmi.aux_cfg.aux_odr = BMI160_AUX_ODR_25HZ; // Aux polling frequency
            rslt = bmi160_config_aux_mode(&m_bmi);
            handle_potential_error(rslt, "bmi160_config_aux_mode");
    
            uint8_t aux_addr = BMM150_DATA_X_LSB; // BMM150 Mag data starts from register address 0x42 
            rslt = bmi160_set_aux_auto_mode(&aux_addr, &m_bmi);
            handle_potential_error(rslt, "bmi160_set_aux_auto_mode");
             
            rslt = bmi160_set_sens_conf(&m_bmi); // Set configuration
            handle_potential_error(rslt, "bmi160_set_sens_conf");
    }

     

    I initiate the tests and calibration using the following snippet:

     

           setup_sensor();
           test_sensor();
           setup_sensor();
           calibrate_sensor();

     

    6 REPLIES 6

    Vincent
    Community Moderator
    Community Moderator

    BMM150 self test can only be done under aux interface manual mode.  in your setup code, it is already change to auto mode,  then the self test is failed. 

    Set the aux interface to auto mode should be the last step before data streaming. 

    BMI160 starts with sleep mode.  but the FOC can only be done in normal mode.  in your code, there is no bmi160_set_power_mode called to change the default power mode to normal. 

    Thank you for your reply. As  per your suggestions I have modified my code to read as follows:

            int8_t rslt; 
            
            m_bmi.id = BMI160_I2C_ADDR;
            m_bmi.interface = BMI160_I2C_INTF;
            m_bmi.read =  i2c_read;
            m_bmi.write = i2c_write;
            m_bmi.delay_ms = user_delay_ms;
            rslt = bmi160_init(&m_bmi);
            handle_potential_error(rslt, "bmi160_init");
    
            /// Configure auxiliary sensor
            /* Configure the BMM150 device structure by mapping user_aux_read and user_aux_write */
            m_bmm.read = user_aux_read;
            m_bmm.write = user_aux_write;
            m_bmm.dev_id = BMM150_DEFAULT_I2C_ADDRESS; 
            /* Ensure that sensor.aux_cfg.aux_i2c_addr = bmm150.id for proper sensor operation */
            m_bmm.delay_ms = user_delay_ms;
            m_bmm.intf = BMM150_I2C_INTF;
     
            /* Configure device structure for auxiliary sensor parameter */
            m_bmi.aux_cfg.aux_sensor_enable = BMI160_ENABLE; // auxiliary sensor enable
            m_bmi.aux_cfg.aux_i2c_addr = m_bmm.dev_id; // auxiliary sensor address
            m_bmi.aux_cfg.manual_enable = BMI160_ENABLE; // setup mode enable
            m_bmi.aux_cfg.aux_rd_burst_len = BMI160_AUX_READ_LEN_3 ;// burst read; 8 bytes
            
            /* Initialize the auxiliary sensor interface */
            rslt = bmi160_aux_init(&m_bmi);
            handle_potential_error(rslt, "bmi160_aux_init");
            /* Auxiliary sensor is enabled and can be accessed from this point */
            
            /* Configure the desired settings in auxiliary BMM150 sensor 
             * using the bmm150 APIs */
            /* Initialising the bmm150 sensor */
            rslt = bmm150_init(&m_bmm);
            handle_potential_error(rslt, "bmm150_init");
            
            /* After the above function call, accel and gyro parameters in the device structure 
              are reset with default values, found in the datasheet of the sensor */
            
            m_bmi.accel_cfg.odr     = BMI160_ACCEL_ODR_25HZ;       // Output data rate
            m_bmi.accel_cfg.range   = BMI160_ACCEL_RANGE_2G;        // Range 
            m_bmi.accel_cfg.bw      = BMI160_ACCEL_BW_NORMAL_AVG4;  // Bandwidth 
            m_bmi.accel_cfg.power   = BMI160_ACCEL_NORMAL_MODE;     // Power mode
            
            m_bmi.gyro_cfg.odr      = BMI160_GYRO_ODR_25HZ;        // Output data rate
            m_bmi.gyro_cfg.range    = BMI160_GYRO_RANGE_500_DPS;    // Range 
            m_bmi.gyro_cfg.bw       = BMI160_GYRO_BW_NORMAL_MODE;   // Bandwidth
            m_bmi.gyro_cfg.power    = BMI160_GYRO_NORMAL_MODE;      // Power mode 
            
            // Configure interrupts
            //struct bmi160_int_settg int_config;
            //int_config.int_channel  = BMI160_INT_CHANNEL_1;               // Select interrupt channel/pin 1
            //int_config.int_type     = BMI160_ACC_GYRO_DATA_RDY_INT;       // Choose DATA_READY interrupt
            //int_config.int_pin_settg.output_en   = BMI160_ENABLE;         // Enabling interrupt pins to act as output pin
            //int_config.int_pin_settg.output_mode = BMI160_DISABLE;        // Choosing push-pull mode for interrupt pin
            //int_config.int_pin_settg.output_type = BMI160_ENABLE;         // ENABLE= active high output
            //int_config.int_pin_settg.edge_ctrl   = BMI160_ENABLE;         // Choosing edge triggered output
            //int_config.int_pin_settg.input_en    = BMI160_DISABLE;        // Disabling interrupt pin to act as input
            //int_config.int_pin_settg.latch_dur   = BMI160_LATCH_DUR_NONE; // Non-latched output
            //rslt = bmi160_set_int_config(&int_config, &m_bmi);            // Set the interrupt
            //handle_potential_error(rslt, "bmi160_set_int_config");
            
            m_bmm.settings.preset_mode= BMM150_PRESETMODE_REGULAR;
            rslt = bmm150_set_presetmode(&m_bmm);
            handle_potential_error(rslt, "bmm150_set_presetmode");
     
            // It is important that the last write to the BMM150 sets the forced mode.
            // This is because the BMI160 writes the last value to the auxiliary sensor 
            // after every read 
            m_bmm.settings.pwr_mode = BMM150_FORCED_MODE;
            rslt = bmm150_set_op_mode(&m_bmm);
            handle_potential_error(rslt, "bmm150_set_op_mode");
            
            
            m_bmi.aux_cfg.aux_odr = BMI160_AUX_ODR_25HZ; // Aux polling frequency
            rslt = bmi160_config_aux_mode(&m_bmi);
            handle_potential_error(rslt, "bmi160_config_aux_mode");
    
            m_bmi.accel_cfg.power = BMI160_ACCEL_NORMAL_MODE;
            m_bmi.gyro_cfg.power = BMI160_GYRO_NORMAL_MODE;
            rslt = bmi160_set_power_mode(&m_bmi);
            handle_potential_error(rslt, "bmi160_set_power_mode");
     
            rslt = bmi160_set_sens_conf(&m_bmi); // Set configuration
            handle_potential_error(rslt, "bmi160_set_sens_conf");
     
            // sensor must be tested before setting auto mode for aux, otherwise magnetometer test fails
            test_sensor();
            
            calibrate_sensor();
           
            uint8_t aux_addr = BMM150_DATA_X_LSB; // BMM150 Mag data starts from register address 0x42 
            rslt = bmi160_set_aux_auto_mode(&aux_addr, &m_bmi);
            handle_potential_error(rslt, "bmi160_set_aux_auto_mode");

     

    Unfortunately I still get the same results -- the magnetometer test fails and the FOC calibrration also fails. According to the BMX160 data sheet before initiating the magnetometer self test one must:

    1. Put the magnetometer interface into normal mode by writing 0x19 to register 0x7E -- this is done through bmi160_aux_init -> config_aux_settg -> config_sec_if
    2. Configured the magnetometer into setup mode by setting mag_manual_enbit in Register (0x4C-0x4F) MAG_IFto “1” -- I don't see this being done anywhere, and I also don't see how I can do this through the API without resorting to manual register writes. Is there a way to do this through the API?

    As for the FOC test I still don't understand why it's failing. In your reply you say that my code does not calll bmi160_set_power_mode() before attempting the calibration, but it does call bmi160_set_sens_conf() which accomplishes the same thing. In my revised code example I have added a call to bmi160_set_power_mode(), which I think is unnecessary, and it still fails.

    Thank you very much for helping me with this!

    Vincent
    Community Moderator
    Community Moderator

    Did you do the following things before you call bmm150_init and other functions inside bmm150.c?  

    /*bmm150 structure*/
    bmm150.dev_id = BMM150_DEFAULT_I2C_ADDRESS;
    bmm150.write = bmi160_aux_read;
    bmm150.read = bmi160_aux_write;
    bmm150.delay_ms = coines_delay_msec;
    bmm150.intf = BMM150_I2C_INTF;

     m_bmi.aux_cfg.manual_enable = BMI160_ENABLE; // setup mode enable

    This is already set manual mode enable when you call bmi_aux_init. 

    In your previous code, you didn't change the accel and gyro power mode to normal.

    Can you check the register 0x02 of BMX160? 

    Do you able to read out sensor data correctly after you enable accel and gyro data? 

    1. Regarding the FOC failure: I checked the value of the register and it is equal to zero  after  the function calls  fails. I stepped through the program with a debugger, and the tests passed when stepping through the program, indicating there is a timing issue. In bmi160.c:trigger_foc() I increased the max number of times that the while() loop is executed and added debugging statements.  My modified library code looks like this now:

     

    static int8_t trigger_foc(struct bmi160_offsets *offset, struct bmi160_dev const *dev)
    {
        int8_t rslt;
        uint8_t foc_status;
        uint8_t cmd = BMI160_START_FOC_CMD;
        uint8_t timeout = 0;
        uint8_t data_array[20];
    
        /* Start the FOC process */
        rslt = bmi160_set_regs(BMI160_COMMAND_REG_ADDR, &cmd, 1, dev);
        if (rslt == BMI160_OK)
        {
            /* Check the FOC status*/
            rslt = get_foc_status(&foc_status, dev);
            fprintf(stderr, "Checking foc status: rslt=%d, foc_status=%d\n",rslt, foc_status);
            if ((rslt != BMI160_OK) || (foc_status != BMI160_ENABLE))
            {
                while ((foc_status != BMI160_ENABLE) && (timeout < 32))
                {
                    /* Maximum time of 250ms is given in 10
                     * steps of 25ms each - 250ms refer datasheet 2.9.1 */
                    dev->delay_ms(25);
    
                    /* Check the FOC status*/
                    rslt = get_foc_status(&foc_status, dev);
                    fprintf(stderr, "Checking foc status: rslt=%d, foc_status=%d timeout=%d ts=%lld\n",rslt, foc_status, timeout, get_timestamp_us());
                    timeout++;
                }
                if ((rslt == BMI160_OK) && (foc_status == BMI160_ENABLE))
                {
                    /* Get offset values from sensor */
                    rslt = bmi160_get_offsets(offset, dev);
                    fprintf(stderr, "Geetting offsets rslt=%d, foc_status=%d ts=%lld\n",rslt, foc_status, get_timestamp_us());
                }
                else
                {
                    /* FOC failure case */
                    rslt = BMI160_FOC_FAILURE;
                }
            }
            if (rslt == BMI160_OK)
            {
                /* Read registers 0x04-0x17 */
                rslt = bmi160_get_regs(BMI160_GYRO_DATA_ADDR, data_array, 20, dev);
                fprintf(stderr, "read registers %d", rslt);
            }
        }
    
        return rslt;
    }

     

    With these modifications, the FOC calibration succeeds on the 20th attempt. The debug output is the following:

    Checking foc status: rslt=0, foc_status=0
    Checking foc status: rslt=0, foc_status=0 timeout=0 ts=1594390337401459
    Checking foc status: rslt=0, foc_status=0 timeout=1 ts=1594390337427819
    Checking foc status: rslt=0, foc_status=0 timeout=2 ts=1594390337454171
    Checking foc status: rslt=0, foc_status=0 timeout=3 ts=1594390337480592
    Checking foc status: rslt=0, foc_status=0 timeout=4 ts=1594390337506955
    Checking foc status: rslt=0, foc_status=0 timeout=5 ts=1594390337533314
    Checking foc status: rslt=0, foc_status=0 timeout=6 ts=1594390337559649
    Checking foc status: rslt=0, foc_status=0 timeout=7 ts=1594390337586005
    Checking foc status: rslt=0, foc_status=0 timeout=8 ts=1594390337612341
    Checking foc status: rslt=0, foc_status=0 timeout=9 ts=1594390337638703
    Checking foc status: rslt=0, foc_status=0 timeout=10 ts=1594390337665110
    Checking foc status: rslt=0, foc_status=0 timeout=11 ts=1594390337691551
    Checking foc status: rslt=0, foc_status=0 timeout=12 ts=1594390337717919
    Checking foc status: rslt=0, foc_status=0 timeout=13 ts=1594390337744286
    Checking foc status: rslt=0, foc_status=0 timeout=14 ts=1594390337770655
    Checking foc status: rslt=0, foc_status=0 timeout=15 ts=1594390337797020
    Checking foc status: rslt=0, foc_status=0 timeout=16 ts=1594390337823495
    Checking foc status: rslt=0, foc_status=0 timeout=17 ts=1594390337849858
    Checking foc status: rslt=0, foc_status=0 timeout=18 ts=1594390337876225
    Checking foc status: rslt=0, foc_status=1 timeout=19 ts=1594390337902595
    Geetting offsets rslt=0, foc_status=1 ts=1594390337904053

    While the calibration succeeds now, I don't understand why. The results of my debugging seem to contradict the datasheet, which states that the FOC routine completes within 250ms. The timestamps above are in epoch time in microseconds and show that my implementation of user_delay_ms() is working as intended. The calibration always  seems to require 20 attempts to complete, i.e. it always succeeds after timeout=19 as above. I am running this on Linux on a Raspberry Pi over I2C.

    2. I have made no progress with the magnetometer self-test. Both normal and advanced self-tests fail, with BMM150_W_NORMAL_SELF_TEST_XYZ_FAIL.

    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