0
\$\begingroup\$

I am working on a compass module. This module provides the information through 6 registers. In fact, there are 3 pairs of registers.Each register pertaining to one coordinate (X,Y and Z). In each pair, there is a MSB register and a LSB register. Data of the MSB register, shift 8 times and its logical OR with the LSB register is computed. The strange thing I observe is when I rotate the Module, in some degrees of rotation, some data show up in these registers but after a certain point it is frozen on a value like 3c. It goes like this, from 00 to 3c the value changes, but after exceeding 3c, only 3c is read out and I have to reset the module. By the way, the Module is HMC5883L.

  • I wonder Where is my mistake?
  • Is there any further assumptions that I am not considering?
  • Have I messed up with computations?

I'm doing as stated in Datashed

Write CRA (00) – send 0x3C 0x00 0x70 (8-average, 15 Hz default or any other rate, normal measurement) 2. Write CRB (01) – send 0x3C 0x01 0xA0 (Gain=5, or any other desired gain) 3. For each measurement query: Write Mode (02) – send 0x3C 0x02 0x01 (Single-measurement mode) Wait 6 ms or monitor status register Send 0x3D 0x06 (Read all 6 bytes. If gain is changed then this data set is using previous gain) Convert three 16-bit 2’s compliment hex values to decimal values and assign to X, Z, Y, respectively.

I suspect this part :

Send 0x3D 0x06 (Read all 6 bytes)

What does it mean exactly?

and the other thing is, do we have to provide address of the every register that intended to be read? Or we only need to provide the first register address and the I2C takes care of the next registers?

Is there an simplified example code?

\$\endgroup\$
  • 1
    \$\begingroup\$ By empty, you mean they have zeros? Does it happen for both the LSB and MSB of the same coordinate and at the same time? \$\endgroup\$ – Big6 Dec 5 '16 at 18:58
  • \$\begingroup\$ @SixtoCabrera , no they are not zero, it shows an value and while rotating suddenly freezes that value. like this, I read out the values from 00 to 3c, and then its values is locked on 3c value. I have to reset it. After rest, this again happens. \$\endgroup\$ – MimSaad Dec 5 '16 at 19:08
  • 1
    \$\begingroup\$ Is you I2C implementation correct? Could you post what your using and how? Is the chip configured correctly? Could you post your configurations? \$\endgroup\$ – vini_i Dec 5 '16 at 19:15
  • \$\begingroup\$ @vini_i , I added more information. \$\endgroup\$ – MimSaad Dec 5 '16 at 19:55
3
\$\begingroup\$

There are a few different parts to your problem:

"Frozen" data registers

some data show up in these registers but after a certain point it is frozen

This is expected behaviour if you have read from any of the 6 data registers, but have not read from all 6 of them; or if you read from the Mode register and haven't written to it again. This is explained in the datasheet:

Data Output Register Operation

When one or more of the output registers are read, new data cannot be placed in any of the output data registers until all six data output registers are read. This requirement also impacts DRDY and RDY, which cannot be cleared until new data is placed in all the output registers.

You can detect this "Lock" condition when you read the Status Register - bit 1 (SR1) is set in that condition, as explained in the datasheet page in the Status Register (I have slightly edited this, to remove a typo in the original text, and to slightly improve the original formatting):

Data output register lock.
This bit is set when:
1. some but not all [...] of the six data output registers have been read,
2. Mode register has been read.
When this bit is set, the six data output registers are locked and any new data will not be placed in these register until one of these conditions are met:
1. all six bytes have been read,
2. the Mode register is changed,
3. the measurement configuration (CRA) is changed,
4. power is reset.

So your challenge is to debug your program, to find how you could cause that situation where you do not read all of the data registers, or where you read the Mode register and don't write it again.

Strange I2C command sequence in the datasheet

You said:

I suspect this part :

Send 0x3D 0x06 (Read all 6 bytes)

What does it mean exactly?

Good question. I haven't used that specific compass IC, but from I2C knowledge, that command makes no sense. 0x3D is the compass 8-bit I2C Read address, meaning that as soon as the I2C Slave device (i.e. here, the compass) ACKs its address, it has control of the bus - the I2C Master cannot send another byte (i.e. here, the 0x06) if the Master correctly follows the I2C specification.

I did a quick search and none of the code samples I found for this device, use that I2C sequence. At least one person claimed that attempting that command caused a hang - perhaps attempting this invalid I2C sequence caused the code running on their MCU master to hang.

See the next point for the suggested command sequence.

Other questions

Or we only need to provide the first register address and the I2C takes care of the next registers?

Almost - you do an I2C transaction to write the address of the first register you want to read. That is why you see the suggestion:

Send 0x3C 0x03

... before reading the 6 data registers.

Then this device implements auto-incrementing of the register address for the subsequent reads. This behaviour is optional in the I2C specification, so don't assume that "I2C takes care" of this on every device - read the device datasheet (and some sample code) to see the specifics. For example, this device's datasheet says:

To clock out the new data, send: 0x3D, and clock out DXRA, DXRB, DZRA, DZRB, DYRA, and DYRB located in registers 3 through 8. The HMC5883L will automatically re-point back to register 3 for the next 0x3D query. All six data registers must be read properly before new data can be placed in any of these data registers.

In my search, I didn't see any examples where the code relies on the claimed internal register pointer "re-pointing" back to register 3 mentioned in that part of the datasheet. I even found a note which explicitly says that such behaviour does not happen on the HMC5883L, although it did on the older HMC5843. Therefore I would not rely on this sentence in the datasheet. Instead I would repeat the "Send 0x3C 0x03" sequence before each "loop" of reading all 6 data registers, as shown in the "continuous measurement" example loop in the datasheet.

Is there an simplified example code?

I found lots of code examples in a quick search, mainly aimed at Arduino usage, but it's just I2C, so it can be ported to another platform, if needed.

One final point: It isn't clear if you are using a ready-made PCB module which you bought somewhere, or the PCB is your own design. Especially if this is your design, note that this type of device causes relatively large, short duration, power consumption spikes from its required capacitors (C1 and C2 in the datasheet schematic). Therefore the PCB layout and those capacitors are critical to reliable operation. Again, there is advice in the datasheet.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.