2024-04-11 02:59 AM
Hi,
I'm attempting to use an LSM303agr magnetometer and calibrate it using the MotionMC library.
My issue is that the MotionMC MMC_Output_t struct always remains at its default value.
I've tried different hardware setups:
I followed the application note UM2192
Here are the steps I've taken:
1-Enabled CRC in Cube MX.
2-Configured MotionMC in the software Pack and enabled it in the Middleware panel of Cube MX.
3-Configured the I2C to communicate with the LSM303agr.
4-At the beginning of the main code, I set up the magnetometer like this:
/*
@brief configure the contol register of the magnetometer
@param lsm303agr : pointer to the struct associate to the device to read
@retval status of the execution
*/
lsm303agr_status_t lsm303agr_setup_magnetometer(lsm303agr_t *lsm303agr)
{
uint8_t cfg_reg_a_m=0;
uint8_t cfg_reg_b_m=0;
uint8_t cfg_reg_c_m=0;
lsm303agr_read_register(lsm303agr,LSM303AGR_CFG_REG_A_M,&cfg_reg_a_m);
printf("CFG_REG_A_M 0x%x\n",cfg_reg_a_m);
lsm303agr_read_register(lsm303agr,LSM303AGR_CFG_REG_B_M,&cfg_reg_b_m);
printf("CFG_REG_B_M 0x%x\n",cfg_reg_b_m);
lsm303agr_read_register(lsm303agr,LSM303AGR_CFG_REG_C_M,&cfg_reg_c_m);
printf("CFG_REG_C_M 0x%x\n",cfg_reg_c_m);
//0b100001100 = 0x8C
//COMP_TEMP_EN = 1 -> mag temp compensation enabled
//REBOOT = 0 -> reboot memory content disabled
//SOFT_RST = 0 -> soft reset disabled
//LP = 0 -> low power mode disabled
//ODR[1:0] = 0x11 -> 100Hz
//MD[1:0] = 0x00 -> continous mode
lsm303agr_write_register(lsm303agr,LSM303AGR_CFG_REG_A_M,0x8C);
//0b00000010 = 0x02
//OFF_CAN_ONE_SHOT = 0 -> offset cancelation in single mode disabled
//INT_on_Data_OFF = 0 -> interutp block recognition do not checks data after HI correction to discover the interupt
//set_FREQ = 0 -> frequency of the pulse is set to every 63 ODR
//OFF_CANC = 1 -> offset cancelation enabled
//LPF = 0 -> low pass filter disabled
lsm303agr_write_register(lsm303agr,LSM303AGR_CFG_REG_B_M,0x02);
//0b00010000 = 0x10
//INT_MAG_PIN = 0 -> interupt signal is not driven on INT_MAG_PIN
//I2C_DIS = 0 -> I2C is enabled
//BDU = 1 -> protection over wrong data enabled
//BLE = 0 -> LSB and MSB not inverted
//SELF_TEST = 0 -> self test disabled
//INT_MAG = 0 -> DRDY pin is not configure as GPIO Output
lsm303agr_write_register(lsm303agr,LSM303AGR_CFG_REG_C_M,0x10);
//set delay to let time to power up
HAL_Delay(LSM303AGR_DELAY_MAGNETOMETER_POWER_UP);
return LSM303AGR_OK;
}
5 - I initialized the MotionMC library and read the MotionMC library version (I obtained "ST MotionMC v2.6.1").
#define LSM303AGR_MAGNETOMETER_CALIBRATION_SAMPLE_TIME 200
.
.
.
MotionMC_Initialize(LSM303AGR_MAGNETOMETER_CALIBRATION_SAMPLE_TIME, 1);
char motion_mc_version[35] = {0};
MotionMC_GetLibVersion(motion_mc_version);
printf("motion mc version : %s \n",motion_mc_version);
6 - I configured a timer to call my calibration function every 200ms.
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim){
if (htim->Instance == TIM6) { //1ms
counter_200ms++;
if (counter_200ms > 200){
token_200ms = true;
counter_200ms = 0;
}
}
}
if (token_200ms){
lsm303agr_magnetometer_run_calib(&lsm303agr);
token_200ms = false;
}
MMC_CalQuality_t lsm303agr_magnetometer_run_calib(lsm303agr_t *lsm303agr)
{
//increment time count
lsm303agr->calib_time_count++;
MMC_Input_t data_in;
MMC_Output_t data_out;
// Get magnetic field X/Y/Z
lsm303agr_read_mag_output_register(lsm303agr);
data_in.Mag[0] = lsm303agr_mag_val_to_uT(lsm303agr->mag.x);
data_in.Mag[1] = lsm303agr_mag_val_to_uT(lsm303agr->mag.y);
data_in.Mag[2] = lsm303agr_mag_val_to_uT(lsm303agr->mag.z);
// Get current sample time in [ms]
data_in.TimeStamp = lsm303agr->calib_time_count * LSM303AGR_MAGNETOMETER_CALIBRATION_SAMPLE_TIME;
// Magnetometer calibration algorithm update
MotionMC_Update(&data_in);
// Get the magnetometer calibration coefficients
MotionMC_GetCalParams(&data_out);
printf("HI x %f\n",data_out.HI_Bias[0]);
printf("HI y %f\n",data_out.HI_Bias[1]);
printf("HI z %f\n",data_out.HI_Bias[2]);
printf("SF x %f %f %f \n",data_out.SF_Matrix[0][0],data_out.SF_Matrix[0][1],data_out.SF_Matrix[0][2]);
printf("SF x %f %f %f \n",data_out.SF_Matrix[1][0],data_out.SF_Matrix[1][1],data_out.SF_Matrix[1][2]);
printf("SF x %f %f %f \n",data_out.SF_Matrix[2][0],data_out.SF_Matrix[2][1],data_out.SF_Matrix[2][2]);
/*
// Apply calibration coefficients
mag_cal_x = (int)((data_in.Mag[0] - data_out.HI_Bias[0]) * data_out.SF_Matrix[0][0]
+ (data_in.Mag[1] - data_out.HI_Bias[1]) * data_out.SF_Matrix[0][1]
+ (data_in.Mag[2] - data_out.HI_Bias[2]) * data_out.SF_Matrix[0][2]);
mag_cal_y = (int)((data_in.Mag[0] - data_out.HI_Bias[0]) * data_out.SF_Matrix[1][0]
+ (data_in.Mag[1] - data_out.HI_Bias[1]) * data_out.SF_Matrix[1][1]
+ (data_in.Mag[2] - data_out.HI_Bias[2]) * data_out.SF_Matrix[1][2]);
mag_cal_z = (int)((data_in.Mag[0] - data_out.HI_Bias[0]) * data_out.SF_Matrix[2][0]
+ (data_in.Mag[1] - data_out.HI_Bias[1]) * data_out.SF_Matrix[2][1]
+ (data_in.Mag[2] - data_out.HI_Bias[2]) * data_out.SF_Matrix[2][2]);
*/
return data_out.CalQuality;
}
I read the magnetometer register as follows:
lsm303agr_status_t lsm303agr_read_mag_output_register(lsm303agr_t *lsm303agr)
{
//create a timeout and init it
timeout_t update_value_timeout;
//variable to store the different register value
uint8_t out_x_lsb =0;
uint8_t out_x_msb =0;
uint8_t out_y_lsb =0;
uint8_t out_y_msb =0;
uint8_t out_z_lsb =0;
uint8_t out_z_msb =0;
// variable to store the status register value
uint8_t status_reg_m = 0;
bool zyxda = 0;
//wait for new data read or for timeout
timeout_start(&update_value_timeout);
do{
lsm303agr_read_register(lsm303agr,LSM303AGR_STATUS_REG_M,&status_reg_m);
zyxda = status_reg_m && 0b00001000;
}
while ((zyxda == false) && (timeout_check(&update_value_timeout, LSM303AGR_UPDATE_MAGNETOMETER_VALUE_TIMEOUT) == false));
if (zyxda == true)
{
lsm303agr_read_register(lsm303agr,LSM303AGR_OUTX_L_REG_M,&out_x_lsb);
lsm303agr_read_register(lsm303agr,LSM303AGR_OUTX_H_REG_M,&out_x_msb);
lsm303agr_read_register(lsm303agr,LSM303AGR_OUTY_L_REG_M,&out_y_lsb);
lsm303agr_read_register(lsm303agr,LSM303AGR_OUTY_H_REG_M,&out_y_msb);
lsm303agr_read_register(lsm303agr,LSM303AGR_OUTZ_L_REG_M,&out_z_lsb);
lsm303agr_read_register(lsm303agr,LSM303AGR_OUTZ_H_REG_M,&out_z_msb);
lsm303agr->mag.x = (out_x_msb<<8) + out_x_lsb;
lsm303agr->mag.y = (out_y_msb<<8) + out_y_lsb;
lsm303agr->mag.z = (out_z_msb<<8) + out_z_lsb;
lsm303agr->mag.d = lsm303agr_mag_val_to_degres(lsm303agr->mag,lsm303agr->accel.horizon_parallel_plan);
return LSM303AGR_OK;
}
else
{
return LSM303AGR_ERROR_UPDATE_MAGNETOMETER_VALUE;
}
}
And convert the value in microtesla (uT):
/*
@brief Convert magnetometer data value to [uT]
@param mag_val : magnetometer data value
@retval magnetic field in [uT]
*/
float lsm303agr_mag_val_to_uT(int16_t mag_val)
{
float mgauss = lsm303agr_mag_val_to_mgauss(mag_val);
float uT = lsm303agr_mgauss_to_uT(mgauss);
return uT;
}
/*
@brief Convert magnetometer data value to [mGauss]
@param mag_val : magnetometer data value
@retval magnetometer data value [mGauss]
*/
float lsm303agr_mag_val_to_mgauss(int16_t mag_val)
{
//according to datasheet 1LSB = 1.5 mGauss
float mgauss = mag_val*1.5f;
return mgauss;
}
/*
@brief Convert [gauss] to [uT]
@param gauss : magnetic field value in [gauss]
@retval magnetic field value in [uT]
*/
float lsm303agr_mgauss_to_uT(float mgauss)
{
//1 Gauss = 0.0001 T <=> 1 mGauss = 0.1uT
float uT = mgauss/10.0f;
return uT;
}
However, I consistently receive the following output:
HI x 0.000000
HI y 0.000000
HI z 0.000000
SF x 1.000000 0.000000 0.000000
SF x 0.000000 1.000000 0.000000
SF x 0.000000 0.000000 1.000000
return MMC_CALQSTATUSUNKNOWN
I've checked the communication with the sensor, and it seems fine since I can read and write to its registers. I've also verified the timer period, which appears to be correct.
I've explored the following topics
and attempted several solutions, including:
- upgrade the head size to 0x1000 and the stack size to 0x2000
- wait for more than 20s, moving the sensor.
However, none of these solutions have worked. Could someone please assist me with this?
Thank you