How to make a digital compass using HMC6352

Electronics & Electrical Engineering Topics
Post Reply
User avatar
Neo
Site Admin
Site Admin
Posts: 2642
Joined: Wed Jul 15, 2009 2:07 am
Location: Colombo

How to make a digital compass using HMC6352

Post by Neo » Sat Aug 06, 2011 6:12 pm

The Honeywell HMC6352 is a fully integrated compass module that combines 2-axis magneto-resistive sensors with the required analogue and digital support circuits, and algorithms for heading computation. By combining the sensor elements, processing electronics, and firmware in to a 6.5mm by 6.5mm by 1.5mm LCC package, Honeywell offers a complete, ready to use electronic compass. This provides a simple solution to integrate compasses into your projects.

Features:
  • Simple I2C interface
  • 2.7 to 5.2V supply range
  • 1 to 20Hz selectable update rate
  • True drop-in solution
  • 0.5 degree heading resolution
  • 1 degree repeatability
  • Supply current : 1mA @ 3V
Description:
The HMC6352 communicates via a two-wire I2C bus system as a slave device. The HMC6352 uses a layered protocol with the interface protocol defined by the I2C bus specification. The data rate is the standard-mode 100kbps rate as defined in the I2C Bus Specification 2.1. The bus bit format is an 8-bit Data/Address send and a 1-bit acknowledge bit. The format of the data bytes (payload) shall be case sensitive ASCII characters or binary data to the HMC6352 slave, and binary data returned. Negative binary values will be in two’s complement form. The default (factory) HMC6352 7-bit slave address is 42(hex) for write operations, or 43(hex) for read operations.

The SCL and SDA lines in this bus specification can be connected to a host of devices. The bus can be a single master to multiple slaves, or it can be a multiple master configuration. All data transfers are initiated by the master device which is responsible for generating the clock signal, and the data transfers are 8 bit long. All devices are addressed by I2C’s unique 7 bit address. After each 8-bit transfer, the master device generates a 9th clock pulse, and releases the SDA line.

sparkfun has a small module implemented with this IC.
http://www.sparkfun.com/products/7915

Datasheet:
http://www.ssec.honeywell.com/magnetic/ ... MC6352.pdf
http://www.sparkfun.com/datasheets/Comp ... MC6352.pdf

Here is nice C code to work with his chip.

HMC6352.h

Code: Select all

/**
 * @author Aaron Berk
 * 
 * @section LICENSE
 *
 * Copyright (c) 2010 ARM Limited
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 * @section DESCRIPTION
 *
 * Honeywell HMC6352 digital compass.
 *
 * Datasheet:
 *
 * http://www.ssec.honeywell.com/magnetic/datasheets/HMC6352.pdf
 */

#ifndef HMC6352_H
#define HMC6352_H

/**
 * Includes
 */
#include "mbed.h"

/**
 * Defines
 */
#define HMC6352_I2C_ADDRESS	0x21 //7-bit address. 0x43 write, 0x42 read.

//Commands.
#define HMC6352_EEPROM_WRITE 0x77
#define HMC6352_EEPROM_READ	0x72
#define HMC6352_RAM_WRITE	0x47
#define HMC6352_RAM_READ	 0x67
#define HMC6352_ENTER_SLEEP	0x53
#define HMC6352_EXIT_SLEEP	 0x57
#define HMC6352_SET_RESET	0x4F
#define HMC6352_ENTER_CALIB	0x43
#define HMC6352_EXIT_CALIB	 0x45
#define HMC6352_SAVE_OPMODE	0x4C
#define HMC6352_GET_DATA	 0x41

//EEPROM locations.
#define HMC6352_SLAVE_ADDR	 0x00
#define HMC6352_MX_OFF_MSB	 0x01
#define HMC6352_MX_OFF_LSB	 0x02
#define HMC6352_MY_OFF_MSB	 0x03
#define HMC6352_MY_OFF_LSB	 0x04
#define HMC6352_TIME_DELAY	 0x05
#define HMC6352_SUMMED		 0x06
#define HMC6352_SOFT_VER	 0x07
#define HMC6352_OPMODE		 0x08

//RAM registers.
#define HMC6352_RAM_OPMODE	 0x74
#define HMC6352_RAM_OUTPUT	 0x4E

#define HMC6352_MX_OFFSET	0x00
#define HMC6352_MY_OFFSET	0x01

