Why would you want to run a perfectly good digital compass like the HMC6352 in magnometer mode instead of letting it calculate heading for you? Because to apply tilt compensation, for example, you need the actual X and Y mag readings.
To get the mag readings, you set the output mode byte in RAM (a RAM write takes 70 microseconds) to specify which axis to measure and then do a normal read (a read takes 6 milliseconds). Output mode will revert to heading on power down, so you're not making any permanent changes to the chip.
The output comes in two's complement form for negative values, which means no complex formulas, just drop it into an integer like you would the heading output.
The downside is that you need to switch back and forth between reading the X axis and the Y axis, so your sample rate is half what it was before, and you need to do the trig yourself, which requires the math.h library and yet more processing delays.
Sample-rate-wise, you'd be better off with a two axis magnometer you could sample directly, but if all you have is the HMC6352, this might help!
Here's some example code based on the well-known code for reading heading from the HMC6352, but packaged as a function:
#include <Wire.h>
#include <math.h>
int magReading = 0;
int XmagValue = 0;
int YmagValue = 0;
int Azimuth = 0;
void setup()
{
Serial.begin(19200);
Wire.begin();
}
void magRead(int outputMode)
{
// int HMC6352Address = 0x42;
// Shift the device's documented slave address (0x42) 1 bit right
// This compensates for how the TWI library only wants the
// 7 most significant bits (with the high bit padded with 0)
// slaveAddress = HMC6352Address >> 1; // This results in 0x21 as the address to pass to TWI
int slaveAddress = 0x21; // This is calculated from HMC6352's address, see comments above
int ramDelay = 100; // us, delay between a RAM write command and its effect, at least 70us
int getDelay = 10; // ms, delay between a get data command and its effect, at least 6ms
byte magData[2];
int i;
switch (outputMode)
{
case 0:
Wire.beginTransmission(slaveAddress);
Wire.send(0x47); // Write to RAM command
Wire.send(0x4E); // Output Mode control byte address
Wire.send(0x00); // 0x00 for Heading mode
Wire.endTransmission();
break;
case 1:
Wire.beginTransmission(slaveAddress);
Wire.send(0x47); // Write to RAM command
Wire.send(0x4E); // Output Mode control byte address
Wire.send(0x03); // 0x03 for Magnetometer X mode
Wire.endTransmission();
break;
case 2:
Wire.beginTransmission(slaveAddress);
Wire.send(0x47); // Write to RAM command
Wire.send(0x4E); // Output Mode control byte address
Wire.send(0x04); // 0x04 for Magnetometer Y mode
Wire.endTransmission();
break;
default:
Wire.beginTransmission(slaveAddress);
Wire.send(0x47); // Write to RAM command
Wire.send(0x4E); // Output Mode control byte address
Wire.send(0x00); // default to Heading mode
Wire.endTransmission();
}
delayMicroseconds(ramDelay); // RAM write needs 70 microseconds to respond
Wire.beginTransmission(slaveAddress);
Wire.send("A"); // The "Get Data" command
Wire.endTransmission();
delay(getDelay); // Get Data needs 6 milliseconds to respond
Wire.requestFrom(slaveAddress, 2); // Request the 2 byte data (MSB comes first)
i = 0;
while(Wire.available() && i < 2)
{
magData[i] = Wire.receive();
i++;
}
magReading = magData[0]*256 + magData[1];
}
void loop()
{
magRead(0);
Azimuth = magReading;
magRead(1);
XmagValue = magReading;
magRead(2);
YmagValue = magReading;
Serial.print("Mag X: ");
Serial.print(XmagValue);
Serial.print(" Mag Y: ");
Serial.print(YmagValue);
Serial.print(" Azimuth: ");
Serial.print(-int(atan2(YmagValue,XmagValue)*57));
Serial.print(" degrees ");
Serial.print(int(Azimuth/10));
Serial.println(" degrees ");
delay(500);
}
You'll see that you get the same result with the heading mode and with the trig applied to the mag readings.