Skip to main content
Graduate II
February 16, 2024
Question

Magnetometer output seem false (IIS2MDC)

  • February 16, 2024
  • 7 replies
  • 3929 views

Hi everyone,

 

First of all, I use a STWINBX1 board, which include a MCU and several sensors. I try to get the magnetic field by reading the output of the magnetometer (IIS2MDC), but the output value seems false.

Please note that :

- I checked the PIN configuration on STM32 CubeIDE, and the PINs are well configured.

- I use HAL library to configure the chipset (IIS2MDC), and to use I2C protocol.

- I tested 2 differents methods of reading the output using I2C functions from HAL (HAL_I2C_Mem_Read and HAL_I2C_Master_Receive). Both method return me the same output, which is even more confusing for me.

- I have two differents boards of the same models that give me the same errors in the output.

- The slave address (of the Magnetometer) is 0011110b. I have two variable for read and write the slave, which are :

 

 

 

 

uint16_t IIS2MDCTR_ADDR_VAR_R = 0x3D; // 00111101b
uint16_t IIS2MDCTR_ADDR_VAR_W = 0x3C; // 00111100b

 

 

 

 

 I store them inside a uint16_t as the HAL library need uint16_t address in functions that I use, but these addresses could fit in a single byte.

I am not used to these kinds of datasheet, so I have severals questions.

The datasheet of the magnetometer give an output x, y, z. Each axis are composed of two bytes. Its said that the output is expressed in "two’s complement". 

Basically, that mean that the output will be a signed integer (that can be positive or negative) ?

As the output is given on two different bytes, I guess that the sign of the total output (combination of the two bytes) is the biggest bit of the MSB ?

Here is one of the two method I used to read the output of the magnetometer (on one axis, "x", for example) :

I removed errors managements of the code to make it clearer

 

 

 

 