#define HMC6352_HEADING_MODE 0x00
#define HMC6352_RAWMAGX_MODE 0x01
#define HMC6352_RAWMAGY_MODE 0x02
#define HMC6352_MAGX_MODE	0x03
#define HMC6352_MAGY_MODE	0x04

//Operational mode register masks.
#define HMC6352_CM_MR_1HZ	0x00
#define HMC6352_CM_MR_5HZ	0x20
#define HMC6352_CM_MR_10HZ	 0x40
#define HMC6352_CM_MR_20HZ	 0x60

#define HMC6352_PERIODIC_SR	0x10

#define HMC6352_STANDBY		0x00
#define HMC6352_QUERY		0x01
#define HMC6352_CONTINUOUS	 0x02

/**
 * Honeywell HMC6352 digital compass.
 */
class HMC6352 {

public:

	/**
	 * Constructor.
	 *
	 * @param sda mbed pin to use for SDA line of I2C interface.
	 * @param scl mbed pin to use for SCL line of I2C interface.
	 */
	HMC6352(PinName sda, PinName scl);

	/**
	 * Sample the device and return the result.
	 *
	 * @return In heading output mode, the current heading as a number between
	 *		 0-3599, representing 0-359.9 degrees.
	 *		 In raw magnetometer X output mode, the raw output of the X-axis
	 *		 magnetometer.
	 *		 In raw magnetometer Y mode, the raw output of the Y-axis
	 *		 magnetometer.
	 *		 In magnetometer X mode, the corrected output of the X-axis
	 *		 magnetometer.
	 *		 In magnetometer Y mode, the corrected output of the Y-axis
	 *		 magnetometer.
	 */
	int sample(void);

	/**
	 * Enter into or exit from sleep mode.
	 *
	 * @param enterOrExit 0x57 -> Exit sleep mode
	 *					0x53 -> Enter
	 */
	void setSleepMode(int enterOrExit);

	/**
	 * Update bridge offsets.
	 *
	 * Performs a set/reset immediately.
	 */
	void setReset(void);

	/**
	 * Enter into or exit from calibration mode.
	 *
	 * @param enterOrExit 0x45 -> Exit
	 *					0x43 -> Enter
	 */
	void setCalibrationMode(int enterOrExit);

	/**
	 * Save the current operation mode byte to EEPROM.
	 */
	void saveOpMode(void);

	/**
	 * Read the memory location on the device which contains the slave address.
	 *
	 * @return The slave address of the device.
	 */
	int getSlaveAddress(void);

	/**
	 * Read the current offset for X or Y axis magnetometer.
	 *
	 * @param axis 0x00 -> X-axis
	 *			 0x01 -> Y-axis
	 * @return The current offset for the axis as a 16-bit number.
	 */
	int getOffset(int axis);

	/**
	 * Set the offset for X or Y axis magnetometer.
	 *
	 * @param axis 0x00 -> X-axis
	 *			 0x01 -> Y-axis
	 * @param offset The offset to set for the axis (set to a 16-bit number).
	 */
	void setOffset(int axis, int offset);

	/**
	 * Get the current time delay.
	 *
	 * @return The time delay in milliseconds.
	 */
	int getTimeDelay(void);

	/**
	 * Set the time delay of the device.
	 *
	 * @param delay The time delay to set in milliseconds (between 0-255);
	 */
	void setTimeDelay(int delay);

	/**
	 * Get the number of measurements being summed each sample.
	 *
	 * @return The number of summed measurement.
	 */
	int getSumNumber(void);

	/**
	 * Set the number of measurements being summed each sample.
	 *
	 * @param sum The number of measurements to be summed (between 0-16);
	 */
	void setSumNumber(int sum);

	/**
	 * Get the software version on the device.
	 *
	 * @return The software version number.
	 */
	int getSoftwareVersion(void);

	/**
	 * Get the current operation mode.
	 *
	 * @return 0x00 -> Standby mode
	 *		 0x01 -> Query mode
	 *		 0x02 -> Continuous mode
	 */
	int getOpMode(void);

	/**
	 * Set the operation mode.
	 *
	 * @param mode 0x00 -> Standby mode
	 *			 0x01 -> Query mode
	 *			 0x02 -> Continuous mode
	 * @param periodicSetReset 0x00 -> No periodic set/reset
	 *						 0x01 -> Periodic set/reset
	 * @measurementRate Measurement rate in Hz for continuous rate.
	 *					Possible rates: {1, 5, 10, 20}Hz.
	 */
	void setOpMode(int mode, int periodicSetReset, int measurementRate = 0);

