Running digital compass HMC6352 in Magnometer mode

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.

I didn't know my code was that well known! :wink:

It's a reference for us all! :slight_smile: