Shop OBEX P1 Docs P2 Docs Learn Events
3-Axis HMC5883L Compass Module Problems — Parallax Forums

3-Axis HMC5883L Compass Module Problems

nato1080nato1080 Posts: 8
edited 2014-08-21 14:41 in Accessories
Hello, I'm working on implementing the HMC5883L 3-Axis compass on my PIC32MX795F512L and I am working in C. My program implements I2C correctly I believe, however I am getting very strange data back from the compass. This is what I'm getting back:
X - 04E4
Y - 04AF
Z - 0478

Based on my understanding of the data sheet none of these are in the correct range.

Here is my command sequence:

I2C Start Event
Send Sequence - 0x3C, 0x00, 0x71
Send Sequence - 0x3C, 0x01, 0x02
Send Sequence - 0x3C, 0x02, 0x00

delay - 100ms - (Longer than necessary)

Send Sequence -0x3C, 0x03

I2C Repeat Start

Send Sequence - 0x3D

Execute 6 I2C read events followed by Acks on all except last
I2C Stop Event


This is the process I'm using for self test mode. This pretty much comes directly from the manual. There was one part of the manual I did not understand very well (and may be my problem). When reading it says send 0x3D 0x06. Is this literally the command that is supposed to be sent, because that is something I am not doing. Rather I am resetting the address pointer to location 3 (MSB of X register) and relying on the compass module to increment the address pointer after each read. I have tried changing the gain to pretty much the entire range, but it does not appear to have effect at all.

Thanks for any help you can provide.

