Let's Make Robots!

HMC6352 Compass Module in Continuous Read Mode

I have a need (desire?) to use compass heading as part of my PID loop to keep my rover heading true.  Using my well established trial and error coding technique, I was unable to get a sufficient sample rate from the HMC6352 Compass module using code I pilfered.. er... borrowed (I plan to give it back!) from the Interwebs.

Most people seem to use the HMC6352 Compass module in "Standby" Mode, or at least every snippit of code I've seen through Google searches does...

The problem with Standby or Query mode, is that it requires a 6ms settling time for each reading, and if you are continuously polling it anyway, you really save little energy.  By setting the HMC6352 into "Continuous Read" mode, you can get the most recent heading immediately. At least up to 20hz.

 

From the Datasheet:
Operational Modes
The HMC6352 has three operational modes plus the ability to enter/exit the non-operational (sleep) mode by
command. Sleep mode sends the internal microprocessor into clock shutdown to save power, and can be brought back by the “W” command (wake). The “S” command returns the processor to sleep mode. The three operational modes are defined by two bits in the internal HMC6352 Operation Mode register. If the master device sends the “L” command, the current operational mode control byte in the RAM register is loaded into the internal EEPROM register and becomes the default operational mode on the next power-up. The application environment of the HMC6352 will dictate the most suitable operational mode.

 

Standby Mode:
(Operational Mode=0) This is the factory default mode. The HMC6352 waits for master device commands or change in operational mode. Receiving an “A” command (get data) will make the HMC6352 perform a
measurement of sensors (magnetometers), compute the compensated magnetometer and heading data, and wait for the next read or command. No new measurements are done until another “A” command is sent. This mode is useful to get data on demand or at random intervals as long as the application can withstand the time delay in getting the data.
Query Mode:
(Operational Mode=1) In this mode the internal processor waits for “A” commands (get data), makes the
measurements and computations, and waits for the next read command to output the data. After each read command, the HMC6352 automatically performs another get data routine and updates the data registers. This mode is designed to get data on demand without repeating “A” commands, and with the master device controlling the timing and data throughput. The tradeoff in this mode is the previous query latency for the advantage of an immediate read of data. The above two modes are the most power conserving readout modes.
Continuous Mode:
(Operational Mode=2) The HMC6352 performs continuous sensor measurements and data computations at selectable rates of 1Hz, 5Hz, 10Hz, or 20Hz, and updates the output data bytes. Subsequent “A” commands are un-necessary unless re-synchronization to the command is desired. Data reads automatically get the most
recent updates. This mode is useful for data demanding applications. The continuous mode measurement rate is selected by two bits in the operational mode selection byte, along with the mode selection and the periodic Set/Reset bit. The periodic Set/Reset function performs a re-alignment of the sensors magnetic domains in case of sensor perming (magnetic upset event), operating temperature shifts, and normal thermal agitation of the domains. Exposure of the HMC6352 to magnetic fields above 20 gauss (disturbing field threshold) leads to possible measurement inaccuracy or “stuck” sensor readings until the set/reset function is performed. With the periodic Set/Reset bit set, the set/reset function occurs every few minutes.
So, what all this means is: It is possible to get data from the HMC6352 at up to a 20hz rate without incurring a 6ms delay for stabilizing... And you get to remove a few lines of setup and read for each loop as well!

-------------------------------------------------------------------------------------------------------------------------------------------------------

Here is a HMC6352 heading read morph of most searches I've found for the Arduino:


float Get_Compass()
{
    Wire.beginTransmission(HMC6352SlaveAddress);
    Wire.write('A');                           // The "Get Data" command
    Wire.endTransmission();

                                 //time delays required by HMC6352 upon receipt of the command
                                 //Get Data. Compensate and Calculate New Heading : 6ms
   delay(6);

  Wire.requestFrom(HMC6352SlaveAddress, 2);  //get the two data bytes, MSB and LSB

                                 //"The heading output data will be the value in tenths of degrees
                                //from zero to 3599 and provided in binary format over the two bytes."
  byte MSB = Wire.read();
  byte LSB = Wire.read();

  float HeadingSum = (MSB << 8) + LSB; //(MSB / LSB sum)
  float HeadingInt = HeadingSum / 10;
  return HeadingInt;
}

 

-------------------------------------------------------------------------------------------------------------------------------------------------------

Here is the new and improved HMC6352 Continuous heading read  for the Arduino:
   There is a little bit of setup, but this is more then compensated for by the lines removed from the actual READ process.

void setup()

{

  Wire.beginTransmission(HMC6352SlaveAddress);
   Wire.write('G');                               // Command - Write to RAM
   Wire.write(0x74);                            // Operational mode control byte register address 74(hex).
   Wire.write(0x72);                           // 20 Hz - Continuous mode.
   Wire.endTransmission();
}

 

float Get_Compass()
{
  Wire.requestFrom(HMC6352SlaveAddress, 2);  //get the two data bytes, MSB and LSB

                                 //"The heading output data will be the value in tenths of degrees
                                //from zero to 3599 and provided in binary format over the two bytes."
  byte MSB = Wire.read();
  byte LSB = Wire.read();

  float HeadingSum = (MSB << 8) + LSB; //(MSB / LSB sum)
  float HeadingInt = HeadingSum / 10;
  return HeadingInt;
}

 

 

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.

Sir, you did a lot of my work for me :)

I've not told you lately but you are an amazing individual.  Your solutions are like ones I wish my brain made. :(

We're working on similar projects, and have similar goals.  You've accomplished so much with the ATtiny's Im jealous!  LOL

I'm just not sure why there is so little actual code for this Compass chip.  Seems everyone is satisfied to simply get a heading out of it periodically, and that's it.

Here's another little snippit for you.   I had trouble with the compass resolution when it was near the dc motors so I wrote a simple calibration routine that would put it into calibration mode, and spin it for several seconds to allow the compass to take gauss readings.


void Calibrate_Compass()

    //Enter User Calibration Mode by sending an ASCII 'C'
    Wire.beginTransmission(HMC6352SlaveAddress);
    Wire.write('C');              // The "C" command  starts calibration
    Wire.endTransmission();
   
    lcount = 0; rcount = 0;
    Serial.print("CALIBRATING Compass, ");
   
    digitalWrite(lmdirpin, forward);  //Establishes direction of Channel A
    digitalWrite(lmbrkpin, LOW);   //Disengage the Brake for Channel A
    analogWrite(lmpwmpin, 50);    //Spins the motor on Channel A at half speed
 

    digitalWrite(rmdirpin, backward); //Establishes direction of Channel B
    digitalWrite(rmbrkpin, LOW);   //Disengage the Brake for Channel B
    analogWrite(rmpwmpin, 50);   //Spins the motor on Channel B at half speed

     //   you could put a delay(20000); in here but I'd rather use odometry.
     while (lcount < 1200) {   // Make two full revolutions
           Serial.print("Left , Right:  "); Serial.print(lcount); Serial.print(", ");    Serial.println(rcount); 
     };                 

    //End User Calibration Mode by sending an ASCII 'E'
    Wire.beginTransmission(HMC6352SlaveAddress);
    Wire.write('E');              // The "E" command exits calibration
    Wire.endTransmission();

    analogWrite(lmpwmpin, 0);    //Spins the motor on Channel A at speed 0
    analogWrite(rmpwmpin, 0);   //Spins the motor on Channel B at speed 0
    lcount = 0; rcount = 0;

}