	/**
	 * Get the current output mode of the device.
	 *
	 * @return The current output mode.
	 */
	int getOutputMode(void);

	/**
	 * Set the output mode of the device.
	 *
	 * @param mode 0x00 -> Heading mode
	 *			 0x01 -> Raw magnetometer X mode
	 *			 0x02 -> Raw magnetometer Y mode
	 *			 0x03 -> Magnetometer X mode
	 *			 0x04 -> Magnetometer Y mode
	 */
	void setOutputMode(int mode);

private:

	I2C* i2c_;
	int	operationMode_;

	/**
	 * Write to EEPROM or RAM on the device.
	 *
	 * @param EepromOrRam 0x77 -> Writing to EEPROM
	 *					0x47 -> Writing to RAM
	 * @param address Address to write to.
	 * @param data Data to write.
	 */
	void write(int EepromOrRam, int address, int data);

	/**
	 * Read EEPROM or RAM on the device.
	 *
	 * @param EepromOrRam 0x72 -> Reading from EEPROM
	 *					0x67 -> Reading from RAM
	 * @param address Address to read from.
	 * @return The contents of the memory address.
	 */
	int read(int EepromOrRam, int address);

};

#endif /* HMC6352_H */
HMC6352.cpp

Code: Select all

/**
 * @author Aaron Berk
 * 
 * @section LICENSE
 *
 * Copyright (c) 2010 ARM Limited
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 * @section DESCRIPTION
 *
 * Honeywell HMC6352 digital compass.
 *
 * Datasheet:
 *
 * http://www.ssec.honeywell.com/magnetic/datasheets/HMC6352.pdf
 */

/**
 * Includes
 */
#include "HMC6352.h"

HMC6352::HMC6352(PinName sda, PinName scl) {

	i2c_ = new I2C(sda, scl);
	//100KHz, as specified by the datasheet.
	i2c_->frequency(100000);

	operationMode_ = getOpMode();

}

int HMC6352::sample(void) {

	char tx[1];
	char rx[2];

	if (operationMode_ == HMC6352_STANDBY || operationMode_ == HMC6352_QUERY) {
		tx[0] = HMC6352_GET_DATA;

		i2c_->write((HMC6352_I2C_ADDRESS << 1) & 0xFE, tx, 1);
		wait_ms(1);
	}

	i2c_->read((HMC6352_I2C_ADDRESS << 1) | 0x01, rx, 2);
	wait_ms(1);

	return (((int)rx[0] << 8) | (int)rx[1]);

}

void HMC6352::setSleepMode(int exitOrEnter) {

	char tx[1];

	tx[0] = exitOrEnter;

	i2c_->write((HMC6352_I2C_ADDRESS << 1) & 0xFE, tx, 1);
	wait_ms(1);

}

void HMC6352::setReset(void) {

	char tx[1];

	tx[0] = HMC6352_SET_RESET;

	i2c_->write((HMC6352_I2C_ADDRESS << 1) & 0xFE, tx, 1);
	wait_ms(7);

}

void HMC6352::setCalibrationMode(int exitOrEnter) {

	char tx[1];
	int delay = 0;

	tx[0] = exitOrEnter;

	if (exitOrEnter == HMC6352_EXIT_CALIB) {
		delay = 15;
	} else if (exitOrEnter == HMC6352_ENTER_CALIB) {
		delay = 1;
	}

	i2c_->write((HMC6352_I2C_ADDRESS << 1) & 0xFE, tx, 1);
	wait_ms(delay);

}

void HMC6352::saveOpMode(void) {

	char tx[1];

	tx[0] = HMC6352_SAVE_OPMODE;

	i2c_->write((HMC6352_I2C_ADDRESS << 1) & 0xFE, tx, 1);
	wait_ms(1);

}

int HMC6352::getSlaveAddress(void) {

	return read(HMC6352_EEPROM_READ, HMC6352_SLAVE_ADDR);

}

int HMC6352::getOffset(int axis) {

	char rx[2] = {0x00, 0x00};

	if (axis == HMC6352_MX_OFFSET) {

		rx[0] = read(HMC6352_EEPROM_READ, HMC6352_MX_OFF_MSB);
		rx[1] = read(HMC6352_EEPROM_READ, HMC6352_MX_OFF_LSB);

	} else if (axis == HMC6352_MY_OFFSET) {

		rx[0] = read(HMC6352_EEPROM_READ, HMC6352_MY_OFF_MSB);
		rx[1] = read(HMC6352_EEPROM_READ, HMC6352_MY_OFF_LSB);

	}

	return ((rx[0] << 8) | (rx[1]));

}