Comments

  • Duane DegnDuane Degn Posts: 10,588
    edited 2014-08-20 13:25
    I played around with the HMC5883L a lot a few months ago. Hopefully I can remember enough about the sensor to be helpful.

    My first concern is the way the PIC uses I2C. The 0x3C address may have too many bits. If your PIC is expecting a 7-bit address, you want to use 0x1E instead of 0x3C.

    One think that tripped me up was the way the compass resets its internal pointer after the last data register is read.

    I look back at my program to see if anything else about your code jumps out at me.
  • Duane DegnDuane Degn Posts: 10,588
    edited 2014-08-20 13:47
    nato1080 wrote: »
    Send Sequence - 0x3C, 0x00, 0x71

    I'd suggest leaving the last two bits set to zero so you don't turn on the internal bias. Wait until you can read the raw data before changing this setting.

    I think I'd change the first two sends to:

    Send Sequence - 0x3C, 0x00, 0x10
    Send Sequence - 0x3C, 0x01, 0x20

    You might need to use:
    Send Sequence - 0x1E, 0x00, 0x10
    Send Sequence - 0x1E, 0x01, 0x20

    I think the configuration you where use would prevent proper operation (though I'm not sure).

    I don't suppose you have a Propeller? If you did you could set each parameter with the demo program I wrote.
  • nato1080nato1080 Posts: 8
    edited 2014-08-20 13:59
    Hey thanks for the quick reply. I'm pretty sure that sending 0x3C is ok. I was having issues with the I2C part before and started a thread over at Microchips's forum to work those issues out. However, I think that my current issue deals with how I'm handling the sensor itself. I just got a logic analyzer though so once I figure out how to use it (First time) I want to verify for sure that I2C is working correctly. Again, that being said I'm pretty sure that part is ok.

    The part of the manual that is really confusing me is the part 5 of the operational example (the Loop part). It says send 0x3D 0x06 to read all 6 bytes. Everywhere else where it has that kind of text it literally means send those values (ex. part 1 where it says 0x3C 0x00 0x70). However, I don't understand exactly what 0x3D 0x06 would do. I understand that 0x3C 0x06 would put the address pointer at location 06, but I'm assuming its different with 0x3D. I thought maybe they simply meant read 6 times??

    Edit: Wrote this before you posted the 2nd time. You're quick. Thanks.
  • Duane DegnDuane Degn Posts: 10,588
    edited 2014-08-20 14:17
    nato1080 wrote: »
    The part of the manual that is really confusing me is the part 5 of the operational example (the Loop part). It says send 0x3D 0x06 to read all 6 bytes.

    I don't understand that section either.

    To read from the compass I use this code (mostly Parallax demo code).
    PUB ReadCompass | localIndex
    
      ifnot readCompassFlag ' only set pointer if needed
        SetPointer(OUTPUT_X_MSB_REG)
      GetRaw                      ' Gather raw data from compass
    

    The pointer only needs to be set if some other register other than the data registers had been accessed prior to this read. After reading the six data registers, the pointer is automatically reset to register 3.

    "GetRaw" is the following method.
    PRI GetRaw | tempRaw[THREE_SPACE_SIZE]  ' Get raw data from continous output
      
      StartI2c
      Send(READ_DATA)
      tempRaw[INDEX_X] := ((receive(true) << 8) | receive(true))
      tempRaw[INDEX_Z] := ((receive(true) << 8) | receive(true))
      tempRaw[INDEX_Y] := ((receive(true) << 8) | receive(false)) 
     
      StopI2c
      repeat result from 0 to MAX_SPACE_INDEX
        raw[result] := ~~tempRaw[result]
    

    The last two lines changes the 16-bit values to 32-bit values (the Propeller generally uses 32-bit variables).
      READ_DATA      = $3D ' Requests Read operation    
    

    The read address uses a "1" in the lsb of the address.
  • nato1080nato1080 Posts: 8
    edited 2014-08-20 14:32
    When you say propeller are you referring to a servo? Either way I don't have one, but I wasn't exactly sure what you were referring to. If this thing keeps giving me problems I'll be tempted to go get on if it will help. Anyway I looked into the changes you suggested. The first line change the differences are 1. No self-test mode, 2. Use only one sample instead of averaging 8. This probably was a problem because I don't think I gave it enough time to sample 8 times before reading. The second line change is a good catch. That was me accidentally putting the wrong value in there. I re-ran it with those two things and here is what I got back:

    for X --> 0xFF (H) 0x083 (L) --> 0xFF83
    for Y --> 0x01 (H) 0x77 (L) --> 0x0177
    for Z --> 0x00 (H) 0xBA (L) --> 0x00BA

    Unfortunately, I don't think these are in valid range either. I also don't know why for X the lower bits had a leading 0, but I'm ignoring that for now...
  • nato1080nato1080 Posts: 8
    edited 2014-08-20 14:38
    I think we are doing very similar things. You send 0x3D. You then initiate 6 reads Acking all but the last. I'm assuming that's with the Boolean value in receive does. If you don't mind sharing, what does your configuration register setup code look like?
  • Duane DegnDuane Degn Posts: 10,588
    edited 2014-08-20 14:39
    nato1080 wrote: »
    When you say propeller are you referring to a servo? Either way I don't have one, but I wasn't exactly sure what you were referring to. If this thing keeps giving me problems I'll be tempted to go get on if it will help. Anyway I looked into the changes you suggested. The first line change the differences are 1. No self-test mode, 2. Use only one sample instead of averaging 8. This probably was a problem because I don't think I gave it enough time to sample 8 times before reading. The second line change is a good catch. That was me accidentally putting the wrong value in there. I re-ran it with those two things and here is what I got back:

    for X --> 0xFF (H) 0x083 (L) --> 0xFF83
    for Y --> 0x01 (H) 0x77 (L) --> 0x0177
    for Z --> 0x00 (H) 0xBA (L) --> 0x00BA

    Unfortunately, I don't think these are in valid range either. I also don't know why for X the lower bits had a leading 0, but I'm ignoring that for now...

    I think those numbers look promising. Enter them into Windows calculator in "programmer" mode with the "Word" sized checked,

    I get
    x = -125
    y = 375
    z = 186

    This has a magnitude of 437 which is a little less than what I get here in Idaho with the gain set to 1.
  • Duane DegnDuane Degn Posts: 10,588
    edited 2014-08-20 14:44
    nato1080 wrote: »
    If you don't mind sharing, what does your configuration register setup code look like?

    There's a link to the code in post #3. You should be able to view the code as a text file (I think).

    My setup code is kind of complicated. It automatically adjusts the gain based on the magnitude of the field.
  • Duane DegnDuane Degn Posts: 10,588
    edited 2014-08-20 15:12
    nato1080 wrote: »
    When you say propeller are you referring to a servo? Either way I don't have one,

    The Propeller is (IMO) the world's funnest microcontroller. I recently linked to my "Ode of a Propeller Fan Boy" post I made to Let's Make Robots. I explain some of the reasons I like the Prop there.

    I have a playlist of YouTube called "Fun with Spin" but it could also have been called "Fun with Propeller Chips". I also keep a list of my various projects in post #2 of my index (see signature). Post #3 of my index has links to Propeller tutorials.
    nato1080 wrote: »
    I think we are doing very similar things. You send 0x3D. You then initiate 6 reads Acking all but the last. I'm assuming that's with the Boolean value in receive does.

    I agree. I think we are doing just about the same thing.
  • nato1080nato1080 Posts: 8
    edited 2014-08-20 15:14
    I pulled this from the pdf for the compass under the data x register - "The value stored in these two registers is a 16-bit value in 2’s complement form, whose range is 0xF800 to 0x07FF."

    I was assuming the value that was returned by the compass had to be between those two values?? Is this not the case? In all honestly I haven't looked super close at what to do with the values once I get them because they never were right... If those are fine then I'll have to go back and reread those sections.


    I just realized my mistake in the above, but I'm going to leave it in case anybody else makes the same mistake. I was not thinking in terms of 2's complement. I was just turning it around in my head thinking that the value had to be between 0x07FF and 0xF800. Doing that my X is too big and my Y & Z are too small. However, the above values are all within the range when considering it is in 2's complement. 0xF800 = -2048 and 0x07FF = 2047. Ooops - Inexperience strikes again.

    Thanks Duane for helping me out with this. I may come back with more questions in the future.
  • Duane DegnDuane Degn Posts: 10,588
    edited 2014-08-20 15:43
    nato1080 wrote: »
    Thanks Duane for helping me out with this. I may come back with more questions in the future.

    You're welcome. There's a surprising about of stuff to keep straight with this sort of stuff.

    You know you'll need a bit of trig to get a heading from the vector right?
  • nato1080nato1080 Posts: 8
    edited 2014-08-21 09:18
    I haven't gotten a chance to investigate completely, but my assumption was something like this.


    I'm assuming that my X value is the magnitude of my vector along the East-West Axis and my Y value is the magnitude of my vector along the North-South axis. So it's just vector math from there. It will look different for each quadrant, but assuming a pos X and a pos Y (Quad 1) my heading from North would be tan-1(X/Y). I don't know exactly how Z fits into all of this yet.


    FYI i'll be amazed if my picture looks decent once I hit Post... Deleted it- it didn't work very well.
  • nato1080nato1080 Posts: 8
    edited 2014-08-21 12:59
    So after messing with this I'm a little confused. These assumptions are correct right?

    Due North - Large (+) Y; Low (+/-) X

    Due East - Large (+) X; Low (+/-) Y

    Due South - Large (-) Y; Low(+/-) X

    Due West - Large (-) X; Low (+/-) Y

    Right now I feel like if I point the hole in the circuit board due North (approx.) I get exactly almost identical values if I rotate it 180 deg. The same is true if I point the hole due East and then rotate 180 deg. (Same Values)
  • Duane DegnDuane Degn Posts: 10,588
    edited 2014-08-21 13:17
    nato1080 wrote: »
    Right now I feel like if I point the hole in the circuit board due North (approx.) I get exactly almost identical values if I rotate it 180 deg. The same is true if I point the hole due East and then rotate 180 deg. (Same Values)

    That doesn't sound right.

    You want to make sure you don't have the bias on (low bits of register 0). The internal calibration bias swamps the sensors and it can't pick up the Earth's magnetic field.

    Other electronics can interfere with the compass. I attempted to keep the sensor stationary using gimbals made from servos but the servos produced too much interference. Here's a video of my attempt. The gizmo works a lot better with the sensor on a four inch rod but I haven't made a video of my improved contraption yet.
  • nato1080nato1080 Posts: 8
    edited 2014-08-21 14:41
    Yea, it's hard to say what effect (or if) the electronics I have sitting around have on the compass. I'm pretty sure I don't have it set in self-test mode, which is what I think you are referring to though I will double check. I need to get a setup with AA batteries running my stuff so I can move away from the computer and other electronics.
Sign In or Register to comment.