HMC5883L Digital Compass Library

Fork of HMC5883L by Baser Kandehir

Files at this revision

API Documentation at this revision

Comitter:
brunohorta
Date:
Wed May 30 19:14:58 2018 +0000
Parent:
2:bbc9ad18fd3e
Commit message:
ble

Changed in this revision

HMC5883L.cpp Show annotated file Show diff for this revision Revisions of this file
HMC5883L.h Show annotated file Show diff for this revision Revisions of this file
--- a/HMC5883L.cpp	Wed Aug 05 13:16:12 2015 +0000
+++ b/HMC5883L.cpp	Wed May 30 19:14:58 2018 +0000
@@ -1,80 +1,11 @@
-/*   HMC5883L Digital Compass Library
-*
-*    @author: Baser Kandehir 
-*    @date: August 5, 2015
-*    @license: MIT license
-*     
-*   Copyright (c) 2015, Baser Kandehir, baser.kandehir@ieee.metu.edu.tr
-*
-*   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.
-*
-*/
-
-// Some part of the code is adapted from Adafruit HMC5883 library
 
 #include "HMC5883L.h"
-
-/* NUCLEO F411RE board */
-static I2C i2c(D14, D15);         // setup i2c (SDA,SCL)  
+static I2C i2c(p4, p2);         // setup i2c (SDA,SCL)  
 
-static float Gauss_LSB_XY = 1100.0F; // Varies with gain
-static float Gauss_LSB_Z =  980.0F;  // Varies with gain
- 
-void HMC5883L::setMagGain(MagGain gain)
-{
-    writeByte(HMC5883L_ADDRESS, CONFIG_B, (int8_t)gain);    
-    
-    switch(gain)
-    {
-        case MagGain_13:
-            Gauss_LSB_XY = 1100;
-            Gauss_LSB_Z  = 980;
-            break;
-        case MagGain_19:
-            Gauss_LSB_XY = 855;
-            Gauss_LSB_Z  = 760;
-            break;
-        case MagGain_25:
-            Gauss_LSB_XY = 670;
-            Gauss_LSB_Z  = 600;
-            break;
-        case MagGain_40:
-            Gauss_LSB_XY = 450;
-            Gauss_LSB_Z  = 400;
-            break;
-        case MagGain_47:
-            Gauss_LSB_XY = 400;
-            Gauss_LSB_Z  = 255;
-            break;
-        case MagGain_56:
-            Gauss_LSB_XY = 330;
-            Gauss_LSB_Z  = 295;
-            break;
-        case MagGain_81:
-            Gauss_LSB_XY = 230;
-            Gauss_LSB_Z  = 205;
-            break;
-    }  
-}
+ uint16_t  mode;
+float    declination_offset_radians;
 
