2017-09-13 02:58:18 -04:00
|
|
|
//
|
2013-09-30 11:00:55 -04:00
|
|
|
// FILE: hmc6352.cpp
|
|
|
|
// AUTHOR: Rob Tillaart
|
2013-09-30 11:04:25 -04:00
|
|
|
// VERSION: 0.1.03
|
2013-09-30 11:00:55 -04:00
|
|
|
// PURPOSE: HMC6352 library for Arduino
|
|
|
|
//
|
2017-09-13 02:58:18 -04:00
|
|
|
// HISTORY:
|
2013-09-30 11:00:55 -04:00
|
|
|
// 0.1.00 - 2011-04-07 initial version
|
2017-09-13 02:58:18 -04:00
|
|
|
// 0.1.01 - 2011-04-09 quite a complete redo
|
2013-09-30 11:03:06 -04:00
|
|
|
// 0.1.02 - 2011-04-12 added timing, fixed a bug
|
2013-09-30 11:04:25 -04:00
|
|
|
// 0.1.03 - 2011-04-13 fixed small things; added getHeading()
|
2017-09-13 02:58:18 -04:00
|
|
|
// 0.1.4 - 2017-09-13 minor refactor
|
|
|
|
//
|
2013-09-30 11:00:55 -04:00
|
|
|
// Released to the public domain
|
|
|
|
//
|
|
|
|
|
|
|
|
#include <hmc6352.h>
|
|
|
|
#include <Wire.h>
|
|
|
|
|
2017-09-13 03:18:55 -04:00
|
|
|
#if defined(ARDUINO) && ARDUINO >= 100
|
|
|
|
#define WIRE_WRITE Wire.write
|
|
|
|
#define WIRE_READ Wire.read
|
|
|
|
#else
|
|
|
|
#define WIRE_WRITE Wire.send
|
|
|
|
#define WIRE_READ Wire.receive
|
|
|
|
#endif
|
|
|
|
|
2013-09-30 11:00:55 -04:00
|
|
|
/* ERROR CODES ALL FUNCTIONS
|
2013-09-30 11:03:06 -04:00
|
|
|
//
|
|
|
|
// * twi_writeTo codes (== endTransmission commands)
|
2013-09-30 11:00:55 -04:00
|
|
|
// 0 .. OK
|
|
|
|
// -1 .. length to long for buffer
|
|
|
|
// -2 .. address send, NACK received
|
|
|
|
// -3 .. data send, NACK received
|
|
|
|
// -4 .. other twi error (lost bus arbitration, bus error, ..)
|
2013-09-30 11:03:06 -04:00
|
|
|
//
|
2017-09-13 02:58:18 -04:00
|
|
|
// * requestFrom
|
2013-09-30 11:03:06 -04:00
|
|
|
// -10 .. not enough values returned
|
|
|
|
//
|
|
|
|
// * function calls
|
|
|
|
// 0 .. OK
|
|
|
|
// -20 .. error param1
|
|
|
|
// -21 .. error param2
|
|
|
|
// -22 .. error param3
|
|
|
|
//
|
2013-09-30 11:00:55 -04:00
|
|
|
*/
|
|
|
|
|
|
|
|
hmc6352::hmc6352(uint8_t device)
|
|
|
|
{
|
2017-09-13 02:58:18 -04:00
|
|
|
Wire.begin();
|
|
|
|
_device = constrain(device, 0x10, 0xF6);
|
2013-09-30 11:00:55 -04:00
|
|
|
}
|
|
|
|
|
2013-09-30 11:04:25 -04:00
|
|
|
int hmc6352::getHeading()
|
|
|
|
{
|
2017-09-13 02:58:18 -04:00
|
|
|
int rv = askHeading();
|
|
|
|
if (rv != 0) return rv;
|
|
|
|
return readHeading();
|
2013-09-30 11:04:25 -04:00
|
|
|
}
|
|
|
|
|
2013-09-30 11:03:06 -04:00
|
|
|
// Ask the device to make a new reading
|
|
|
|
int hmc6352::askHeading()
|
2013-09-30 11:00:55 -04:00
|
|
|
{
|
2017-09-13 02:58:18 -04:00
|
|
|
int rv = cmd(HMC_GET_DATA);
|
|
|
|
if (rv != 0) return -rv; // problem with handshake
|
|
|
|
delay(6); // see datasheet, p8
|
|
|
|
return rv;
|
2013-09-30 11:00:55 -04:00
|
|
|
}
|
|
|
|
|
2013-09-30 11:03:06 -04:00
|
|
|
// read the last value from the
|
|
|
|
int hmc6352::readHeading()
|
2013-09-30 11:00:55 -04:00
|
|
|
{
|
2017-09-13 02:58:18 -04:00
|
|
|
int rv = Wire.requestFrom(_device, (uint8_t)2); // remove ambiguity
|
|
|
|
if (rv != 2) return -10;
|
2017-09-13 03:18:55 -04:00
|
|
|
rv = WIRE_READ() * 256; // MSB
|
|
|
|
rv += WIRE_READ(); // LSB
|
2017-09-13 02:58:18 -04:00
|
|
|
return rv;
|
2013-09-30 11:00:55 -04:00
|
|
|
}
|
|
|
|
|
2013-09-30 11:03:06 -04:00
|
|
|
// wake up from energy saving modus
|
2013-09-30 11:00:55 -04:00
|
|
|
int hmc6352::wakeUp()
|
|
|
|
{
|
2017-09-13 02:58:18 -04:00
|
|
|
int rv = cmd(HMC_WAKE);
|
|
|
|
delayMicroseconds(100);
|
|
|
|
return rv;
|
2013-09-30 11:00:55 -04:00
|
|
|
}
|
|
|
|
|
2013-09-30 11:03:06 -04:00
|
|
|
// go into energy saving modus
|
2013-09-30 11:00:55 -04:00
|
|
|
int hmc6352::sleep()
|
|
|
|
{
|
2017-09-13 02:58:18 -04:00
|
|
|
int rv = cmd(HMC_SLEEP);
|
|
|
|
delayMicroseconds(10);
|
|
|
|
return rv;
|
2013-09-30 11:00:55 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// values obtained from dump
|
|
|
|
int hmc6352::factoryReset()
|
|
|
|
{
|
2017-09-13 02:58:18 -04:00
|
|
|
writeRAM(0x74, 0x50); // is needed !!
|
|
|
|
writeCmd(HMC_WRITE_EEPROM, 0, 66);
|
|
|
|
writeCmd(HMC_WRITE_EEPROM, 1, 0);
|
|
|
|
writeCmd(HMC_WRITE_EEPROM, 2, 0);
|
|
|
|
writeCmd(HMC_WRITE_EEPROM, 3, 0);
|
|
|
|
writeCmd(HMC_WRITE_EEPROM, 4, 0);
|
|
|
|
writeCmd(HMC_WRITE_EEPROM, 5, 1);
|
|
|
|
writeCmd(HMC_WRITE_EEPROM, 6, 4);
|
|
|
|
writeCmd(HMC_WRITE_EEPROM, 7, 6);
|
|
|
|
writeCmd(HMC_WRITE_EEPROM, 8, 0x50);
|
|
|
|
cmd(HMC_SAVE_OP_MODE);
|
|
|
|
delayMicroseconds(125);
|
|
|
|
return 0;
|
2013-09-30 11:00:55 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// HANDLE WITH CARE - RESTART NECESSARY
|
2013-09-30 11:03:06 -04:00
|
|
|
// Returns Operational Mode Config Byte
|
2013-09-30 11:00:55 -04:00
|
|
|
int hmc6352::setOperationalModus(hmcMode m, uint8_t freq, bool periodicReset)
|
|
|
|
{
|
2017-09-13 02:58:18 -04:00
|
|
|
byte omcb = 0; // Operational Mode Control Byte
|
|
|
|
switch(freq)
|
|
|
|
{
|
|
|
|
case 1: break;
|
|
|
|
case 5: omcb |= 0x20; break;
|
|
|
|
case 10: omcb |= 0x40; break;
|
|
|
|
case 20: omcb |= 0x60; break;
|
|
|
|
default: return -21;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (periodicReset) omcb |= 0x10;
|
|
|
|
|
|
|
|
switch(m)
|
|
|
|
{
|
|
|
|
case STANDBY: break; // omcb |= 0x00;
|
|
|
|
case QUERY: omcb |= 0x01; break;
|
|
|
|
case CONT: omcb |= 0x02; break;
|
|
|
|
default: return -20;
|
|
|
|
}
|
|
|
|
|
|
|
|
writeCmd(HMC_WRITE_RAM, 0x74, omcb);
|
|
|
|
cmd(HMC_SAVE_OP_MODE);
|
|
|
|
delayMicroseconds(125);
|
|
|
|
return omcb;
|
|
|
|
}
|
2013-09-30 11:00:55 -04:00
|
|
|
|
2013-09-30 11:03:06 -04:00
|
|
|
// read the Operational Modus as byte from EEPROM
|
|
|
|
// TODO: split into 3 items
|
|
|
|
//
|
2013-09-30 11:00:55 -04:00
|
|
|
int hmc6352::getOperationalModus()
|
|
|
|
{
|
2017-09-13 02:58:18 -04:00
|
|
|
// datasheet state that at startup the OM is copied from EEPROM
|
|
|
|
// and that after writing to RAM a reboot is needed to enable new settings
|
|
|
|
// my interpretation ==> EEPROM is leading
|
|
|
|
return readCmd(HMC_READ_RAM, 0x74);
|
|
|
|
//return readCmd(HMC_READ_EEPROM, 0x08);
|
2013-09-30 11:00:55 -04:00
|
|
|
}
|
|
|
|
|
2013-09-30 11:03:06 -04:00
|
|
|
// Switch between normal heading and raw modes
|
|
|
|
// default = 0 ==> normal headings
|
|
|
|
// Note: after a reboot the output modus will be 0 again.
|
2013-09-30 11:00:55 -04:00
|
|
|
int hmc6352::setOutputModus(uint8_t om)
|
|
|
|
{
|
2017-09-13 02:58:18 -04:00
|
|
|
if (om > 4) return -20;
|
|
|
|
return writeCmd(HMC_WRITE_RAM, 0x4E, om);
|
2013-09-30 11:00:55 -04:00
|
|
|
}
|
|
|
|
|
2013-09-30 11:03:06 -04:00
|
|
|
// Read the output modus from RAM
|
2013-09-30 11:00:55 -04:00
|
|
|
int hmc6352::getOutputModus()
|
|
|
|
{
|
2017-09-13 02:58:18 -04:00
|
|
|
return readCmd(HMC_READ_RAM, 0x4E);
|
2013-09-30 11:00:55 -04:00
|
|
|
}
|
|
|
|
|
2013-09-30 11:03:06 -04:00
|
|
|
// NOT TESTED
|
|
|
|
int hmc6352::callibrationOn()
|
|
|
|
{
|
2017-09-13 02:58:18 -04:00
|
|
|
int rv = cmd(HMC_CALLIBRATE_ON);
|
|
|
|
delayMicroseconds(10);
|
|
|
|
return rv;
|
2013-09-30 11:03:06 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// NOT TESTED
|
|
|
|
int hmc6352::callibrationOff()
|
|
|
|
{
|
2017-09-13 02:58:18 -04:00
|
|
|
int rv = cmd(HMC_CALLIBRATE_OFF);
|
|
|
|
delay(15);
|
|
|
|
return rv;
|
2013-09-30 11:03:06 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// NOT TESTED
|
|
|
|
int hmc6352::setI2CAddress(uint8_t address)
|
2013-09-30 11:00:55 -04:00
|
|
|
{
|
2017-09-13 02:58:18 -04:00
|
|
|
if (address < 0x10 || address > 0xF6 ) return -20;
|
|
|
|
return writeCmd(HMC_WRITE_EEPROM, 0, address);
|
2013-09-30 11:00:55 -04:00
|
|
|
}
|
|
|
|
|
2013-09-30 11:03:06 -04:00
|
|
|
// returns current I2C address
|
|
|
|
int hmc6352::getI2CAddress()
|
2013-09-30 11:00:55 -04:00
|
|
|
{
|
2017-09-13 02:58:18 -04:00
|
|
|
return readCmd(HMC_READ_EEPROM, 0);
|
2013-09-30 11:00:55 -04:00
|
|
|
}
|
|
|
|
|
2013-09-30 11:03:06 -04:00
|
|
|
// NOT TESTED
|
|
|
|
// meaning time delay unknown
|
|
|
|
// therefore removed from lib for now
|
2013-09-30 11:00:55 -04:00
|
|
|
int hmc6352::setTimeDelay(uint8_t msec)
|
|
|
|
{
|
2017-09-13 02:58:18 -04:00
|
|
|
return writeCmd(HMC_WRITE_EEPROM, 5, msec);
|
2013-09-30 11:00:55 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
int hmc6352::getTimeDelay()
|
|
|
|
{
|
2017-09-13 02:58:18 -04:00
|
|
|
return readCmd(HMC_READ_EEPROM, 5);
|
2013-09-30 11:00:55 -04:00
|
|
|
}
|
|
|
|
|
2013-09-30 11:03:06 -04:00
|
|
|
// NOT TESTED
|
|
|
|
// meaning measurement summing unknown
|
|
|
|
// therefore removed from lib for now
|
2013-09-30 11:00:55 -04:00
|
|
|
int hmc6352::setMeasurementSumming(uint8_t ms)
|
|
|
|
{
|
2017-09-13 02:58:18 -04:00
|
|
|
if (ms > 16 ) ms = 16;
|
|
|
|
return writeCmd(HMC_WRITE_EEPROM, 6, ms);
|
2013-09-30 11:00:55 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
int hmc6352::getMeasurementSumming()
|
|
|
|
{
|
2017-09-13 02:58:18 -04:00
|
|
|
return readCmd(HMC_READ_EEPROM, 6);
|
2013-09-30 11:00:55 -04:00
|
|
|
}
|
|
|
|
|
2013-09-30 11:03:06 -04:00
|
|
|
// Makes only sense in setOperationalModus()
|
|
|
|
// therefore removed from lib for now
|
|
|
|
int hmc6352::saveOpMode()
|
2013-09-30 11:00:55 -04:00
|
|
|
{
|
2017-09-13 02:58:18 -04:00
|
|
|
int rv = cmd(HMC_SAVE_OP_MODE);
|
|
|
|
delayMicroseconds(125);
|
|
|
|
return rv;
|
2013-09-30 11:00:55 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-09-30 11:03:06 -04:00
|
|
|
// NOT TESTED
|
|
|
|
// meaning UpdateOffsets unknown
|
|
|
|
// therefore removed from lib for now
|
|
|
|
int hmc6352::updateOffsets()
|
2013-09-30 11:00:55 -04:00
|
|
|
{
|
2017-09-13 02:58:18 -04:00
|
|
|
int rv = cmd(HMC_UPDATE_OFFSETS);
|
|
|
|
delay(6);
|
|
|
|
return rv;
|
2013-09-30 11:00:55 -04:00
|
|
|
}
|
|
|
|
|
2013-09-30 11:03:06 -04:00
|
|
|
// idem
|
|
|
|
// use at own risk ...
|
2013-09-30 11:00:55 -04:00
|
|
|
int hmc6352::writeEEPROM(uint8_t address, uint8_t data)
|
|
|
|
{
|
2017-09-13 02:58:18 -04:00
|
|
|
return writeCmd(HMC_WRITE_EEPROM, address, data);
|
2013-09-30 11:00:55 -04:00
|
|
|
}
|
|
|
|
|
2013-09-30 11:03:06 -04:00
|
|
|
// idem
|
2013-09-30 11:00:55 -04:00
|
|
|
int hmc6352::readEEPROM(uint8_t address)
|
|
|
|
{
|
2017-09-13 02:58:18 -04:00
|
|
|
return readCmd(HMC_READ_EEPROM, address);
|
2013-09-30 11:00:55 -04:00
|
|
|
}
|
|
|
|
|
2013-09-30 11:03:06 -04:00
|
|
|
|
|
|
|
// idem
|
|
|
|
// Most RAM locations have an unknown meaning
|
|
|
|
// use at own risk ...
|
2013-09-30 11:00:55 -04:00
|
|
|
int hmc6352::writeRAM(uint8_t address, uint8_t data)
|
|
|
|
{
|
2017-09-13 02:58:18 -04:00
|
|
|
return writeCmd(HMC_WRITE_RAM, address, data);
|
2013-09-30 11:00:55 -04:00
|
|
|
}
|
2013-09-30 11:03:06 -04:00
|
|
|
// idem
|
2013-09-30 11:00:55 -04:00
|
|
|
int hmc6352::readRAM(uint8_t address)
|
|
|
|
{
|
2017-09-13 02:58:18 -04:00
|
|
|
return readCmd(HMC_READ_RAM, address);
|
2013-09-30 11:00:55 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* PRIVATE FUNCTIONS
|
2017-09-13 02:58:18 -04:00
|
|
|
mostly to remove redundancy in functions above
|
2013-09-30 11:00:55 -04:00
|
|
|
*/
|
|
|
|
int hmc6352::cmd(uint8_t c)
|
|
|
|
{
|
2017-09-13 02:58:18 -04:00
|
|
|
Wire.beginTransmission(_device);
|
2017-09-13 03:18:55 -04:00
|
|
|
WIRE_WRITE(c);
|
2017-09-13 02:58:18 -04:00
|
|
|
int rv = Wire.endTransmission();
|
|
|
|
delay(10);
|
|
|
|
return rv;
|
2013-09-30 11:00:55 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
int hmc6352::readCmd(uint8_t c, uint8_t address)
|
|
|
|
{
|
2017-09-13 02:58:18 -04:00
|
|
|
Wire.beginTransmission(_device);
|
2017-09-13 03:18:55 -04:00
|
|
|
WIRE_WRITE(c);
|
|
|
|
WIRE_WRITE(address);
|
2017-09-13 02:58:18 -04:00
|
|
|
int rv = Wire.endTransmission();
|
|
|
|
if (rv != 0) return -rv;
|
|
|
|
|
|
|
|
delayMicroseconds(70);
|
|
|
|
|
|
|
|
rv = Wire.requestFrom(_device, (uint8_t)1);
|
|
|
|
if (rv != 1) return -10;
|
2017-09-13 03:18:55 -04:00
|
|
|
rv = WIRE_READ();
|
2017-09-13 02:58:18 -04:00
|
|
|
return rv;
|
2013-09-30 11:00:55 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
int hmc6352::writeCmd(uint8_t c, uint8_t address, uint8_t data)
|
|
|
|
{
|
2017-09-13 02:58:18 -04:00
|
|
|
Wire.beginTransmission(_device);
|
2017-09-13 03:18:55 -04:00
|
|
|
WIRE_WRITE(c);
|
|
|
|
WIRE_WRITE(address);
|
|
|
|
WIRE_WRITE(data);
|
2017-09-13 02:58:18 -04:00
|
|
|
int rv = Wire.endTransmission();
|
|
|
|
delayMicroseconds(70);
|
|
|
|
return rv;
|
2013-09-30 11:00:55 -04:00
|
|
|
}
|