Compass sensor library
Dependents: compassDemo weather_station_proj weather_station_project weather_station_proj_v1_2
HMC5983.cpp@0:cffff4c45a14, 2018-06-15 (annotated)
- Committer:
- acracan
- Date:
- Fri Jun 15 12:22:40 2018 +0000
- Revision:
- 0:cffff4c45a14
- Child:
- 1:fb6804e865fd
Adaptation from Arduino HMC5983 library
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
acracan | 0:cffff4c45a14 | 1 | /* |
acracan | 0:cffff4c45a14 | 2 | * HMC5983.cpp - library header |
acracan | 0:cffff4c45a14 | 3 | * |
acracan | 0:cffff4c45a14 | 4 | * simple library for the HMC5983 sensor from Honeywell |
acracan | 0:cffff4c45a14 | 5 | * adaptation form MBED from the Arduino library |
acracan | 0:cffff4c45a14 | 6 | * |
acracan | 0:cffff4c45a14 | 7 | * (c) 2014 Korneliusz Jarzebski, www.jarzebski.pl |
acracan | 0:cffff4c45a14 | 8 | * (c) 2014 David Cuartielles, Arduino LLC |
acracan | 0:cffff4c45a14 | 9 | * (c) 2016 Abel Romero, www.abelromero.com |
acracan | 0:cffff4c45a14 | 10 | * (c) 2018 Arcadie Cracan |
acracan | 0:cffff4c45a14 | 11 | */ |
acracan | 0:cffff4c45a14 | 12 | |
acracan | 0:cffff4c45a14 | 13 | // MISSING: EARTH DECLINATION ANGLE |
acracan | 0:cffff4c45a14 | 14 | // In other words, we are not making any compensation for the earth's north pole location vs the magnetic measurement |
acracan | 0:cffff4c45a14 | 15 | |
acracan | 0:cffff4c45a14 | 16 | #include "HMC5983.h" |
acracan | 0:cffff4c45a14 | 17 | #include <new> |
acracan | 0:cffff4c45a14 | 18 | |
acracan | 0:cffff4c45a14 | 19 | HMC5983::HMC5983(PinName sda, PinName scl) : i2c_(*reinterpret_cast<I2C*>(i2cRaw)) |
acracan | 0:cffff4c45a14 | 20 | { |
acracan | 0:cffff4c45a14 | 21 | // Placement new to avoid additional heap memory allocation. |
acracan | 0:cffff4c45a14 | 22 | new(i2cRaw) I2C(sda, scl); |
acracan | 0:cffff4c45a14 | 23 | |
acracan | 0:cffff4c45a14 | 24 | init(); |
acracan | 0:cffff4c45a14 | 25 | } |
acracan | 0:cffff4c45a14 | 26 | |
acracan | 0:cffff4c45a14 | 27 | HMC5983::HMC5983(I2C &i2c): i2c_(i2c){ |
acracan | 0:cffff4c45a14 | 28 | init(); |
acracan | 0:cffff4c45a14 | 29 | } |
acracan | 0:cffff4c45a14 | 30 | |
acracan | 0:cffff4c45a14 | 31 | bool HMC5983::init() |
acracan | 0:cffff4c45a14 | 32 | { |
acracan | 0:cffff4c45a14 | 33 | if ((fastRegister8(HMC5983_REG_IDENT_A) != 0x48) |
acracan | 0:cffff4c45a14 | 34 | || (fastRegister8(HMC5983_REG_IDENT_B) != 0x34) |
acracan | 0:cffff4c45a14 | 35 | || (fastRegister8(HMC5983_REG_IDENT_C) != 0x33)) { |
acracan | 0:cffff4c45a14 | 36 | return false; |
acracan | 0:cffff4c45a14 | 37 | } |
acracan | 0:cffff4c45a14 | 38 | |
acracan | 0:cffff4c45a14 | 39 | // Set Gain Range |
acracan | 0:cffff4c45a14 | 40 | setRange(HMC5983_RANGE_8_1GA); |
acracan | 0:cffff4c45a14 | 41 | // Set DataRate 220Hz ~4.5ms |
acracan | 0:cffff4c45a14 | 42 | setDataRate(HMC5983_DATARATE_220HZ); |
acracan | 0:cffff4c45a14 | 43 | // Set number of samples to average |
acracan | 0:cffff4c45a14 | 44 | setSampleAverages(HMC5983_SAMPLEAVERAGE_2); |
acracan | 0:cffff4c45a14 | 45 | // Set Mode |
acracan | 0:cffff4c45a14 | 46 | setMeasurementMode(HMC5983_CONTINOUS); |
acracan | 0:cffff4c45a14 | 47 | |
acracan | 0:cffff4c45a14 | 48 | // Setup DRDY int |
acracan | 0:cffff4c45a14 | 49 | // if (ISR_callback != NULL) { |
acracan | 0:cffff4c45a14 | 50 | // pinMode(3, INPUT_PULLUP); |
acracan | 0:cffff4c45a14 | 51 | // attachInterrupt(digitalPinToInterrupt(3), ISR_callback, FALLING); |
acracan | 0:cffff4c45a14 | 52 | // } |
acracan | 0:cffff4c45a14 | 53 | return true; |
acracan | 0:cffff4c45a14 | 54 | } |
acracan | 0:cffff4c45a14 | 55 | |
acracan | 0:cffff4c45a14 | 56 | /* |
acracan | 0:cffff4c45a14 | 57 | From datasheet for the HMC5983 |
acracan | 0:cffff4c45a14 | 58 | Below is an example of a (power-on) initialization process for “continuous-measurement mode” via I2C interface: |
acracan | 0:cffff4c45a14 | 59 | 1. Write CRA (00) – send 0x3C 0x00 0x70 (8-average, 15 Hz default or any other rate, normal measurement) |
acracan | 0:cffff4c45a14 | 60 | 2. Write CRB (01) – send 0x3C 0x01 0xA0 (Gain=5, or any other desired gain) |
acracan | 0:cffff4c45a14 | 61 | 3. For each measurement query: |
acracan | 0:cffff4c45a14 | 62 | Write Mode (02) – send 0x3C 0x02 0x01 (Single-measurement mode) |
acracan | 0:cffff4c45a14 | 63 | Wait 6 ms or monitor status register or DRDY hardware interrupt pin |
acracan | 0:cffff4c45a14 | 64 | Send 0x3D 0x06 (Read all 6 bytes. If gain is changed then this data set is using previous gain) |
acracan | 0:cffff4c45a14 | 65 | Convert three 16-bit 2’s compliment hex values to decimal values and assign to X, Z, Y, respectively. |
acracan | 0:cffff4c45a14 | 66 | (Self addition:) |
acracan | 0:cffff4c45a14 | 67 | 4. Convert the magnetic information into a compass value |
acracan | 0:cffff4c45a14 | 68 | REGARDING THE CALCULATION OF THE ACTUAL HEADING VALUE |
acracan | 0:cffff4c45a14 | 69 | From AN-203 http://www51.honeywell.com/aero/common/documents/myaerospacecatalog-documents/Defense_Brochures-documents/Magnetic__Literature_Application_notes-documents/AN203_Compass_Heading_Using_Magnetometers.pdf |
acracan | 0:cffff4c45a14 | 70 | The magnetic compass heading can be determined (in degrees) from the magnetometer's x and y readings by using the |
acracan | 0:cffff4c45a14 | 71 | following set of equations: |
acracan | 0:cffff4c45a14 | 72 | Direction (y>0) = 90 - [arcTAN(x/y)]*180/PI |
acracan | 0:cffff4c45a14 | 73 | Direction (y<0) = 270 - [arcTAN(x/y)]*180/PI |
acracan | 0:cffff4c45a14 | 74 | Direction (y=0, x<0) = 180.0 |
acracan | 0:cffff4c45a14 | 75 | Direction (y=0, x>0) = 0.0 |
acracan | 0:cffff4c45a14 | 76 | */ |
acracan | 0:cffff4c45a14 | 77 | |
acracan | 0:cffff4c45a14 | 78 | void HMC5983::setRange(hmc5983_range_t range) { |
acracan | 0:cffff4c45a14 | 79 | |
acracan | 0:cffff4c45a14 | 80 | writeRegister8(HMC5983_REG_CONFIG_B, range << 5); |
acracan | 0:cffff4c45a14 | 81 | } |
acracan | 0:cffff4c45a14 | 82 | |
acracan | 0:cffff4c45a14 | 83 | hmc5983_range_t HMC5983::getRange(void) |
acracan | 0:cffff4c45a14 | 84 | { |
acracan | 0:cffff4c45a14 | 85 | return (hmc5983_range_t)((readRegister8(HMC5983_REG_CONFIG_B) >> 5)); |
acracan | 0:cffff4c45a14 | 86 | } |
acracan | 0:cffff4c45a14 | 87 | |
acracan | 0:cffff4c45a14 | 88 | void HMC5983::setMeasurementMode(hmc5983_mode_t mode) { |
acracan | 0:cffff4c45a14 | 89 | uint8_t value; |
acracan | 0:cffff4c45a14 | 90 | |
acracan | 0:cffff4c45a14 | 91 | value = readRegister8(HMC5983_REG_MODE); |
acracan | 0:cffff4c45a14 | 92 | value &= 0b11111100; |
acracan | 0:cffff4c45a14 | 93 | value |= mode; |
acracan | 0:cffff4c45a14 | 94 | |
acracan | 0:cffff4c45a14 | 95 | writeRegister8(HMC5983_REG_MODE, value); |
acracan | 0:cffff4c45a14 | 96 | } |
acracan | 0:cffff4c45a14 | 97 | |
acracan | 0:cffff4c45a14 | 98 | hmc5983_mode_t HMC5983::getMeasurementMode(void) { |
acracan | 0:cffff4c45a14 | 99 | uint8_t value; |
acracan | 0:cffff4c45a14 | 100 | |
acracan | 0:cffff4c45a14 | 101 | value = readRegister8(HMC5983_REG_MODE); |
acracan | 0:cffff4c45a14 | 102 | value &= 0b00000011; |
acracan | 0:cffff4c45a14 | 103 | |
acracan | 0:cffff4c45a14 | 104 | return (hmc5983_mode_t)value; |
acracan | 0:cffff4c45a14 | 105 | } |
acracan | 0:cffff4c45a14 | 106 | |
acracan | 0:cffff4c45a14 | 107 | void HMC5983::setDataRate(hmc5983_dataRate_t dataRate) { |
acracan | 0:cffff4c45a14 | 108 | uint8_t value; |
acracan | 0:cffff4c45a14 | 109 | |
acracan | 0:cffff4c45a14 | 110 | value = readRegister8(HMC5983_REG_CONFIG_A); |
acracan | 0:cffff4c45a14 | 111 | value &= 0b11100011; |
acracan | 0:cffff4c45a14 | 112 | value |= (dataRate << 2); |
acracan | 0:cffff4c45a14 | 113 | |
acracan | 0:cffff4c45a14 | 114 | writeRegister8(HMC5983_REG_CONFIG_A, value); |
acracan | 0:cffff4c45a14 | 115 | } |
acracan | 0:cffff4c45a14 | 116 | |
acracan | 0:cffff4c45a14 | 117 | hmc5983_dataRate_t HMC5983::getDataRate(void) { |
acracan | 0:cffff4c45a14 | 118 | uint8_t value; |
acracan | 0:cffff4c45a14 | 119 | |
acracan | 0:cffff4c45a14 | 120 | value = readRegister8(HMC5983_REG_CONFIG_A); |
acracan | 0:cffff4c45a14 | 121 | value &= 0b00011100; |
acracan | 0:cffff4c45a14 | 122 | value >>= 2; |
acracan | 0:cffff4c45a14 | 123 | |
acracan | 0:cffff4c45a14 | 124 | return (hmc5983_dataRate_t)value; |
acracan | 0:cffff4c45a14 | 125 | } |
acracan | 0:cffff4c45a14 | 126 | |
acracan | 0:cffff4c45a14 | 127 | void HMC5983::setSampleAverages(hmc5983_sampleAverages_t sampleAverages) { |
acracan | 0:cffff4c45a14 | 128 | uint8_t value; |
acracan | 0:cffff4c45a14 | 129 | |
acracan | 0:cffff4c45a14 | 130 | value = readRegister8(HMC5983_REG_CONFIG_A); |
acracan | 0:cffff4c45a14 | 131 | value &= 0b10011111; |
acracan | 0:cffff4c45a14 | 132 | value |= (sampleAverages << 5); |
acracan | 0:cffff4c45a14 | 133 | |
acracan | 0:cffff4c45a14 | 134 | writeRegister8(HMC5983_REG_CONFIG_A, value); |
acracan | 0:cffff4c45a14 | 135 | } |
acracan | 0:cffff4c45a14 | 136 | |
acracan | 0:cffff4c45a14 | 137 | hmc5983_sampleAverages_t HMC5983::getSampleAverages(void) { |
acracan | 0:cffff4c45a14 | 138 | uint8_t value; |
acracan | 0:cffff4c45a14 | 139 | |
acracan | 0:cffff4c45a14 | 140 | value = readRegister8(HMC5983_REG_CONFIG_A); |
acracan | 0:cffff4c45a14 | 141 | value &= 0b01100000; |
acracan | 0:cffff4c45a14 | 142 | value >>= 5; |
acracan | 0:cffff4c45a14 | 143 | |
acracan | 0:cffff4c45a14 | 144 | return (hmc5983_sampleAverages_t)value; |
acracan | 0:cffff4c45a14 | 145 | } |
acracan | 0:cffff4c45a14 | 146 | |
acracan | 0:cffff4c45a14 | 147 | // Write byte to register |
acracan | 0:cffff4c45a14 | 148 | void HMC5983::writeRegister8(uint8_t reg, uint8_t value) { |
acracan | 0:cffff4c45a14 | 149 | char cmd[2]; |
acracan | 0:cffff4c45a14 | 150 | |
acracan | 0:cffff4c45a14 | 151 | cmd[0] = reg; |
acracan | 0:cffff4c45a14 | 152 | cmd[1] = value; |
acracan | 0:cffff4c45a14 | 153 | i2c_.write(HMC5983_ADDRESS, cmd, 2); |
acracan | 0:cffff4c45a14 | 154 | } |
acracan | 0:cffff4c45a14 | 155 | |
acracan | 0:cffff4c45a14 | 156 | // Read byte to register |
acracan | 0:cffff4c45a14 | 157 | uint8_t HMC5983::fastRegister8(uint8_t reg) { |
acracan | 0:cffff4c45a14 | 158 | uint8_t value; |
acracan | 0:cffff4c45a14 | 159 | |
acracan | 0:cffff4c45a14 | 160 | i2c_.write(HMC5983_ADDRESS, (char *)®, 1); |
acracan | 0:cffff4c45a14 | 161 | i2c_.read(HMC5983_ADDRESS, (char *)&value, 1); |
acracan | 0:cffff4c45a14 | 162 | |
acracan | 0:cffff4c45a14 | 163 | return value; |
acracan | 0:cffff4c45a14 | 164 | } |
acracan | 0:cffff4c45a14 | 165 | |
acracan | 0:cffff4c45a14 | 166 | // Read byte from register |
acracan | 0:cffff4c45a14 | 167 | uint8_t HMC5983::readRegister8(uint8_t reg) { |
acracan | 0:cffff4c45a14 | 168 | uint8_t value; |
acracan | 0:cffff4c45a14 | 169 | |
acracan | 0:cffff4c45a14 | 170 | i2c_.write(HMC5983_ADDRESS, (char *)®, 1); |
acracan | 0:cffff4c45a14 | 171 | i2c_.read(HMC5983_ADDRESS, (char *)&value, 1); |
acracan | 0:cffff4c45a14 | 172 | |
acracan | 0:cffff4c45a14 | 173 | return value; |
acracan | 0:cffff4c45a14 | 174 | |
acracan | 0:cffff4c45a14 | 175 | // Wire.requestFrom(HMC5983_ADDRESS, 1); |
acracan | 0:cffff4c45a14 | 176 | // while(!Wire.available()) {}; |
acracan | 0:cffff4c45a14 | 177 | // value = Wire.read(); |
acracan | 0:cffff4c45a14 | 178 | } |
acracan | 0:cffff4c45a14 | 179 | |
acracan | 0:cffff4c45a14 | 180 | // Read word from register |
acracan | 0:cffff4c45a14 | 181 | int16_t HMC5983::readRegister16(uint8_t reg) { |
acracan | 0:cffff4c45a14 | 182 | int16_t value; |
acracan | 0:cffff4c45a14 | 183 | char resp[2]; |
acracan | 0:cffff4c45a14 | 184 | |
acracan | 0:cffff4c45a14 | 185 | i2c_.write(HMC5983_ADDRESS, (char *)®, 1); |
acracan | 0:cffff4c45a14 | 186 | i2c_.read(HMC5983_ADDRESS, resp, 2); |
acracan | 0:cffff4c45a14 | 187 | |
acracan | 0:cffff4c45a14 | 188 | // Wire.requestFrom(HMC5983_ADDRESS, 2); |
acracan | 0:cffff4c45a14 | 189 | // while(!Wire.available()) {}; |
acracan | 0:cffff4c45a14 | 190 | |
acracan | 0:cffff4c45a14 | 191 | value = (uint8_t)resp[0] << 8 | (uint8_t)resp[1]; |
acracan | 0:cffff4c45a14 | 192 | |
acracan | 0:cffff4c45a14 | 193 | return value; |
acracan | 0:cffff4c45a14 | 194 | } |
acracan | 0:cffff4c45a14 | 195 | |
acracan | 0:cffff4c45a14 | 196 | double HMC5983::read() { |
acracan | 0:cffff4c45a14 | 197 | // the values for X, Y & Z must be read in X, Z & Y order. |
acracan | 0:cffff4c45a14 | 198 | char data[6]; |
acracan | 0:cffff4c45a14 | 199 | writeRegister8(HMC5983_OUT_X_MSB, 0); // Select MSB X register |
acracan | 0:cffff4c45a14 | 200 | i2c_.read(HMC5983_ADDRESS, data, 6); |
acracan | 0:cffff4c45a14 | 201 | uint8_t X_MSB = data[0]; |
acracan | 0:cffff4c45a14 | 202 | uint8_t X_LSB = data[1]; |
acracan | 0:cffff4c45a14 | 203 | uint8_t Z_MSB = data[2]; |
acracan | 0:cffff4c45a14 | 204 | uint8_t Z_LSB = data[3]; |
acracan | 0:cffff4c45a14 | 205 | uint8_t Y_MSB = data[4]; |
acracan | 0:cffff4c45a14 | 206 | uint8_t Y_LSB = data[5]; |
acracan | 0:cffff4c45a14 | 207 | |
acracan | 0:cffff4c45a14 | 208 | // compose byte for X, Y, Z's LSB & MSB 8bit registers |
acracan | 0:cffff4c45a14 | 209 | int16_t HX = (uint16_t(X_MSB) << 8) | X_LSB; |
acracan | 0:cffff4c45a14 | 210 | int16_t HZ = (uint16_t(Z_MSB) << 8) | Z_LSB; |
acracan | 0:cffff4c45a14 | 211 | int16_t HY = (uint16_t(Y_MSB) << 8) | Y_LSB; |
acracan | 0:cffff4c45a14 | 212 | |
acracan | 0:cffff4c45a14 | 213 | // convert the numbers to fit the |
acracan | 0:cffff4c45a14 | 214 | // if (HX > 0x07FF) HX = 0xFFFF - HX; |
acracan | 0:cffff4c45a14 | 215 | // if (HZ > 0x07FF) HZ = 0xFFFF - HZ; |
acracan | 0:cffff4c45a14 | 216 | // if (HY > 0x07FF) HY = 0xFFFF - HY; |
acracan | 0:cffff4c45a14 | 217 | |
acracan | 0:cffff4c45a14 | 218 | // declare the heading variable we'll be returning |
acracan | 0:cffff4c45a14 | 219 | double H = 0; |
acracan | 0:cffff4c45a14 | 220 | |
acracan | 0:cffff4c45a14 | 221 | // this is the correct way, fixed from original David's work. |
acracan | 0:cffff4c45a14 | 222 | // corrected following datasheet and his own comments...xD |
acracan | 0:cffff4c45a14 | 223 | // even corrected from datasheet, the 90-270 angle is a bit confusing, but the 360º are captured. |
acracan | 0:cffff4c45a14 | 224 | if (HY > 0) H = 90.0 - atan(double(HX) / HY) * 180.0 / M_PI; |
acracan | 0:cffff4c45a14 | 225 | if (HY < 0) H = 270.0 - atan(double(HX) / HY) * 180.0 / M_PI; |
acracan | 0:cffff4c45a14 | 226 | if (HY == 0 && HX < 0) H = 180; |
acracan | 0:cffff4c45a14 | 227 | if (HY == 0 && HX > 0) H = 0; |
acracan | 0:cffff4c45a14 | 228 | |
acracan | 0:cffff4c45a14 | 229 | // point to first data register (from datasheet). Only for continuous-measurement mode. |
acracan | 0:cffff4c45a14 | 230 | // Wire.requestFrom(HMC5983_ADDRESS, 0x03); |
acracan | 0:cffff4c45a14 | 231 | |
acracan | 0:cffff4c45a14 | 232 | return H; |
acracan | 0:cffff4c45a14 | 233 | } |