3-Axis HMC5883L Compass Module Problems
nato1080
Posts: 8
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.
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
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.
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.
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.
I don't understand that section either.
To read from the compass I use this code (mostly Parallax demo code).
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.
The last two lines changes the 16-bit values to 32-bit values (the Propeller generally uses 32-bit variables).
The read address uses a "1" in the lsb of the address.
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.
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.
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.
I agree. I think we are doing just about the same thing.
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.
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?
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.
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)
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.