void HMC6352::setOffset(int axis, int offset) {

	char tx[2] = {0x00, 0x00};

	tx[0] = (offset & 0x0000FF00) >> 8;
	tx[1] = (offset & 0x000000FF);

	if (axis == HMC6352_MX_OFFSET) {

		write(HMC6352_EEPROM_WRITE, HMC6352_MX_OFF_MSB, tx[0]);
		write(HMC6352_EEPROM_WRITE, HMC6352_MX_OFF_MSB, tx[1]);

	} else if (axis == HMC6352_MY_OFFSET) {

		write(HMC6352_EEPROM_WRITE, HMC6352_MY_OFF_MSB, tx[0]);
		write(HMC6352_EEPROM_WRITE, HMC6352_MY_OFF_MSB, tx[1]);

	}

}

int HMC6352::getTimeDelay(void) {

	return read(HMC6352_EEPROM_READ, HMC6352_TIME_DELAY);

}

void HMC6352::setTimeDelay(int delay) {

	write(HMC6352_EEPROM_WRITE, HMC6352_TIME_DELAY, delay);

}

int HMC6352::getSumNumber(void) {

	return read(HMC6352_EEPROM_READ, HMC6352_SUMMED);

}

void HMC6352::setSumNumber(int sum) {

	write(HMC6352_EEPROM_WRITE, HMC6352_SUMMED, sum);

}

int HMC6352::getSoftwareVersion(void) {

	return read(HMC6352_EEPROM_READ, HMC6352_SOFT_VER);

}

int HMC6352::getOpMode(void) {

	int response = 0;

	response = read(HMC6352_RAM_READ, HMC6352_RAM_OPMODE);

	return (response & 0x00000003);

}

void HMC6352::setOpMode(int mode, int periodicSetReset, int measurementRate) {

	char opModeByte = mode;

	if (periodicSetReset == 1) {
		opModeByte |= HMC6352_PERIODIC_SR;
	}

	if (measurementRate == 5) {
		opModeByte |= HMC6352_CM_MR_5HZ;
	} else if (measurementRate == 10) {
		opModeByte |= HMC6352_CM_MR_10HZ;
	} else if (measurementRate == 20) {
		opModeByte |= HMC6352_CM_MR_20HZ;
	}

	write(HMC6352_RAM_WRITE, HMC6352_RAM_OPMODE, opModeByte);
	write(HMC6352_EEPROM_WRITE, HMC6352_OPMODE, opModeByte);

	operationMode_ = mode;

}

int HMC6352::getOutputMode(void) {

	return read(HMC6352_RAM_READ, HMC6352_RAM_OUTPUT);

}

void HMC6352::setOutputMode(int mode) {

	write(HMC6352_RAM_WRITE, HMC6352_RAM_OUTPUT, mode);

}

void HMC6352::write(int EepromOrRam, int address, int data) {

	char tx[3];

	tx[0] = EepromOrRam;
	tx[1] = address;
	tx[2] = data;

	i2c_->write((HMC6352_I2C_ADDRESS << 1) & 0xFE, tx, 3);
	wait_ms(1);

}

int HMC6352::read(int EepromOrRam, int address) {

	char tx[2];
	char rx[1];

	tx[0] = EepromOrRam;
	tx[1] = address;

	i2c_->write((HMC6352_I2C_ADDRESS << 1) & 0xFE, tx, 2);
	wait_ms(1);
	i2c_->read((HMC6352_I2C_ADDRESS << 1) | 0x01, rx, 1);
	wait_ms(1);

	return (rx[0]);
}

Public Member Functions:
HMC6352 (PinName sda, PinName scl)
Constructor

int sample (void)
Sample the device and return the result

void setSleepMode (int enterOrExit)
Enter into or exit from sleep mode

void setReset (void)
Update bridge offsets

void setCalibrationMode (int enterOrExit)
Enter into or exit from calibration mode

void saveOpMode (void)
Save the current operation mode byte to EEPROM

int getSlaveAddress (void)
Read the memory location on the device which contains the slave address

int getOffset (int axis)
Read the current offset for X or Y axis magnetometer

void setOffset (int axis, int offset)
Set the offset for X or Y axis magnetometer

int getTimeDelay (void)
Get the current time delay

void setTimeDelay (int delay)
Set the time delay of the device