int16_t getMagneticField(uint8_t LSB_addr){
	char buffer_message[100];
	uint8_t buf[2];
	uint8_t buf_receive[2];
	uint16_t slave_addr_read = 0x3D;
	uint16_t slave_addr_write = 0x3C;

	buf[0] = 0x60; // address of CFG_REG_A (used to configure how we use the magnetometer)
	buf[1] = 0x81; // 1000 0001 bit configured to allow single mode
	HAL_I2C_Master_Transmit(&hi2c2, slave_addr_write, buf, 2, HAL_MAX_DELAY);

	HAL_StatusTypeDef ret = HAL_I2C_Mem_Read(&hi2c2, slave_addr_read, LSB_addr, I2C_MEMADD_SIZE_8BIT, buf_receive, 2, HAL_MAX_DELAY);
	
	int16_t result = (((int16_t)buf_receive[1]) << | buf_receive[0];

	return result * sensitivity;
}

 

 

 

 

For context, here is the configuration of the magnetometer :

 

 

 

 

void ConfigureMagnetometer()
{
	uint8_t buf[2];
	buf[0] = 0x60; // addresse du registre CFG_REG_A
	buf[1] = 0x81; // 1000 0001 bit configured (enable temperature/single mode read)
	HAL_I2C_Master_Transmit(&hi2c2, IIS2MDCTR_ADDR_W, buf, 2, HAL_MAX_DELAY);
	buf[0] = 0x61; // addresse du registre CFG_REG_B
	buf[1] = 0x12; // 0001 0010 bit configured (enable offset cancellation in single mode read)
	HAL_I2C_Master_Transmit(&hi2c2, IIS2MDCTR_ADDR_W, buf, 2, HAL_MAX_DELAY);
	buf[0] = 0x62; // addresse du registre CFG_REG_C
	buf[1] = 0x10; // 0001 0000 bit configured
	HAL_I2C_Master_Transmit(&hi2c2, IIS2MDCTR_ADDR_W, buf, 2, HAL_MAX_DELAY);

	HAL_Delay(20);
}

 

 

 

 

So, my problem is :

When i read the first byte of data (the LSB), the value seem fine. She is updating as I move the board myself.
The other byte of data (the MSB), is always min value (0), or max value (255). He is sometimes equal to 1 or 254, but more rarely, which seems false. And it seem to exist a pattern with these value, with max, min or max -1, or min + 1.

In addition, I intepret the output x, y, z with a simple computing (sqrt of squarred x + squarred y + squarred z), and also this intepretation give me false results, as this result must be constant (when x, y and z change), but its not the case. 

I tried to give you the more context I can. If you need any others information to understand my problem, feel free to ask, I'll answer you.
I don't know what I am doing wrong here. Thanks for your help, have a good day.

    This topic has been closed for replies.

    7 replies

    Super User
    February 16, 2024

    An int16_t value expressed as two unsigned bytes will look like this. The problem is just how you're interpreting it.

    value = MSB LSB
    ...
    -2 = 0xFF 0xFE
    -1 = 0xFF 0xFF
    0 = 0x00 0x00
    1 = 0x00 0x01
    2 = 0x00 0x02
    ...

    Notice how the MSB goes from 0 to 255 even though the value only changes by 1.

    Super User
    February 16, 2024

    Since the registers are in little endian format (LSB first), you should be able to convert it directly to int16 with the following:

    int16_t value = 0;
    HAL_I2C_Mem_Read(&hi2c2, slave_addr_read, LSB_addr, I2C_MEMADD_SIZE_8BIT, (uint8_t*)&value, 2, HAL_MAX_DELAY);

     

    Graduate II
    February 16, 2024

    Ok, 

    thanks a lot for your answer, I didnt paid attention to the endianess.

    My output (sqrt(squarred(x) + squarred(y) + squarred(z))) is still wrong.

    He vary from 2 values even If i dont move the sensor.
    Here is a sample of the output if the sensor is stationary

    sqrt = 670.387202
    sqrt = 899.553778
    sqrt = 666.946025
    sqrt = 903.058691
    sqrt = 652.453830
    sqrt = 894.912286
    sqrt = 659.749195
    sqrt = 910.469110
    sqrt = 655.916915
    sqrt = 906.294654
    sqrt = 662.160857
    sqrt = 915.705739
    sqrt = 657.499810
    sqrt = 907.316924
    sqrt = 650.509031
    sqrt = 902.203414
    sqrt = 654.368398
    sqrt = 901.768263
    sqrt = 652.144923
    sqrt = 909.713691
    sqrt = 654.878615
    sqrt = 903.834609
    sqrt = 662.786542
    sqrt = 896.974916
    sqrt = 669.574492
    sqrt = 906.651532
    And if I move the board, the output is varying. I think I miss something else.

    Graduate II
    February 19, 2024

    Here is a sample of my result (sqrt(squarred(x) + squarred(y) + squarred(z))) with x, y and z values (when the magnetometer is stationary):

    By the way, I checked some github repositories that use IIS2MDC and they seems to return a int32 (for x, y and z values), as these axis are int16_t multiplied by sensitivity (sensitivity = 1.500). So I think int16_t may overflow if we dont cast axis * sensitivity in int32_t. So I changed , my return statement from 

    return result * sensitivity; // was returning an int16_t

    to

    return (int32_t)((float)((float)value * sensitivity)); // now return an int32_t

    It seems to still fluctuate around two values when the magnetometer is stationary
    sqrt = 175.002857(x:7 y:79 z:156)
    sqrt = 538.209067(x:225 y:462 z:160)
    sqrt = 440.424795(x:85 y:43 z:430)
    sqrt = 548.681146(x:213 y:469 z:189)
    sqrt = 423.381625(x:42 y:88 z:412)
    sqrt = 690.890006(x:198 y:490 z:445)
    sqrt = 433.504325(x:43 y:94 z:421)
    sqrt = 689.281510(x:202 y:487 z:444)
    sqrt = 429.760398(x:27 y:82 z:421)
    sqrt = 687.393628(x:195 y:489 z:442)
    sqrt = 425.495006(x:37 y:91 z:414)
    sqrt = 695.765765(x:184 y:495 z:453)
    sqrt = 423.933957(x:40 y:82 z:414)
    sqrt = 693.220744(x:189 y:495 z:447)
    sqrt = 437.280231(x:33 y:78 z:429)
    sqrt = 689.962318(x:184 y:484 z:456)
    sqrt = 434.986207(x:34 y:91 z:424)
    sqrt = 696.862253(x:207 y:492 z:448)
    sqrt = 437.133847(x:31 y:93 z:426)
    sqrt = 694.612122(x:193 y:489 z:454)
    sqrt = 426.852434(x:33 y:85 z:417)
    sqrt = 688.881702(x:205 y:487 z:442)
    sqrt = 438.466646(x:34 y:84 z:429)
    sqrt = 686.232468(x:189 y:487 z:445)
    sqrt = 431.435974(x:22 y:82 z:423)
    sqrt = 695.920973(x:193 y:489 z:456)
    sqrt = 429.617272(x:33 y:79 z:421)
    sqrt = 699.886419(x:189 y:498 z:454)
    sqrt = 431.533313(x:19 y:78 z:424)

    I didnt changed configuration's registers from first messages. Still can't find my error.

    Super User
    February 19, 2024

    Those values aren't just fluctuating, they are changing every other cycle. Not something that can be explained by misinterpreting the result. This suggests a code error somewhere else in the program. It's like you're getting different values on every other read. Perhaps you are not getting the magnetometer values.

    Graduate II
    February 19, 2024

    If I use the sensor in continuous mode (instead of single mode) I can get more stable sample, like :

    (but theses value really change if I move the board, which is not good)

    Spoiler
    sqrt = 641.922893(x:-456 y:375 z:-252)
    sqrt = 642.316900(x:-451 y:381 z:-253)
    sqrt = 647.569301(x:-459 y:381 z:-252)
    sqrt = 641.568391(x:-453 y:376 z:-255)
    sqrt = 642.072426(x:-454 y:379 z:-250)
    sqrt = 648.742630(x:-459 y:381 z:-255)
    sqrt = 644.035713(x:-454 y:379 z:-255)
    sqrt = 648.545295(x:-457 y:379 z:-261)
    sqrt = 643.330397(x:-453 y:381 z:-252)
    sqrt = 646.793630(x:-459 y:381 z:-250)
    sqrt = 642.161195(x:-453 y:381 z:-249)
    sqrt = 648.180530(x:-459 y:384 z:-249)
    sqrt = 645.742983(x:-460 y:378 z:-250)
    sqrt = 637.838538(x:-453 y:373 z:-250)
    sqrt = 641.142730(x:-454 y:372 z:-258)
    sqrt = 644.511443(x:-453 y:381 z:-255)
    sqrt = 644.034937(x:-454 y:381 z:-252)
    sqrt = 643.923132(x:-453 y:382 z:-252)
    sqrt = 644.795316(x:-457 y:376 z:-256)
    sqrt = 647.964505(x:-459 y:379 z:-256)

    When I was using single mode, as said the doc, I reconfigure to single mode after each read of register (as the sensor return to idle mode after he is read).

    Super User
    February 19, 2024

    These values look good. I don't see any problems with them. You should expect some amount of noise in the readings.

    > but theses value really change if I move the board, which is not good

    Moving the board will affect the readings, no surprise or issues there. Surely it would be a problem if they didn't change, right? Magnetic field will be affected by orientation of the board and proximity to conductive materials.

    Graduate II
    February 19, 2024

    I read that this formula :

    formula.png

    should give a constant result (even if we move the board). x, y and z should change, but not the result of this formula. 

    But maybe I missintepretated the information.

    And if I move the board in the space, the output vary a lot, another sample :

    Spoiler
    sqrt = 383.669128(x:-175 y:201 z:-276)
    sqrt = 480.163514(x:-366 y:124 z:285)
    sqrt = 460.194524(x:-195 y:-273 z:315)
    sqrt = 395.055692(x:-112 y:-367 z:94)
    sqrt = 250.433624(x:42 y:-208 z:-133)
    sqrt = 447.889495(x:-60 y:-403 z:186)
    sqrt = 348.902565(x:-312 y:58 z:-145)
    sqrt = 349.264942(x:-235 y:-244 z:85)
    sqrt = 363.331804(x:-220 y:-267 z:111)
    sqrt = 367.016348(x:-324 y:22 z:-171)
    sqrt = 518.497830(x:-186 y:370 z:-312)
    sqrt = 573.880650(x:165 y:505 z:-217)
    sqrt = 642.066196(x:183 y:574 z:-222)
    sqrt = 646.623538(x:187 y:583 z:-208)

     

    Graduate II
    February 20, 2024

    A little update if someone want to take informations from this post

    - The documentation seems to say that continous mode (configurable in CFG_REG) automatically apply offset.

    - Its also said (in application note => an5080-iis2mdc.pdf page 19/26 : If we use the sensors in single-mode instead of continuous mode.

    "The computation of the hard-iron distortion field should be performed by an external processor."

     

    I dont know why, but it seems that we need an external sensor to measure the offset if we want to use IIS2MDC in single mode.

    If someone have more informations about that, feel free to share !

     

    Super User
    February 20, 2024

    The readings from a magnetic field is difficult to use. If you want your equation to hold, your sensor must be calibrated to remove the offset and apply scaling factors. This calibration is nontrival. For example, here is a 30 page manual for how to do it on one device:

    https://www.mouser.com/pdfDocs/MagneticCalibrationManual-2.pdf

    On this device, calibration would be setting the OFFSET_X_REG values to the midpoint of the values obtained when rotating the sensor in a circle among all orientations.