07-03-2020 04:51 PM
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();
07-04-2020 01:59 AM
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.
07-04-2020 05:28 PM
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!
07-07-2020 02:40 AM
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?
07-10-2020 04:30 PM
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.