-void HMC5883L::writeByte(uint8_t address, uint8_t regAddress, uint8_t data)
-{
+void HMC5883L::writeByte(uint8_t address, uint8_t regAddress, uint8_t data){
     char data_write[2];
     data_write[0]=regAddress;           // I2C sends MSB first. Namely  >>|regAddress|>>|data|
     data_write[1]=data;
@@ -101,16 +32,111 @@
         dest[i]=data[i];
 }
 
-void HMC5883L::init()
-{
+void HMC5883L::init(){
+    declination_offset_radians =  0 - (( 54 + (1/60 * 4) ) * (M_PI / 180));
+    mode = COMPASS_CONTINUOUS | COMPASS_SCALE_130 | COMPASS_VERTICAL_X_EAST;  
     writeByte(HMC5883L_ADDRESS, CONFIG_A, 0x78);  // Number of samples averaged: 8, Data output rate: 75 Hz
-    writeByte(HMC5883L_ADDRESS, MODE,     0x00);  // Continuous-Measurement Mode
-    setMagGain(MagGain_13);
+    writeByte(HMC5883L_ADDRESS, MODE,     mode & 0x03);  // Continuous-Measurement Mode
     wait_ms(10);
 }
+void HMC5883L::setScale( uint16_t scale ){
+  // Scale is the bits marked S in mode
+  //    xxxxxxxxxxxSSSMM  
+  mode = (mode & ~0x1C) | (scale & 0x1C);
+  writeByte(HMC5883L_ADDRESS,CONFIG_B, (( mode >> 2 ) & 0x07) << 5);
+}
 
-void HMC5883L::readMagData(float* dest)
+/** Set the orientation to one of COMPASS_HORIZONTAL_X_NORTH 
+ * through COMPASS_VERTICAL_Y_WEST
+ *  
+ */
+
+void HMC5883L::setOrientation( uint16_t orientation )
 {
+  // Orientation is the bits marked XXXYYYZZZ in mode
+  //    xxXXXYYYZZZxxxxx
+  mode = (mode & ~0x3FE0) | (orientation & 0x3FE0);    
+}
+
+float s_mag_north, s_mag_west;
+double HMC5883L::getHeading(){
+      
+    float sample[3];
+    readMagData(sample);
+  
+  
+  
+  float heading;    
+  
+  // Determine which of the Axes to use for North and West (when compass is "pointing" north)
+  float mag_north, mag_west = -100000;
+
+ switch((mode >> 5) & 0x07 )
+  {
+    case COMPASS_NORTH: mag_north = sample[2]; break;
+    case COMPASS_SOUTH: mag_north = 0-sample[2]; break;
+    case COMPASS_WEST:  mag_west  = sample[2]; break;
+    case COMPASS_EAST:  mag_west  = 0-sample[2]; break;
+      
+    // Don't care
+    case COMPASS_UP:
+    case COMPASS_DOWN:
+     break;
+  }
+  
+  // Y = bits 3 - 5
+  switch(((mode >> 5) >> 3) & 0x07 )
+  {
+    case COMPASS_NORTH: mag_north = sample[1];  break;
+    case COMPASS_SOUTH: mag_north = 0-sample[1]; ;  break;
+    case COMPASS_WEST:  mag_west  = sample[1];  break;
+    case COMPASS_EAST:  mag_west  = 0-sample[1];  break;
+      
+    // Don't care
+    case COMPASS_UP:
+    case COMPASS_DOWN:
+     break;
+  }
+  
+  // X = bits 6 - 8
+  switch(((mode >> 5) >> 6) & 0x07 )
+  {
+    case COMPASS_NORTH: mag_north = sample[0]; break;
+    case COMPASS_SOUTH: mag_north = 0-sample[0]; break;
+    case COMPASS_WEST:  mag_west  = sample[0]; break;
+    case COMPASS_EAST:  mag_west  = 0-sample[0]; break;
+      
+    // Don't care
+    case COMPASS_UP:
+    case COMPASS_DOWN:
+     break;
+  }
+    if(s_mag_north == -100000) {
+        s_mag_north = mag_north;
+    }
+    if(s_mag_west == -100000) {
+        s_mag_west = mag_west;
+    }
+  // calculate heading from the north and west magnetic axes
+  heading = atan2(mag_west - s_mag_west, mag_north- s_mag_north);
+  // Adjust the heading by the declination
+  heading += declination_offset_radians;
+  
+  // Correct for when signs are reversed.
+  if(heading < 0)
+    heading += 2*M_PI;
+    
+ // Check for wrap due to addition of declination.
+  if(heading > 2*M_PI)
+    heading -= 2*M_PI;
+    
+  // Convert radians to degrees for readability.
+  heading =  heading * 180/M_PI; 
+  return heading ;
+}
+
+
+void HMC5883L::readMagData(float* dest){
     uint8_t rawData[6]; // x,y,z mag data
     
     /* Read six raw data registers sequentially and write them into data array */
@@ -121,36 +147,5 @@
     dest[2] = (int16_t)(((int16_t)rawData[2]<<8) | rawData[3]);  // MAG_ZOUT
     dest[1] = (int16_t)(((int16_t)rawData[4]<<8) | rawData[5]);  // MAG_YOUT 
     
-    /* Convert raw data to magnetic field values in microtesla */
-     dest[0] = dest[0] / Gauss_LSB_XY * GAUSS_TO_MICROTESLA;
-     dest[1] = dest[1] / Gauss_LSB_XY * GAUSS_TO_MICROTESLA;
-     dest[2] = dest[2] / Gauss_LSB_Z  * GAUSS_TO_MICROTESLA;
-}
 
-double HMC5883L::getHeading()
-{
-    float magData[3];
-    readMagData(magData);
-    
-    /* Calculate the heading while Z axis of the module is pointing up */
-    double heading = atan2(magData[1], magData[0]);
-    
-    // After calculating heading declination angle should be added to heading which is the error of the magnetic field in specific location.
-    // declinationAngle can be found here http://www.magnetic-declination.com/
-    // For Ankara (my location) declinationAngle is ~5.5 degrees (0.096 radians)
-    float declinationAngle = 0.096;
-    heading += declinationAngle;
-    
-    // Correct for when signs are reversed.
-    if(heading < 0)
-        heading += 2*PI;
-    
-    // Check for wrap due to addition of declination.
-    if(heading > 2*PI)
-        heading -= 2*PI;
-    
-    /* Convert radian to degrees */
-    heading = heading * 180 / PI;  
-    
-    return heading;    
-}
+}
\ No newline at end of file
--- a/HMC5883L.h	Wed Aug 05 13:16:12 2015 +0000
+++ b/HMC5883L.h	Wed May 30 19:14:58 2018 +0000
@@ -28,8 +28,7 @@
 
 #include "mbed.h"
 #include "math.h"
-#include "ledControl.h"
-
+#define M_PI 3.14159265359 
 #define PI 3.14159265359 
 #define GAUSS_TO_MICROTESLA 100
 #define HMC5883L_ADDRESS 0x3C
@@ -48,8 +47,37 @@
 #define ID_A         0x0A
 #define ID_B         0x0B
 #define ID_C         0x0C
+#define COMPASS_SCALE_088  0x00 << 2
+#define COMPASS_SCALE_130  0x01 << 2
+#define COMPASS_SCALE_190  0x02 << 2
+#define COMPASS_SCALE_250  0x03 << 2
+#define COMPASS_SCALE_400  0x04 << 2
+#define COMPASS_SCALE_470  0x05 << 2
+#define COMPASS_SCALE_560  0x06 << 2
+#define COMPASS_SCALE_810  0x07 << 2
 
+//  xxXXXYYYZZZxxxxx
+//  ORIENTATION: 
+#define COMPASS_NORTH 0x00 
+#define COMPASS_SOUTH 0x01
+#define COMPASS_WEST  0x02
+#define COMPASS_EAST  0x03
+#define COMPASS_UP    0x04
+#define COMPASS_DOWN  0x05
+#define COMPASS_CONTINUOUS 0x00
+#define COMPASS_SINGLE     0x01
+#define COMPASS_IDLE       0x02
+
+
+// When "pointing" north, define the direction of each of the silkscreen'd arrows
+// (imagine the Z arrow points out of the top of the device) only N/S/E/W are allowed
+#define COMPASS_HORIZONTAL_X_NORTH  ( (COMPASS_NORTH << 6)  | (COMPASS_WEST  << 3)  | COMPASS_UP    ) << 5
+#define COMPASS_HORIZONTAL_Y_NORTH  ( (COMPASS_EAST  << 6)  | (COMPASS_NORTH << 3)  | COMPASS_UP    ) << 5
+#define COMPASS_VERTICAL_X_EAST     ( (COMPASS_EAST  << 6)  | (COMPASS_UP    << 3)  | COMPASS_SOUTH ) << 5
+#define COMPASS_VERTICAL_Y_WEST     ( (COMPASS_UP    << 6)  | (COMPASS_WEST  << 3)  | COMPASS_SOUTH ) << 5
 /* Magnetometer Gain Settings */
+
+    
 enum MagGain
 {
     MagGain_088 =  0x00,      // +/- 0.88 Ga
@@ -68,8 +96,10 @@
         void init();
         double getHeading();
         void readMagData(float* dest);  
+        void setScale(uint16_t sampling_mode );
+        void setOrientation(uint16_t sampling_mode );    
     private:
-        void setMagGain(MagGain gain);
+        
         void writeByte(uint8_t address, uint8_t regAddress, uint8_t data);
         char readByte(uint8_t address, uint8_t regAddress);
         void readBytes(uint8_t address, uint8_t regAddress, uint8_t byteNum, uint8_t* dest);