int getSumNumber (void)
Get the number of measurements being summed each sample

void setSumNumber (int sum)
Set the number of measurements being summed each sample

int getSoftwareVersion (void)
Get the software version on the device

int getOpMode (void)
Get the current operation mode

void setOpMode (int mode, int periodicSetReset, int measurementRate=0)
Set the operation mode

int getOutputMode (void)
Get the current output mode of the device

void setOutputMode (int mode)
Set the output mode of the device
User avatar
Neo
Site Admin
Site Admin
Posts: 2642
Joined: Wed Jul 15, 2009 2:07 am
Location: Colombo

Re: How to make a digital compass using HMC6352

Post by Neo » Sat Aug 06, 2011 7:00 pm

Another code:

Code: Select all

/* Program to interface a micro with a hmc6352*/
#include<avr/io.h>
#include<util/delay.h>

#include<util/twi.h>


void Wait_TWI_int(void)
{
	while (!(TWCR & (1<<TWINT)));
}


void twi_init()
{
	TWBR=0x20;
	PORTB=0x01;
	TWSR=0b00000000;
	PORTA=TWSR;
	TWCR=(TWCR & 0b00000100);
	TWCR=(TWCR | 0b00000100);
	 
}

void twi_start()
{
	TWCR=(TWCR & 0b01100100);
	TWCR=(TWCR | 0b11100100);
	Wait_TWI_int();
	PORTB=0x02;
	PORTA=TWSR;
	//if (TWSR==0x08)
	//	PORTA=PORTA | 0x04;
	//else
	//	PORTA=PORTA | 0b00010000;
}

void twi_SLAR()
{
	TWDR=0x43;
	TWCR=TWCR & 0b01000100;
	TWCR=TWCR | 0b11000100;
	Wait_TWI_int();
	PORTB=0x03;
	PORTA=TWSR;
	//if (TWSR==0x40)
	//	PORTA=PORTA | 0b00001000;
	//if (TWSR==0x48)
	//	PORTA=PORTA | 0b00000100;
	 
}
	 
void twi_SLAW()
{
	TWDR=0x42;
	TWCR=TWCR & 0b01000000;
	TWCR=TWCR | 0b11000100;
	Wait_TWI_int();
	PORTB=0x01;
	PORTA=TWSR;
	 
	//if (TWSR==0x18)
	//	PORTA=PORTA | 0b00100000;
	//else
	//	PORTA=PORTA | 0b00010000;
		 
}
	 
void twi_transmit()
{
	TWDR='A';
	TWCR=TWCR & 0b01000000;
	TWCR=TWCR | 0b11000100;	 
	Wait_TWI_int();
	PORTB=0x03;
	PORTA=TWSR;
	//if (TWSR==0x28)
	//	PORTA=PORTA | 0b00000010;
	//if (TWSR==0x30)
	//	PORTA=PORTA | 0b00000001;
}
		 
int twi_read()
{
	int data;
	TWCR=TWCR & 0b00000000;
	TWCR=TWCR | 0b11000100;	 
	Wait_TWI_int();
	data=TWDR;
	PORTB=0x01;
	PORTA=TWSR;
	//if (TWSR==0x50)
	//	PORTA=PORTA | 0b00000010;
	//if (TWSR==0x58)
	//	PORTA=PORTA | 0b00000001;
	return(data);
}

void twi_stop()
{
	TWCR=TWCR & 0b01010000;
	TWCR=TWCR | 0b11010100;
	_delay_ms(10);
	Wait_TWI_int();
	PORTB=0x02;
	PORTA=TWSR;
}
		 
int main()
{
	DDRA=0xFF;
	DDRB=0xFF;
	twi_init();
	int dir1,dir2,dir;
	
	while (1)
	{	 
		_delay_ms(1000);
		twi_start();
		_delay_ms(1000);
		twi_SLAW();
		_delay_ms(1000);
		twi_transmit();
		_delay_ms(1000);
		twi_stop();
		_delay_ms(1000);
		twi_start();
		_delay_ms(1000);
		twi_SLAR();
		_delay_ms(1000);
		dir1=twi_read();
		_delay_ms(1000);
		dir2=twi_read();
		_delay_ms(1000);
		dir=dir2<<8 | dir1;
		//if(dir>0b0000000000010000) PORTA=0x01;
		//	else PORTA=0x01;
		twi_stop();
	}
}
Post Reply

Return to “Electronics & Electrical Engineering”