From aeaf84ccc64899a3bae4894ed256cd94ba969635 Mon Sep 17 00:00:00 2001 From: Jon Trulson Date: Wed, 29 Mar 2017 13:15:45 -0600 Subject: [PATCH] bmm150: split into new library, C port, FTI, C++ wraps C Signed-off-by: Jon Trulson --- examples/c++/CMakeLists.txt | 4 +- examples/c/CMakeLists.txt | 1 + examples/c/bmm150.c | 96 ++++ examples/java/BMM150_Example.java | 12 +- examples/java/CMakeLists.txt | 2 +- examples/javascript/bmm150.js | 16 +- examples/python/bmm150.py | 18 +- include/fti/upm_magnetometer.h | 42 ++ include/upm_fti.h | 4 +- src/bmm150/CMakeLists.txt | 9 + src/bmm150/bmm150.c | 803 ++++++++++++++++++++++++++++++ src/bmm150/bmm150.cxx | 201 ++++++++ src/bmm150/bmm150.h | 374 ++++++++++++++ src/bmm150/bmm150.hpp | 350 +++++++++++++ src/bmm150/bmm150_defs.h | 272 ++++++++++ src/bmm150/bmm150_fti.c | 106 ++++ src/bmm150/javaupm_bmm150.i | 23 + src/bmm150/jsupm_bmm150.i | 14 + src/bmm150/license.txt | 56 +++ src/bmm150/pyupm_bmm150.i | 22 + 20 files changed, 2397 insertions(+), 28 deletions(-) create mode 100644 examples/c/bmm150.c create mode 100644 include/fti/upm_magnetometer.h create mode 100644 src/bmm150/CMakeLists.txt create mode 100644 src/bmm150/bmm150.c create mode 100644 src/bmm150/bmm150.cxx create mode 100644 src/bmm150/bmm150.h create mode 100644 src/bmm150/bmm150.hpp create mode 100644 src/bmm150/bmm150_defs.h create mode 100644 src/bmm150/bmm150_fti.c create mode 100644 src/bmm150/javaupm_bmm150.i create mode 100644 src/bmm150/jsupm_bmm150.i create mode 100644 src/bmm150/license.txt create mode 100644 src/bmm150/pyupm_bmm150.i diff --git a/examples/c++/CMakeLists.txt b/examples/c++/CMakeLists.txt index 413edc56..cd65ac9a 100644 --- a/examples/c++/CMakeLists.txt +++ b/examples/c++/CMakeLists.txt @@ -340,6 +340,8 @@ add_example (mma8x5x) add_example (mag3110) add_example (hdc1000) add_example (bmg160) +add_example (bma250e) +add_example (bmm150) # These are special cases where you specify example binary, source file and module(s) include_directories (${PROJECT_SOURCE_DIR}/src) @@ -365,8 +367,6 @@ add_custom_example (adc-example-cxx adc-sensor.cxx "ads1x15") add_custom_example (light-sensor-example-cxx light-sensor.cxx "si1132;max44009") add_custom_example (light-controller-example-cxx light-controller.cxx "lp8860;ds1808lc;hlg150h") add_custom_example (bme280-example-cxx bme280.cxx bmp280) -add_custom_example (bma250e-example-cxx bma250e.cxx bmx055) -add_custom_example (bmm150-example-cxx bmm150.cxx bmx055) add_custom_example (bmc150-example-cxx bmc150.cxx bmx055) add_custom_example (bmi055-example-cxx bmi055.cxx bmx055) if (OPENZWAVE_FOUND) diff --git a/examples/c/CMakeLists.txt b/examples/c/CMakeLists.txt index f2a7c073..1be5d666 100644 --- a/examples/c/CMakeLists.txt +++ b/examples/c/CMakeLists.txt @@ -155,6 +155,7 @@ add_example (abpdrrt005pg2a5) add_example (lcdks) add_example (bmg160) add_example (bma250e) +add_example (bmm150) # Custom examples add_custom_example (nmea_gps_i2c-example-c nmea_gps_i2c.c nmea_gps) diff --git a/examples/c/bmm150.c b/examples/c/bmm150.c new file mode 100644 index 00000000..c6052deb --- /dev/null +++ b/examples/c/bmm150.c @@ -0,0 +1,96 @@ +/* + * Author: Jon Trulson + * Copyright (c) 2017 Intel Corporation. + * + * The MIT License + * + * 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. + */ + +#include +#include +#include + +#include "upm_utilities.h" +#include "bmm150.h" + +bool shouldRun = true; + +void sig_handler(int signo) +{ + if (signo == SIGINT) + shouldRun = false; +} + + +int main(int argc, char **argv) +{ + signal(SIGINT, sig_handler); +//! [Interesting] + +#if defined(CONFIG_BOARD_ARDUINO_101_SSS) + // ARDUINO_101_SSS (ARC core) must use I2C + // Instantiate a BMM150 instance using default i2c bus and address + bmm150_context sensor = bmm150_init(BMM150_DEFAULT_I2C_BUS, + BMM150_DEFAULT_ADDR, -1); +#elif defined(CONFIG_BOARD_ARDUINO_101) + // ARDUINO_101 (Quark core) where you must use SPI + // Instantiate a BMM150 instance using default SPI bus and pin 10 as CS + bmm150_context sensor = bmm150_init(BMM150_DEFAULT_SPI_BUS, + -1, 10); +#else + // everything else use I2C by default + // Instantiate a BMM150 instance using default i2c bus and address + bmm150_context sensor = bmm150_init(BMM150_DEFAULT_I2C_BUS, + BMM150_DEFAULT_ADDR, -1); +#endif + + if (!sensor) + { + printf("bmm150_init() failed.\n"); + return 1; + } + + // now output data every 250 milliseconds + while (shouldRun) + { + float x, y, z; + + if (bmm150_update(sensor)) + { + printf("bmm150_update() failed\n"); + return 1; + } + + bmm150_get_magnetometer(sensor, &x, &y, &z); + printf("Magnetometer x: %f y: %f z: %f g\n", + x, y, z); + + upm_delay_ms(250); + } + + printf("Exiting...\n"); + + bmm150_close(sensor); + +//! [Interesting] + + return 0; +} diff --git a/examples/java/BMM150_Example.java b/examples/java/BMM150_Example.java index fc34ac75..e7b5117d 100644 --- a/examples/java/BMM150_Example.java +++ b/examples/java/BMM150_Example.java @@ -2,6 +2,8 @@ * Author: Jon Trulson * Copyright (c) 2016 Intel Corporation. * + * The MIT License + * * 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 @@ -22,7 +24,7 @@ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import upm_bmx055.BMM150; +import upm_bmm150.BMM150; public class BMM150_Example { @@ -42,11 +44,11 @@ public class BMM150_Example // update our values from the sensor sensor.update(); - float dataA[] = sensor.getMagnetometer(); + upm_bmm150.floatVector data = sensor.getMagnetometer(); - System.out.println("Magnetometer x: " + dataA[0] - + " y: " + dataA[1] - + " z: " + dataA[2] + System.out.println("Magnetometer x: " + data.get(0) + + " y: " + data.get(1) + + " z: " + data.get(2) + " uT"); System.out.println(); diff --git a/examples/java/CMakeLists.txt b/examples/java/CMakeLists.txt index ffa0018e..b16f39c2 100644 --- a/examples/java/CMakeLists.txt +++ b/examples/java/CMakeLists.txt @@ -168,6 +168,7 @@ add_example(SensorTemplateSample sensortemplate) add_example(P9813Sample p9813) add_example(BMG160_Example bmg160) add_example(BMA250E_Example bma250e) +add_example(BMM150_Example bmm150) add_example_with_path(Jhd1313m1_lcdSample jhd1313m1 jhd1313m1) add_example_with_path(Jhd1313m1Sample jhd1313m1 jhd1313m1) @@ -181,7 +182,6 @@ if(SWIG_VERSION VERSION_GREATER 3.0.8) add_example_with_path(BME280_InterfaceExample bmp280 bmp280) endif() -add_example_with_path(BMM150_Example bmx055 bmx055) add_example_with_path(BMC150_Example bmx055 bmx055) add_example_with_path(BMI055_Example bmx055 bmx055) if (OPENZWAVE_FOUND) diff --git a/examples/javascript/bmm150.js b/examples/javascript/bmm150.js index 84e6c38f..86f309b4 100644 --- a/examples/javascript/bmm150.js +++ b/examples/javascript/bmm150.js @@ -2,6 +2,8 @@ * Author: Jon Trulson * Copyright (c) 2016 Intel Corporation. * + * The MIT License + * * 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 @@ -22,7 +24,7 @@ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -var sensorObj = require('jsupm_bmx055'); +var sensorObj = require('jsupm_bmm150'); // Instantiate a BMM150 instance using default i2c bus and address var sensor = new sensorObj.BMM150(); @@ -30,21 +32,17 @@ var sensor = new sensorObj.BMM150(); // For SPI, bus 0, you would pass -1 as the address, and a valid pin for CS: // BMM150(0, -1, 10); -var x = new sensorObj.new_floatp(); -var y = new sensorObj.new_floatp(); -var z = new sensorObj.new_floatp(); - // now output data every 250 milliseconds setInterval(function() { // update our values from the sensor sensor.update(); - sensor.getMagnetometer(x, y, z); + var data = sensor.getMagnetometer(); console.log("Magnetometer x: " - + sensorObj.floatp_value(x) - + " y: " + sensorObj.floatp_value(y) - + " z: " + sensorObj.floatp_value(z) + + data.get(0) + + " y: " + data.get(1) + + " z: " + data.get(2) + " uT"); console.log(); diff --git a/examples/python/bmm150.py b/examples/python/bmm150.py index 08d52654..4ed4dbcc 100755 --- a/examples/python/bmm150.py +++ b/examples/python/bmm150.py @@ -1,6 +1,8 @@ #!/usr/bin/python # Author: Jon Trulson -# Copyright (c) 2016 Intel Corporation. +# Copyright (c) 2016-2017 Intel Corporation. +# +# The MIT License # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the @@ -23,7 +25,7 @@ from __future__ import print_function import time, sys, signal, atexit -from upm import pyupm_bmx055 as sensorObj +from upm import pyupm_bmm150 as sensorObj def main(): # Instantiate a BMP250E instance using default i2c bus and address @@ -46,18 +48,14 @@ def main(): atexit.register(exitHandler) signal.signal(signal.SIGINT, SIGINTHandler) - x = sensorObj.new_floatp() - y = sensorObj.new_floatp() - z = sensorObj.new_floatp() - # now output data every 250 milliseconds while (1): sensor.update() - sensor.getMagnetometer(x, y, z) - print("Magnetometer x:", sensorObj.floatp_value(x), end=' ') - print(" y:", sensorObj.floatp_value(y), end=' ') - print(" z:", sensorObj.floatp_value(z), end=' ') + data = sensor.getMagnetometer() + print("Magnetometer x:", data[0], end=' ') + print(" y:", data[1], end=' ') + print(" z:", data[2], end=' ') print(" uT") print() diff --git a/include/fti/upm_magnetometer.h b/include/fti/upm_magnetometer.h new file mode 100644 index 00000000..044d0b25 --- /dev/null +++ b/include/fti/upm_magnetometer.h @@ -0,0 +1,42 @@ +/* + * Author: Jon Trulson + * Copyright (c) 2017 Intel Corporation. + * + * 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. + */ +#ifndef UPM_MAGNETOMETER_H_ +#define UPM_MAGNETOMETER_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +// Magnetometer function table +typedef struct _upm_magnetometer_ft { + upm_result_t (*upm_magnetometer_set_scale) (void* dev, float* scale); + upm_result_t (*upm_magnetometer_set_offset) (void* dev, float* offset); + upm_result_t (*upm_magnetometer_get_value) (void* dev, float* value); +} upm_magnetometer_ft; + +#ifdef __cplusplus +} +#endif + +#endif /* UPM_MAGNETOMETER_H_ */ diff --git a/include/upm_fti.h b/include/upm_fti.h index 785f01e6..1e7d9018 100644 --- a/include/upm_fti.h +++ b/include/upm_fti.h @@ -76,7 +76,8 @@ typedef enum { UPM_ORP, UPM_BINARY, UPM_ROTARYENCODER, - UPM_BUTTONS + UPM_BUTTONS, + UPM_MAGNETOMETER } upm_sensor_t; /* Supported IO protocols via MRAA */ @@ -130,6 +131,7 @@ typedef struct _upm_sensor_ft* (*func_get_upm_sensor_ft)(upm_sensor_t sensor_typ #include #include #include +#include #ifdef __cplusplus } diff --git a/src/bmm150/CMakeLists.txt b/src/bmm150/CMakeLists.txt new file mode 100644 index 00000000..46f9d809 --- /dev/null +++ b/src/bmm150/CMakeLists.txt @@ -0,0 +1,9 @@ +upm_mixed_module_init (NAME bmm150 + DESCRIPTION "3-Axis Digital Magnetometer" + C_HDR bmm150.h bmm150_defs.h + C_SRC bmm150.c + CPP_HDR bmm150.hpp + CPP_SRC bmm150.cxx + FTI_SRC bmm150_fti.c + CPP_WRAPS_C + REQUIRES mraa) diff --git a/src/bmm150/bmm150.c b/src/bmm150/bmm150.c new file mode 100644 index 00000000..bd5b903b --- /dev/null +++ b/src/bmm150/bmm150.c @@ -0,0 +1,803 @@ +/* + * Author: Jon Trulson + * Copyright (c) 2017 Intel Corporation. + * + * The MIT License + * + * 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. + */ + + +#include +#include + +#include "upm_utilities.h" + +#include "bmm150.h" + +// SPI CS on and off functions +static void _csOn(const bmm150_context dev) +{ + assert(dev != NULL); + + if (dev->gpioCS) + mraa_gpio_write(dev->gpioCS, 0); +} + +static void _csOff(const bmm150_context dev) +{ + assert(dev != NULL); + + if (dev->gpioCS) + mraa_gpio_write(dev->gpioCS, 1); +} + +// These trimming algorithms (bmm050_bosch_compensate_*()) are taken +// from the Bosch BMM050 reference driver code. See license.txt. + +// Bosch compensation functions +static float _bmm050_compensate_X_float(bmm150_context dev, + int16_t mag_data_x) +{ + assert(dev != NULL); + + uint16_t data_r = dev->hall; + float inter_retval = 0; + + if (mag_data_x != -4096 /* no overflow */ + ) { + if ((data_r != 0) + && (dev->dig_xyz1 != 0)) { + inter_retval = ((((float)dev->dig_xyz1) + * 16384.0 / data_r) - 16384.0); + } else { + inter_retval = 0.0f; + return inter_retval; + } + inter_retval = (((mag_data_x * ((((((float)dev->dig_xy2) * + (inter_retval*inter_retval / + 268435456.0) + + inter_retval * ((float)dev->dig_xy1) + / 16384.0)) + 256.0) * + (((float)dev->dig_x2) + 160.0))) + / 8192.0) + + (((float)dev->dig_x1) * + 8.0)) / 16.0; + } else { + inter_retval = 0.0f; + } + return inter_retval; +} + +static float _bmm050_compensate_Y_float(bmm150_context dev, + int16_t mag_data_y) +{ + assert(dev != NULL); + + uint16_t data_r = dev->hall; + float inter_retval = 0; + + if (mag_data_y != -4096 /* no overflow */ + ) { + if ((data_r != 0) + && (dev->dig_xyz1 != 0)) { + inter_retval = ((((float)dev->dig_xyz1) + * 16384.0 + /data_r) - 16384.0); + } else { + inter_retval = 0.0f; + return inter_retval; + } + inter_retval = (((mag_data_y * ((((((float)dev->dig_xy2) * + (inter_retval*inter_retval + / 268435456.0) + + inter_retval * ((float)dev->dig_xy1) + / 16384.0)) + + 256.0) * + (((float)dev->dig_y2) + 160.0))) + / 8192.0) + + (((float)dev->dig_y1) * 8.0)) + / 16.0; + } else { + /* overflow, set output to 0.0f */ + inter_retval = 0.0f; + } + return inter_retval; +} + +static float _bmm050_compensate_Z_float(bmm150_context dev, + int16_t mag_data_z) +{ + assert(dev != NULL); + + uint16_t data_r = dev->hall; + float inter_retval = 0; + /* no overflow */ + if (mag_data_z != -16384) { + if ((dev->dig_z2 != 0) + && (dev->dig_z1 != 0) + && (dev->dig_xyz1 != 0) + && (data_r != 0)) { + inter_retval = ((((((float)mag_data_z)- + ((float)dev->dig_z4)) * 131072.0)- + (((float)dev->dig_z3)*(((float)data_r) + -((float)dev->dig_xyz1)))) + /((((float)dev->dig_z2)+ + ((float)dev->dig_z1)*((float)data_r) / + 32768.0) * 4.0)) / 16.0; + } + } else { + /* overflow, set output to 0.0f */ + inter_retval = 0.0f; + } + return inter_retval; +} + +// read trim data +static upm_result_t _bmm150_read_trim_data(const bmm150_context dev) +{ + assert(dev != NULL); + + int bufLen = 10; + uint8_t calibData[bufLen]; + + // 2 bytes first + if (bmm150_read_regs(dev, BMM150_REG_TRIM_DIG_X1, calibData, 2) != 2) + return UPM_ERROR_OPERATION_FAILED; + + dev->dig_x1 = (int8_t)calibData[0]; + dev->dig_y1 = (int8_t)calibData[1]; + + // next block of 4 bytes + if (bmm150_read_regs(dev, BMM150_REG_TRIM_DIG_Z4_LSB, calibData, 4) != 4) + return UPM_ERROR_OPERATION_FAILED; + + dev->dig_z4 = (int16_t)((calibData[1] << 8) | calibData[0]); + dev->dig_x2 = (int8_t)calibData[2]; + dev->dig_y2 = (int8_t)calibData[3]; + + // final block of 10 bytes + if (bmm150_read_regs(dev, BMM150_REG_TRIM_DIG_Z2_LSB, calibData, 10) != 10) + return UPM_ERROR_OPERATION_FAILED; + + dev->dig_z2 = (int16_t)((calibData[1] << 8) | calibData[0]); + dev->dig_z1 = (uint16_t)((calibData[3] << 8) | calibData[2]); + dev->dig_xyz1 = (uint16_t)((calibData[5] << 8) | calibData[4]); + dev->dig_z3 = (int16_t)((calibData[7] << 8) | calibData[6]); + dev->dig_xy2 = (int8_t)calibData[8]; + dev->dig_xy1 = calibData[9]; + + return UPM_SUCCESS; +} + + +// init +bmm150_context bmm150_init(int bus, int addr, int cs) +{ + bmm150_context dev = + (bmm150_context)malloc(sizeof(struct _bmm150_context)); + + if (!dev) + return NULL; + + // zero out context + memset((void *)dev, 0, sizeof(struct _bmm150_context)); + + // make sure MRAA is initialized + if (mraa_init() != MRAA_SUCCESS) + { + printf("%s: mraa_init() failed.\n", __FUNCTION__); + bmm150_close(dev); + return NULL; + } + + if (addr < 0) + dev->isSPI = true; + + if (dev->isSPI) + { + if (!(dev->spi = mraa_spi_init(bus))) + { + printf("%s: mraa_spi_init() failed.\n", __FUNCTION__); + bmm150_close(dev); + return NULL; + } + + // Only create cs context if we are actually using a valid pin. + // A hardware controlled pin should specify cs as -1. + if (cs >= 0) + { + if (!(dev->gpioCS = mraa_gpio_init(cs))) + { + printf("%s: mraa_gpio_init() failed.\n", __FUNCTION__); + bmm150_close(dev); + return NULL; + } + mraa_gpio_dir(dev->gpioCS, MRAA_GPIO_OUT); + } + + mraa_spi_mode(dev->spi, MRAA_SPI_MODE0); + if (mraa_spi_frequency(dev->spi, 5000000)) + { + printf("%s: mraa_spi_frequency() failed.\n", __FUNCTION__); + bmm150_close(dev); + return NULL; + } + } + else + { + // I2C + + if (!(dev->i2c = mraa_i2c_init(bus))) + { + printf("%s: mraa_i2c_init() failed.\n", __FUNCTION__); + bmm150_close(dev); + return NULL; + } + + if (mraa_i2c_address(dev->i2c, addr)) + { + printf("%s: mraa_i2c_address() failed.\n", __FUNCTION__); + bmm150_close(dev); + return NULL; + } + } + + // power bit must be on for chip ID to be accessible + bmm150_set_power_bit(dev, true); + + // not really, just need to set it to something valid until + // bmm150_set_opmode() is called in bmm150_devinit(). + dev->opmode = BMM150_OPERATION_MODE_SLEEP; + + upm_delay_ms(50); + + // check the chip id + + uint8_t chipID = bmm150_get_chip_id(dev); + + if (chipID != BMM150_DEFAULT_CHIPID) + { + printf("%s: invalid chip id: %02x. Expected %02x\n", + __FUNCTION__, chipID, BMM150_DEFAULT_CHIPID); + bmm150_close(dev); + return NULL; + } + + // call devinit with a default high resolution mode + if (bmm150_devinit(dev, BMM150_USAGE_HIGH_ACCURACY)) + { + printf("%s: bmm150_devinit() failed.\n", __FUNCTION__); + bmm150_close(dev); + return NULL; + } + + return dev; +} + +void bmm150_close(bmm150_context dev) +{ + assert(dev != NULL); + + bmm150_uninstall_isr(dev, BMM150_INTERRUPT_INT); + bmm150_uninstall_isr(dev, BMM150_INTERRUPT_DR); + + if (dev->i2c) + mraa_i2c_stop(dev->i2c); + if (dev->spi) + mraa_spi_stop(dev->spi); + if (dev->gpioCS) + mraa_gpio_close(dev->gpioCS); + + free(dev); +} + +upm_result_t bmm150_devinit(const bmm150_context dev, + BMM150_USAGE_PRESETS_T usage) +{ + assert(dev != NULL); + + // just in case... + if (bmm150_set_power_bit(dev, true)) + { + printf("%s: bmm150_set_power_bit() failed.\n", __FUNCTION__); + return UPM_ERROR_OPERATION_FAILED; + } + + // get trim data + if (_bmm150_read_trim_data(dev)) + { + printf("%s: _bmm150_read_trim_data() failed.\n", __FUNCTION__); + return UPM_ERROR_OPERATION_FAILED; + } + + if (bmm150_set_opmode(dev, BMM150_OPERATION_MODE_NORMAL)) + { + printf("%s: bmm150_set_opmode() failed.\n", __FUNCTION__); + return UPM_ERROR_OPERATION_FAILED; + } + + upm_delay_ms(50); // 50ms, in case we are waking up + + if (bmm150_set_preset_mode(dev, usage)) + { + printf("%s: bmm150_set_preset_mode() failed.\n", __FUNCTION__); + return UPM_ERROR_OPERATION_FAILED; + } + + // settle + upm_delay_ms(50); + + return UPM_SUCCESS; +} + +upm_result_t bmm150_update(const bmm150_context dev) +{ + assert(dev != NULL); + + // special care when in a forced mode - need to trigger a + // measurement, and wait for the opmode to return to OPMODE_SLEEP, + // then we can read the values. + + if (dev->opmode == BMM150_OPERATION_MODE_FORCED) + { + // trigger measurement + if (bmm150_set_opmode(dev, BMM150_OPERATION_MODE_FORCED)) + { + printf("%s: bmm150_set_opmode() failed.\n", __FUNCTION__); + return UPM_ERROR_OPERATION_FAILED; + } + + // opmode will return to BMM150_OPERATION_MODE_SLEEP after + // measurement is complete + do { + upm_delay_ms(5); + } while (bmm150_get_opmode(dev) == BMM150_OPERATION_MODE_FORCED); + } + + const int bufLen = 8; + uint8_t buf[bufLen]; + + if (bmm150_read_regs(dev, BMM150_REG_MAG_X_LSB, buf, bufLen) != bufLen) + { + printf("%s: bmm150_read_regs() failed.\n", __FUNCTION__); + return UPM_ERROR_OPERATION_FAILED; + } + + // we need to get the hall data first, since it's needed for the + // bosch compensation functions for each of the xyz axes + + dev->hall = (uint16_t)(buf[7] << 8 | (buf[6] & + (_BMM150_MAG_RHALL_LSB_LSB_MASK << + _BMM150_MAG_RHALL_LSB_LSB_SHIFT))); + dev->hall /= 4; + + int16_t val; + + // x + val = (int16_t)(buf[1] << 8 | (buf[0] & (_BMM150_MAG_XY_LSB_LSB_MASK << + _BMM150_MAG_XY_LSB_LSB_SHIFT))); + val /= 8; + dev->magX = _bmm050_compensate_X_float(dev, val); + + // y + val = (int16_t)(buf[3] << 8 | (buf[2] & (_BMM150_MAG_XY_LSB_LSB_MASK << + _BMM150_MAG_XY_LSB_LSB_SHIFT))); + val /= 8; + dev->magY = _bmm050_compensate_Y_float(dev, val); + + // z + val = (int16_t)(buf[5] << 8 | (buf[4] & (_BMM150_MAG_Z_LSB_LSB_MASK << + _BMM150_MAG_Z_LSB_LSB_SHIFT))); + val /= 2; + dev->magZ = _bmm050_compensate_Z_float(dev, val); + + return UPM_SUCCESS; +} + +uint8_t bmm150_read_reg(const bmm150_context dev, uint8_t reg) +{ + assert(dev != NULL); + + if (dev->isSPI) + { + reg |= 0x80; // needed for read + uint8_t pkt[2] = {reg, 0}; + + _csOn(dev); + if (mraa_spi_transfer_buf(dev->spi, pkt, pkt, 2)) + { + _csOff(dev); + printf("%s: mraa_spi_transfer_buf() failed.\n", __FUNCTION__); + return 0xff; + } + _csOff(dev); + + return pkt[1]; + } + else + return (uint8_t)mraa_i2c_read_byte_data(dev->i2c, reg); +} + +int bmm150_read_regs(const bmm150_context dev, uint8_t reg, + uint8_t *buffer, int len) +{ + assert(dev != NULL); + + if (dev->isSPI) + { + reg |= 0x80; // needed for read + + uint8_t sbuf[len + 1]; + memset((char *)sbuf, 0, len + 1); + sbuf[0] = reg; + + _csOn(dev); + if (mraa_spi_transfer_buf(dev->spi, sbuf, sbuf, len + 1)) + { + _csOff(dev); + printf("%s: mraa_spi_transfer_buf() failed.\n", __FUNCTION__); + return -1; + } + _csOff(dev); + + // now copy it into user buffer + for (int i=0; ii2c, reg, buffer, len) != len) + return -1; + } + + return len; +} + +upm_result_t bmm150_write_reg(const bmm150_context dev, + uint8_t reg, uint8_t val) +{ + assert(dev != NULL); + + if (dev->isSPI) + { + reg &= 0x7f; // mask off 0x80 for writing + uint8_t pkt[2] = {reg, val}; + + _csOn(dev); + if (mraa_spi_transfer_buf(dev->spi, pkt, NULL, 2)) + { + _csOff(dev); + printf("%s: mraa_spi_transfer_buf() failed.\n", + __FUNCTION__); + + return UPM_ERROR_OPERATION_FAILED; + } + _csOff(dev); + } + else + { + if (mraa_i2c_write_byte_data(dev->i2c, val, reg)) + { + printf("%s: mraa_i2c_write_byte_data() failed.\n", + __FUNCTION__); + return UPM_ERROR_OPERATION_FAILED; + } + } + + return UPM_SUCCESS; +} + +uint8_t bmm150_get_chip_id(const bmm150_context dev) +{ + assert(dev != NULL); + + return bmm150_read_reg(dev, BMM150_REG_CHIP_ID); +} + +void bmm150_get_magnetometer(const bmm150_context dev, + float *x, float *y, float *z) +{ + assert(dev != NULL); + + if (x) + *x = dev->magX; + + if (y) + *y = dev->magY; + + if (z) + *z = dev->magZ; +} + +upm_result_t bmm150_reset(const bmm150_context dev) +{ + assert(dev != NULL); + + // mask off reserved bits + uint8_t reg = + (bmm150_read_reg(dev, BMM150_REG_POWER_CTRL) + & ~_BMM150_POWER_CTRL_RESERVED_BITS); + + reg |= BMM150_POWER_CTRL_SOFT_RESET0 | BMM150_POWER_CTRL_SOFT_RESET1; + + if (bmm150_write_reg(dev, BMM150_REG_POWER_CTRL, reg)) + return UPM_ERROR_OPERATION_FAILED; + + upm_delay(1); + // device will return to SLEEP mode... + + return UPM_SUCCESS; +} + +upm_result_t bmm150_set_output_data_rate(const bmm150_context dev, + BMM150_DATA_RATE_T odr) +{ + assert(dev != NULL); + + uint8_t reg = bmm150_read_reg(dev, BMM150_REG_OPMODE); + + reg &= ~(_BMM150_OPMODE_DATA_RATE_MASK << _BMM150_OPMODE_DATA_RATE_SHIFT); + reg |= (odr << _BMM150_OPMODE_DATA_RATE_SHIFT); + + if (bmm150_write_reg(dev, BMM150_REG_OPMODE, reg)) + return UPM_ERROR_OPERATION_FAILED; + + return UPM_SUCCESS; +} + +upm_result_t bmm150_set_power_bit(const bmm150_context dev, bool power) +{ + assert(dev != NULL); + + // mask off reserved bits + uint8_t reg = + (bmm150_read_reg(dev, BMM150_REG_POWER_CTRL) + & ~_BMM150_POWER_CTRL_RESERVED_BITS); + + if (power) + reg |= BMM150_POWER_CTRL_POWER_CTRL_BIT; + else + reg &= ~BMM150_POWER_CTRL_POWER_CTRL_BIT; + + if (bmm150_write_reg(dev, BMM150_REG_POWER_CTRL, reg)) + return UPM_ERROR_OPERATION_FAILED; + + return UPM_SUCCESS; +} + +upm_result_t bmm150_set_opmode(const bmm150_context dev, + BMM150_OPERATION_MODE_T opmode) +{ + assert(dev != NULL); + + uint8_t reg = bmm150_read_reg(dev, BMM150_REG_OPMODE); + + reg &= ~(_BMM150_OPMODE_OPERATION_MODE_MASK + << _BMM150_OPMODE_OPERATION_MODE_SHIFT); + reg |= (opmode << _BMM150_OPMODE_OPERATION_MODE_SHIFT); + + if (bmm150_write_reg(dev, BMM150_REG_OPMODE, reg)) + return UPM_ERROR_OPERATION_FAILED; + + dev->opmode = opmode; + return UPM_SUCCESS; +} + +BMM150_OPERATION_MODE_T bmm150_get_opmode(const bmm150_context dev) +{ + assert(dev != NULL); + + uint8_t reg = bmm150_read_reg(dev, BMM150_REG_OPMODE); + + reg &= (_BMM150_OPMODE_OPERATION_MODE_MASK + << _BMM150_OPMODE_OPERATION_MODE_SHIFT); + reg >>= _BMM150_OPMODE_OPERATION_MODE_SHIFT; + + return (BMM150_OPERATION_MODE_T)reg; +} + +uint8_t bmm150_get_interrupt_enable(const bmm150_context dev) +{ + assert(dev != NULL); + + return bmm150_read_reg(dev, BMM150_REG_INT_EN); +} + +upm_result_t bmm150_set_interrupt_enable(const bmm150_context dev, + uint8_t bits) +{ + assert(dev != NULL); + + if (bmm150_write_reg(dev, BMM150_REG_INT_EN, bits)) + return UPM_ERROR_OPERATION_FAILED; + + return UPM_SUCCESS; +} + +uint8_t bmm150_get_interrupt_config(const bmm150_context dev) +{ + assert(dev != NULL); + + return bmm150_read_reg(dev, BMM150_REG_INT_CONFIG); +} + +upm_result_t bmm150_set_interrupt_config(const bmm150_context dev, + uint8_t bits) +{ + assert(dev != NULL); + + if (bmm150_write_reg(dev, BMM150_REG_INT_CONFIG, bits)) + return UPM_ERROR_OPERATION_FAILED; + + return UPM_SUCCESS; +} + +uint8_t bmm150_get_interrupt_status(const bmm150_context dev) +{ + assert(dev != NULL); + + return bmm150_read_reg(dev, BMM150_REG_INT_STATUS); +} + +upm_result_t bmm150_set_repetitions_xy(const bmm150_context dev, + uint8_t reps) +{ + assert(dev != NULL); + + if (bmm150_write_reg(dev, BMM150_REG_REP_XY, reps)) + return UPM_ERROR_OPERATION_FAILED; + + return UPM_SUCCESS; +} + +upm_result_t bmm150_set_repetitions_z(const bmm150_context dev, + uint8_t reps) +{ + assert(dev != NULL); + + if (bmm150_write_reg(dev, BMM150_REG_REP_Z, reps)) + return UPM_ERROR_OPERATION_FAILED; + + return UPM_SUCCESS; +} + +upm_result_t bmm150_set_preset_mode(const bmm150_context dev, + BMM150_USAGE_PRESETS_T usage) +{ + assert(dev != NULL); + + bool error = false; + + // these recommended presets come from the datasheet, Table 3, + // Section 4.2 + switch (usage) + { + case BMM150_USAGE_LOW_POWER: + if (bmm150_set_repetitions_xy(dev, 3) + || bmm150_set_repetitions_z(dev, 3) + || bmm150_set_output_data_rate(dev, BMM150_DATA_RATE_10HZ)) + error = true; + + break; + + case BMM150_USAGE_REGULAR: + if (bmm150_set_repetitions_xy(dev, 9) + || bmm150_set_repetitions_z(dev, 15) + || bmm150_set_output_data_rate(dev, BMM150_DATA_RATE_10HZ)) + error = true; + + break; + + case BMM150_USAGE_ENHANCED_REGULAR: + if (bmm150_set_repetitions_xy(dev, 15) + || bmm150_set_repetitions_z(dev, 27) + || bmm150_set_output_data_rate(dev, BMM150_DATA_RATE_10HZ)) + error = true; + + break; + + case BMM150_USAGE_HIGH_ACCURACY: + if (bmm150_set_repetitions_xy(dev, 47) + || bmm150_set_repetitions_z(dev, 83) + || bmm150_set_output_data_rate(dev, BMM150_DATA_RATE_20HZ)) + error = true; + + break; + + default: + printf("%s: Invalid usage value passed.\n", __FUNCTION__); + error = true; + } + + if (error) + return UPM_ERROR_OPERATION_FAILED; + + return UPM_SUCCESS; +} + +upm_result_t bmm150_install_isr(const bmm150_context dev, + BMM150_INTERRUPT_PINS_T intr, int gpio, + mraa_gpio_edge_t level, + void (*isr)(void *), void *arg) +{ + assert(dev != NULL); + + // delete any existing ISR and GPIO context for this interrupt + bmm150_uninstall_isr(dev, intr); + + mraa_gpio_context gpio_isr = NULL; + + // create gpio context + if (!(gpio_isr = mraa_gpio_init(gpio))) + { + printf("%s: mraa_gpio_init() failed.\n", __FUNCTION__); + return UPM_ERROR_OPERATION_FAILED; + } + + mraa_gpio_dir(gpio_isr, MRAA_GPIO_IN); + + if (mraa_gpio_isr(gpio_isr, level, isr, arg)) + { + mraa_gpio_close(gpio_isr); + printf("%s: mraa_gpio_isr() failed.\n", __FUNCTION__); + return UPM_ERROR_OPERATION_FAILED; + } + + switch (intr) + { + case BMM150_INTERRUPT_INT: + dev->gpioINT = gpio_isr; + break; + + case BMM150_INTERRUPT_DR: + dev->gpioDR = gpio_isr; + break; + } + + return UPM_SUCCESS; +} + +void bmm150_uninstall_isr(const bmm150_context dev, + BMM150_INTERRUPT_PINS_T intr) +{ + assert(dev != NULL); + + switch (intr) + { + case BMM150_INTERRUPT_INT: + if (dev->gpioINT) + { + mraa_gpio_isr_exit(dev->gpioINT); + mraa_gpio_close(dev->gpioINT); + dev->gpioINT = NULL; + } + break; + + case BMM150_INTERRUPT_DR: + if (dev->gpioDR) + { + mraa_gpio_isr_exit(dev->gpioDR); + mraa_gpio_close(dev->gpioDR); + dev->gpioDR = NULL; + } + break; + } +} diff --git a/src/bmm150/bmm150.cxx b/src/bmm150/bmm150.cxx new file mode 100644 index 00000000..3cfbd599 --- /dev/null +++ b/src/bmm150/bmm150.cxx @@ -0,0 +1,201 @@ +/* + * Author: Jon Trulson + * Copyright (c) 2016-2017 Intel Corporation. + * + * The MIT License + * + * 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. + */ + +#include +#include +#include +#include +#include + +#include "bmm150.hpp" + +using namespace upm; +using namespace std; + +BMM150::BMM150(int bus, int addr, int cs) : + m_bmm150(bmm150_init(bus, addr, cs)) +{ + if (!m_bmm150) + throw std::runtime_error(string(__FUNCTION__) + + ": bmm150_init() failed"); +} + +BMM150::~BMM150() +{ + bmm150_close(m_bmm150); +} + +void BMM150::init(BMM150_USAGE_PRESETS_T usage) +{ + if (bmm150_devinit(m_bmm150, usage)) + throw std::runtime_error(string(__FUNCTION__) + + ": bmm150_devinit() failed"); +} + +void BMM150::update() +{ + if (bmm150_update(m_bmm150)) + throw std::runtime_error(string(__FUNCTION__) + + ": bmm150_update() failed"); +} + +uint8_t BMM150::readReg(uint8_t reg) +{ + return bmm150_read_reg(m_bmm150, reg); +} + +int BMM150::readRegs(uint8_t reg, uint8_t *buffer, int len) +{ + int rv = bmm150_read_regs(m_bmm150, reg, buffer, len); + if (rv < 0) + throw std::runtime_error(string(__FUNCTION__) + + ": bmm150_read_regs() failed"); + + return rv; +} + +void BMM150::writeReg(uint8_t reg, uint8_t val) +{ + if (bmm150_write_reg(m_bmm150, reg, val)) + throw std::runtime_error(string(__FUNCTION__) + + ": bmm150_write_reg() failed"); +} + +uint8_t BMM150::getChipID() +{ + return bmm150_get_chip_id(m_bmm150); +} + +void BMM150::getMagnetometer(float *x, float *y, float *z) +{ + bmm150_get_magnetometer(m_bmm150, x, y, z); +} + +std::vector BMM150::getMagnetometer() +{ + float v[3]; + + getMagnetometer(&v[0], &v[1], &v[2]); + return std::vector(v, v+3); +} + +void BMM150::reset() +{ + if (bmm150_reset(m_bmm150)) + throw std::runtime_error(string(__FUNCTION__) + + ": bmm150_reset() failed"); +} + +void BMM150::setOutputDataRate(BMM150_DATA_RATE_T odr) +{ + if (bmm150_set_output_data_rate(m_bmm150, odr)) + throw std::runtime_error(string(__FUNCTION__) + + ": bmm150_set_output_data_rate() failed"); +} + +void BMM150::setPowerBit(bool power) +{ + if (bmm150_set_power_bit(m_bmm150, power)) + throw std::runtime_error(string(__FUNCTION__) + + ": bmm150_set_set_power_bit() failed"); +} + +void BMM150::setOpmode(BMM150_OPERATION_MODE_T opmode) +{ + if (bmm150_set_opmode(m_bmm150, opmode)) + throw std::runtime_error(string(__FUNCTION__) + + ": bmm150_set_opmode() failed"); +} + +BMM150_OPERATION_MODE_T BMM150::getOpmode() +{ + return bmm150_get_opmode(m_bmm150); +} + +uint8_t BMM150::getInterruptEnable() +{ + return bmm150_get_interrupt_enable(m_bmm150); +} + +void BMM150::setInterruptEnable(uint8_t bits) +{ + if (bmm150_set_interrupt_enable(m_bmm150, bits)) + throw std::runtime_error(string(__FUNCTION__) + + ": bmm150_set_interrupt_enable() failed"); +} + +uint8_t BMM150::getInterruptConfig() +{ + return bmm150_get_interrupt_config(m_bmm150); +} + +void BMM150::setInterruptConfig(uint8_t bits) +{ + if (bmm150_set_interrupt_config(m_bmm150, bits)) + throw std::runtime_error(string(__FUNCTION__) + + ": bmm150_set_interrupt_config() failed"); +} + +uint8_t BMM150::getInterruptStatus() +{ + return bmm150_get_interrupt_status(m_bmm150); +} + +void BMM150::setRepetitionsXY(uint8_t reps) +{ + if (bmm150_set_repetitions_xy(m_bmm150, reps)) + throw std::runtime_error(string(__FUNCTION__) + + ": bmm150_set_repetitions_xy() failed"); +} + +void BMM150::setRepetitionsZ(uint8_t reps) +{ + if (bmm150_set_repetitions_z(m_bmm150, reps)) + throw std::runtime_error(string(__FUNCTION__) + + ": bmm150_set_repetitions_z() failed"); +} + +void BMM150::setPresetMode(BMM150_USAGE_PRESETS_T usage) +{ + if (bmm150_set_preset_mode(m_bmm150, usage)) + throw std::runtime_error(string(__FUNCTION__) + + ": bmm150_set_preset_mode() failed"); +} + +void BMM150::installISR(BMM150_INTERRUPT_PINS_T intr, int gpio, + mraa::Edge level, + void (*isr)(void *), void *arg) +{ + if (bmm150_install_isr(m_bmm150, intr, gpio, (mraa_gpio_edge_t) level, + isr, arg)) + throw std::runtime_error(string(__FUNCTION__) + + ": bmm150_install_isr() failed"); +} + +void BMM150::uninstallISR(BMM150_INTERRUPT_PINS_T intr) +{ + bmm150_uninstall_isr(m_bmm150, intr); +} diff --git a/src/bmm150/bmm150.h b/src/bmm150/bmm150.h new file mode 100644 index 00000000..4d5ec00c --- /dev/null +++ b/src/bmm150/bmm150.h @@ -0,0 +1,374 @@ +/* + * Author: Jon Trulson + * Copyright (c) 2017 Intel Corporation. + * + * The MIT License + * + * 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. + */ +#pragma once + +#include +#include + +#include +#include +#include + +#include "upm.h" + +#include "bmm150_defs.h" + +#ifdef __cplusplus +extern "C" { +#endif + + /** + * @file bmm150.h + * @library bmm150 + * @brief C API for the bmm150 driver + * + * @include bmm150.c + */ + + /** + * Device context + */ + typedef struct _bmm150_context { + mraa_i2c_context i2c; + mraa_spi_context spi; + mraa_gpio_context gpioCS; // SPI CS pin + mraa_gpio_context gpioINT; // intr + mraa_gpio_context gpioDR; // DR (Data Ready) intr + + // using SPI? + bool isSPI; + BMM150_OPERATION_MODE_T opmode; + + // mag data + float magX; + float magY; + float magZ; + + // hall resistance (it is NOT futile!) + uint16_t hall; + + // trimming data + int8_t dig_x1; + int8_t dig_y1; + + int16_t dig_z4; + int8_t dig_x2; + int8_t dig_y2; + + int16_t dig_z2; + uint16_t dig_z1; + uint16_t dig_xyz1; + int16_t dig_z3; + int8_t dig_xy2; + uint8_t dig_xy1; + } *bmm150_context; + + + /** + * BMM150 initialization. + * + * This device can support both I2C and SPI. For SPI, set the addr + * to -1, and specify a positive integer representing the Chip + * Select (CS) pin for the cs argument. If you are using a + * hardware CS pin (like edison with arduino breakout), then you + * can connect the proper pin to the hardware CS pin on your MCU + * and supply -1 for cs. The default operating mode is I2C. + * + * @param bus I2C or SPI bus to use. + * @param addr The address for this device, or -1 for SPI. + * @param cs The gpio pin to use for the SPI Chip Select. Use -1 + * for I2C or for SPI with a hardware controlled pin. + * @return The device context, or NULL if an error occurred. + */ + bmm150_context bmm150_init(int bus, int addr, int cs); + + /** + * BMM150 Destructor. + * + * @param dev The device context. + */ + void bmm150_close(bmm150_context dev); + + /** + * Update the internal stored values from sensor data. + * + * @param dev The device context. + * @return UPM result. + */ + upm_result_t bmm150_update(const bmm150_context dev); + + /** + * Return the chip ID. + * + * @param dev The device context. + * @return The chip ID. + */ + uint8_t bmm150_get_chip_id(const bmm150_context dev); + + /** + * Return magnetometer data in micro-Teslas (uT). update() must + * have been called prior to calling this method. + * + * @param dev The device context. + * @param x Pointer to a floating point value that will have the + * current x component placed into it. + * @param y Pointer to a floating point value that will have the + * current y component placed into it. + * @param z Pointer to a floating point value that will have the + * current z component placed into it. + */ + void bmm150_get_magnetometer(const bmm150_context dev, + float *x, float *y, float *z); + + /** + * Initialize the device and start operation. This function is + * called from the constructor so it will not typically need to be + * called by a user unless the device is reset. This method will + * call bmm150_set_preset_mode() with the passed parameter. + * + * @param dev The device context. + * @param usage One of the BMM150_USAGE_PRESETS_T values. The + * default is BMM150_USAGE_HIGH_ACCURACY. + * @return UPM result. + */ + upm_result_t bmm150_devinit(const bmm150_context dev, + BMM150_USAGE_PRESETS_T usage); + + /** + * Set one of the Bosch recommended preset modes. These modes + * configure the sensor for varying use cases. + * + * @param dev The device context. + * @param usage One of the BMM150_USAGE_PRESETS_T values. The + * default set at initilaization time is + * BMM150_USAGE_HIGH_ACCURACY. + * @return UPM result. + */ + upm_result_t bmm150_set_preset_mode(const bmm150_context dev, + BMM150_USAGE_PRESETS_T usage); + + /** + * Perform a device soft-reset. The device will be placed in + * SUSPEND mode afterward with all configured setting lost, so + * some re-initialization will be required to get data from the + * sensor. Calling bmm150_devinit() will get everything running + * again. + * + * @param dev The device context. + * @return UPM result. + */ + upm_result_t bmm150_reset(const bmm150_context dev); + + /** + * Set the magnetometer Output Data Rate. See the datasheet for + * details. + * + * @param dev The device context. + * @param odr One of the BMM150_DATA_RATE_T values. + * @return UPM result. + */ + upm_result_t bmm150_set_output_data_rate(const bmm150_context dev, + BMM150_DATA_RATE_T odr); + + /** + * Set or clear the Power bit. When the power bit is cleared, the + * device enters a deep suspend mode where only the + * BMM150_REG_POWER_CTRL register can be accessed. This bit needs + * to be enabled for the device to operate. See the datasheet for + * details. The constructor enables this by default. After a + * deep suspend mode has been entered, all configured data is lost + * and the device must be reconfigured (as via bmm150_devinit()). + * + * @param dev The device context. + * @param power True to enable the bit, false otherwise. + * @return UPM result. + */ + upm_result_t bmm150_set_power_bit(const bmm150_context dev, bool power); + + /** + * Set the operating mode of the device. See the datasheet for + * details. + * + * @param dev The device context. + * @param power One of the BMM150_POWER_MODE_T values. + * @return UPM result. + */ + upm_result_t bmm150_set_opmode(const bmm150_context dev, + BMM150_OPERATION_MODE_T opmode); + + /** + * Get the current operating mode of the device. See the datasheet for + * details. The power bit must be one for this method to succeed. + * + * @param dev The device context. + * @return One of the BMM150_OPERATION_MODE_T values. + */ + BMM150_OPERATION_MODE_T bmm150_get_opmode(const bmm150_context dev); + + /** + * Return the Interrupt Enables register. This register + * allows you to enable various interrupt conditions. See the + * datasheet for details. + * + * @param dev The device context. + * @return A bitmask of BMM150_INT_EN_BITS_T bits. + */ + uint8_t bmm150_get_interrupt_enable(const bmm150_context dev); + + /** + * Set the Interrupt Enables register. See the datasheet for + * details. + * + * @param dev The device context. + * @param bits A bitmask of BMM150_INT_EN_BITS_T bits. + * @return UPM result. + */ + upm_result_t bmm150_set_interrupt_enable(const bmm150_context dev, + uint8_t bits); + + /** + * Return the Interrupt Config register. This register allows + * determining the electrical characteristics of the 2 interrupt + * pins (open-drain/push-pull and level/edge triggering) as well + * as other options. See the datasheet for details. + * + * @param dev The device context. + * @return A bitmask of BMM150_INT_CONFIG_BITS_T bits. + */ + uint8_t bmm150_get_interrupt_config(const bmm150_context dev); + + /** + * Set the Interrupt Config register. This register + * allows determining the electrical characteristics of the 2 + * interrupt pins (open-drain/push-pull and level/edge + * triggering). See the datasheet for details. + * + * @param dev The device context. + * @param bits A bitmask of BMM150_INT_CONFIG_BITS_T bits. + * @return UPM result. + */ + upm_result_t bmm150_set_interrupt_config(const bmm150_context dev, + uint8_t bits); + + /** + * Return the interrupt status register. This register + * indicates which interrupts have been triggered. See the + * datasheet for details. + * + * @param dev The device context. + * @return a bitmask of BMM150_INT_STATUS_BITS_T bits. + */ + uint8_t bmm150_get_interrupt_status(const bmm150_context dev); + + /** + * Set the repetition counter for the X and Y axes. This allows the + * device to average a number of measurements for a more stable + * output. See the datasheet for details. + * + * @param dev The device context. + * @param reps A coefficient for specifying the number of + * repititions to perform. (1 + 2(reps)) + * @return UPM result. + */ + upm_result_t bmm150_set_repetitions_xy(const bmm150_context dev, + uint8_t reps); + + /** + * Set the repetition counter for the Z axis. This allows the + * device to average a number of measurements for a more stable + * output. See the datasheet for details. + * + * @param dev The device context. + * @param reps A coefficient for specifying the number of + * repititions to perform. (1 + (reps)) + * @return UPM result. + */ + upm_result_t bmm150_set_repetitions_z(const bmm150_context dev, + uint8_t reps); + + /** + * Install an interrupt handler. + * + * @param dev The device context. + * @param intr One of the BMM150_INTERRUPT_PINS_T values + * specifying which interrupt pin you are installing. + * @param gpio GPIO pin to use as interrupt pin. + * @param level The interrupt trigger level (one of the + * mraa_gpio_edge_t values). Make sure that you have configured + * the interrupt pin properly for whatever level you choose. + * @param isr The interrupt handler, accepting a void * argument + * @param arg The argument to pass the the interrupt handler. + * @return UPM result. + */ + upm_result_t bmm150_install_isr(const bmm150_context dev, + BMM150_INTERRUPT_PINS_T intr, + int gpio, mraa_gpio_edge_t level, + void (*isr)(void *), void *arg); + + /** + * Uninstall a previously installed interrupt handler. + * + * @param dev The device context. + * @param intr One of the BMM150_INTERRUPT_PINS_T values + * specifying which interrupt pin you are removing. + */ + void bmm150_uninstall_isr(const bmm150_context dev, + BMM150_INTERRUPT_PINS_T intr); + + /** + * Read a register. + * + * @param dev The device context. + * @param reg The register to read. + * @return The value of the register. + */ + uint8_t bmm150_read_reg(const bmm150_context dev, uint8_t reg); + + /** + * Read contiguous registers into a buffer. + * + * @param dev The device context. + * @param buffer The buffer to store the results. + * @param len The number of registers to read. + * @return The number of bytes read, or -1 on error. + */ + int bmm150_read_regs(const bmm150_context dev, uint8_t reg, + uint8_t *buffer, int len); + + /** + * Write to a register. + * + * @param dev The device context. + * @param reg The register to write to. + * @param val The value to write. + * @return UPM result. + */ + upm_result_t bmm150_write_reg(const bmm150_context dev, + uint8_t reg, uint8_t val); + +#ifdef __cplusplus +} +#endif diff --git a/src/bmm150/bmm150.hpp b/src/bmm150/bmm150.hpp new file mode 100644 index 00000000..fcad76d9 --- /dev/null +++ b/src/bmm150/bmm150.hpp @@ -0,0 +1,350 @@ +/* + * Author: Jon Trulson + * Copyright (c) 2016-2017 Intel Corporation. + * + * The MIT License + * + * 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. + */ +#pragma once + +#include +#include + +#include +#include "bmm150.h" + +namespace upm { + + /** + * @library bmx050 + * @sensor bmm150 + * @comname 3-axis Geomagnetic Sensor + * @altname bmm050 + * @type compass + * @man bosch + * @con i2c spi gpio + * @web https://www.bosch-sensortec.com/bst/products/all_products/bmm150 + * + * @brief API for the BMM150 3-Axis Geomagnetic Sensor + * + * The BMM150 is a standalone geomagnetic sensor for consumer market + * applications. It allows measurements of the magnetic field in + * three perpendicular axes. Based on Bosch's proprietary FlipCore + * technology, performance and features of BMM150 are carefully + * tuned and perfectly match the demanding requirements of all + * 3-axis mobile applications such as electronic compass, navigation + * or augmented reality. + * + * An evaluation circuitry (ASIC) converts the output of the + * geomagnetic sensor to digital results which can be read out over + * the industry standard digital interfaces (SPI and I2C). + * + * Not all functionality of this chip has been implemented in this + * driver, however all the pieces are present to add any desired + * functionality. This driver supports both I2C (default) and SPI + * operation. + * + * This device requires 3.3v operation. + * + * @snippet bmm150.cxx Interesting + */ + + class BMM150 { + public: + /** + * BMM150 constructor. + * + * This device can support both I2C and SPI. For SPI, set the addr + * to -1, and specify a positive integer representing the Chip + * Select (CS) pin for the cs argument. If you are using a + * hardware CS pin (like edison with arduino breakout), then you + * can connect the proper pin to the hardware CS pin on your MCU + * and supply -1 for cs. The default operating mode is I2C. + * + * @param bus I2C or SPI bus to use. + * @param addr The address for this device. -1 for SPI. + * @param cs The gpio pin to use for the SPI Chip Select. -1 for + * I2C or for SPI with a hardware controlled pin. + * + * @throws std::runtime_error on failure. + */ + BMM150(int bus=BMM150_DEFAULT_I2C_BUS, + int addr=BMM150_DEFAULT_ADDR, + int cs=-1); + + /** + * BMM150 Destructor. + */ + ~BMM150(); + + /** + * Update the internal stored values from sensor data. + * + * @throws std::runtime_error on failure. + */ + void update(); + + /** + * Return the chip ID. + * + * @return The chip ID. + */ + uint8_t getChipID(); + + /** + * Return magnetometer data in micro-Teslas (uT). update() must + * have been called prior to calling this method. + * + * @param x Pointer to a floating point value that will have the + * current x component placed into it. + * @param y Pointer to a floating point value that will have the + * current y component placed into it. + * @param z Pointer to a floating point value that will have the + * current z component placed into it. + */ + void getMagnetometer(float *x, float *y, float *z); + + /** + * Return magnetometer data in micro-Teslas (uT) in the form + * of a floating point vector. update() must have been called + * prior to calling this method. + * + * @return A floating point vector containing x, y, and z in + * that order. + */ + std::vector getMagnetometer(); + + /** + * Initialize the device and start operation. This function is + * called from the constructor so will not typically need to be + * called by a user unless the device is reset. This method will + * call setPresetMode() with the passed parameter. + * + * @param usage One of the BMM150_USAGE_PRESETS_T values. The + * default is BMM150_USAGE_HIGH_ACCURACY. + * @throws std::runtime_error on failure. + */ + void init(BMM150_USAGE_PRESETS_T usage=BMM150_USAGE_HIGH_ACCURACY); + + /** + * Set one of the Bosch recommended preset modes. These modes + * configure the sensor for varying use cases. + * + * @param usage One of the BMM150_USAGE_PRESETS_T values. The + * default is BMM150_USAGE_HIGH_ACCURACY. + * + * @throws std::runtime_error on failure. + */ + void setPresetMode(BMM150_USAGE_PRESETS_T usage); + + /** + * Perform a device soft-reset. The device will be placed in + * SUSPEND mode afterward with all configured setting lost, so + * some re-initialization will be required to get data from the + * sensor. Calling init() will get everything running again. + * + * @throws std::runtime_error on failure. + */ + void reset(); + + /** + * Set the magnetometer Output Data Rate. See the datasheet for + * details. + * + * @param odr One of the BMM150_DATA_RATE_T values. + * @throws std::runtime_error on failure. + */ + void setOutputDataRate(BMM150_DATA_RATE_T odr); + + /** + * Set or clear the Power bit. When the power bit is cleared, the + * device enters a deep suspend mode where only the REG_POWER_CTRL + * register can be accessed. This bit needs to be enabled for the + * device to operate. See the datasheet for details. The + * constructor enables this by default. After a deep suspend mode + * has been entered, all configured data is lost and the device + * must be reconfigured (as via init()). + * + * @param power true to enable the bit, false otherwise. + * @throws std::runtime_error on failure. + */ + void setPowerBit(bool power); + + /** + * Set the operating mode of the device. See the datasheet for + * details. + * + * @param power One of the BMM150_POWER_MODE_T values. + * @throws std::runtime_error on failure. + */ + void setOpmode(BMM150_OPERATION_MODE_T opmode); + + /** + * Get the current operating mode of the device. See the datasheet for + * details. The power bit must be one for this method to succeed. + * + * @return One of the BMM150_OPERATION_MODE_T values. + */ + BMM150_OPERATION_MODE_T getOpmode(); + + /** + * Return the Interrupt Enables register. This register + * allows you to enable various interrupt conditions. See the + * datasheet for details. + * + * @return A bitmask of BMM150_INT_EN_BITS_T bits. + */ + uint8_t getInterruptEnable(); + + /** + * Set the Interrupt Enables register. See the datasheet for + * details. + * + * @param bits A bitmask of BMM150_INT_EN_BITS_T bits. + * @throws std::runtime_error on failure. + */ + void setInterruptEnable(uint8_t bits); + + /** + * Return the Interrupt Config register. This register allows + * determining the electrical characteristics of the 2 interrupt + * pins (open-drain/push-pull and level/edge triggering) as well + * as other options. See the datasheet for details. + * + * @return A bitmask of BMM150_INT_CONFIG_BITS_T bits. + */ + uint8_t getInterruptConfig(); + + /** + * Set the Interrupt Config register. This register + * allows determining the electrical characteristics of the 2 + * interrupt pins (open-drain/push-pull and level/edge + * triggering). See the datasheet for details. + * + * @param bits A bitmask of BMM150_INT_CONFIG_BITS_T bits. + * @throws std::runtime_error on failure. + */ + void setInterruptConfig(uint8_t bits); + + /** + * Return the interrupt status register. This register + * indicates which interrupts have been triggered. See the + * datasheet for details. + * + * @return a bitmask of BMM150_INT_STATUS_BITS_T bits. + */ + uint8_t getInterruptStatus(); + + /** + * Set the repetition counter for the X and Y axes. This allows the + * device to average a number of measurements for a more stable + * output. See the datasheet for details. + * + * @param reps A coefficient for specifying the number of + * repititions to perform. (1 + 2(reps)) + * @throws std::runtime_error on failure. + */ + void setRepetitionsXY(uint8_t reps); + + /** + * Set the repetition counter for the Z axis. This allows the + * device to average a number of measurements for a more stable + * output. See the datasheet for details. + * + * @param reps A coefficient for specifying the number of + * repititions to perform. (1 + (reps)) + * @throws std::runtime_error on failure. + */ + void setRepetitionsZ(uint8_t reps); + +#if defined(SWIGJAVA) || defined(JAVACALLBACK) + void installISR(BMM150_INTERRUPT_PINS_T intr, int gpio, + mraa::Edge level, + jobject runnable) + { + installISR(intr, gpio, level, mraa_java_isr_callback, runnable); + } +#else + /** + * Install an interrupt handler. + * + * @param intr One of the BMM150_INTERRUPT_PINS_T values + * specifying which interrupt pin you are installing. + * @param gpio GPIO pin to use as interrupt pin. + * @param level The interrupt trigger level (one of mraa::Edge + * values). Make sure that you have configured the interrupt pin + * properly for whatever level you choose. + * @param isr The interrupt handler, accepting a void * argument. + * @param arg The argument to pass the the interrupt handler. + * @throws std::runtime_error on failure. + */ + void installISR(BMM150_INTERRUPT_PINS_T intr, int gpio, + mraa::Edge level, + void (*isr)(void *), void *arg); +#endif + + /** + * Uninstall a previously installed interrupt handler. + * + * @param intr One of the BMM150_INTERRUPT_PINS_T values + * specifying which interrupt pin you are removing. + */ + void uninstallISR(BMM150_INTERRUPT_PINS_T intr); + + /** + * Read a register. + * + * @param reg The register to read. + * @return The value of the register. + */ + uint8_t readReg(uint8_t reg); + + /** + * Read contiguous registers into a buffer. + * + * @param buffer The buffer to store the results. + * @param len The number of registers to read. + * @return The number of bytes read. + * @throws std::runtime_error on failure. + */ + int readRegs(uint8_t reg, uint8_t *buffer, int len); + + /** + * Write to a register. + * + * @param reg The register to write to. + * @param val The value to write. + * @throws std::runtime_error on failure. + */ + void writeReg(uint8_t reg, uint8_t val); + + protected: + bmm150_context m_bmm150; + + private: + // Adding a private function definition for java bindings +#if defined(SWIGJAVA) || defined(JAVACALLBACK) + void installISR(BMM150_INTERRUPT_PINS_T intr, int gpio, + mraa::Edge level, + void (*isr)(void *), void *arg); +#endif + }; +} diff --git a/src/bmm150/bmm150_defs.h b/src/bmm150/bmm150_defs.h new file mode 100644 index 00000000..76db9f1b --- /dev/null +++ b/src/bmm150/bmm150_defs.h @@ -0,0 +1,272 @@ +/* + * Author: Jon Trulson + * Copyright (c) 2017 Intel Corporation. + * + * The MIT License + * + * 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. + */ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#define BMM150_DEFAULT_I2C_BUS 0 +#define BMM150_DEFAULT_SPI_BUS 0 +#define BMM150_DEFAULT_ADDR 0x10 + +#define BMM150_DEFAULT_CHIPID 0x32 + + // NOTE: Reserved registers must not be written into. Reading + // from them may return indeterminate values. Registers + // containing reserved bitfields must be written as 0. Reading + // reserved bitfields may return indeterminate values. + + /** + * BMM150 registers + */ + typedef enum { + BMM150_REG_CHIP_ID = 0x40, + + // 0x41 reserved + + BMM150_REG_MAG_X_LSB = 0x42, + BMM150_REG_MAG_X_MSB = 0x43, + BMM150_REG_MAG_Y_LSB = 0x44, + BMM150_REG_MAG_Y_MSB = 0x45, + BMM150_REG_MAG_Z_LSB = 0x46, + BMM150_REG_MAG_Z_MSB = 0x47, + + BMM150_REG_RHALL_LSB = 0x48, + BMM150_REG_RHALL_MSB = 0x49, + + BMM150_REG_INT_STATUS = 0x4a, + + BMM150_REG_POWER_CTRL = 0x4b, + + BMM150_REG_OPMODE = 0x4c, + + BMM150_REG_INT_EN = 0x4d, + BMM150_REG_INT_CONFIG = 0x4e, + + BMM150_REG_LOW_THRES = 0x4f, + BMM150_REG_HIGH_THRES = 0x50, + + BMM150_REG_REP_XY = 0x51, + BMM150_REG_REP_Z = 0x52, + + // 0x53-0x71 reserved (mostly) + + // TRIM registers from Bosch BMM050 driver + BMM150_REG_TRIM_DIG_X1 = 0x5d, + BMM150_REG_TRIM_DIG_Y1 = 0x5e, + + BMM150_REG_TRIM_DIG_Z4_LSB = 0x62, + BMM150_REG_TRIM_DIG_Z4_MSB = 0x63, + BMM150_REG_TRIM_DIG_X2 = 0x64, + BMM150_REG_TRIM_DIG_Y2 = 0x65, + + BMM150_REG_TRIM_DIG_Z2_LSB = 0x68, + BMM150_REG_TRIM_DIG_Z2_MSB = 0x69, + BMM150_REG_TRIM_DIG_Z1_LSB = 0x6a, + BMM150_REG_TRIM_DIG_Z1_MSB = 0x6b, + BMM150_REG_TRIM_DIG_XYZ1_LSB = 0x6c, + BMM150_REG_TRIM_DIG_XYZ1_MSB = 0x6d, + BMM150_REG_TRIM_DIG_Z3_LSB = 0x6e, + BMM150_REG_TRIM_DIG_Z3_MSB = 0x6f, + BMM150_REG_TRIM_DIG_XY2 = 0x70, + BMM150_REG_TRIM_DIG_XY1 = 0x71 + } BMM150_REGS_T; + + /** + * REG_MAG_XY_LSB bits (for X and Y mag data LSB's only) + */ + typedef enum { + _BMM150_MAG_XY_LSB_RESERVED_BITS = 0x02 | 0x04, + + BMM150_MAG_XY_LSB_SELFTEST_XY = 0x01, + + BMM150_MAG_XY_LSB_LSB0 = 0x08, + BMM150_MAG_XY_LSB_LSB1 = 0x10, + BMM150_MAG_XY_LSB_LSB2 = 0x20, + BMM150_MAG_XY_LSB_LSB3 = 0x40, + BMM150_MAG_XY_LSB_LSB4 = 0x80, + _BMM150_MAG_XY_LSB_LSB_MASK = 31, + _BMM150_MAG_XY_LSB_LSB_SHIFT = 3 + } BMM150_MAG_XY_LSB_BITS_T; + + /** + * REG_MAG_Z_LSB bits (for Z LSB only) + */ + typedef enum { + BMM150_MAG_Z_LSB_SELFTEST_Z = 0x01, + + BMM150_MAG_Z_LSB_LSB0 = 0x02, + BMM150_MAG_Z_LSB_LSB1 = 0x04, + BMM150_MAG_Z_LSB_LSB2 = 0x08, + BMM150_MAG_Z_LSB_LSB3 = 0x10, + BMM150_MAG_Z_LSB_LSB4 = 0x20, + BMM150_MAG_Z_LSB_LSB5 = 0x40, + BMM150_MAG_Z_LSB_LSB6 = 0x80, + _BMM150_MAG_Z_LSB_LSB_MASK = 127, + _BMM150_MAG_Z_LSB_LSB_SHIFT = 1 + } MAG_Z_LSB_BITS_T; + + /** + * REG_MAG_RHALL_LSB bits (for RHALL LSB only) + */ + typedef enum { + _BMM150_MAG_RHALL_LSB_RESERVED_BITS = 0x02, + + BMM150_MAG_RHALL_LSB_DATA_READY_STATUS = 0x01, + + BMM150_MAG_RHALL_LSB_LSB0 = 0x04, + BMM150_MAG_RHALL_LSB_LSB1 = 0x08, + BMM150_MAG_RHALL_LSB_LSB2 = 0x10, + BMM150_MAG_RHALL_LSB_LSB3 = 0x20, + BMM150_MAG_RHALL_LSB_LSB4 = 0x40, + BMM150_MAG_RHALL_LSB_LSB5 = 0x80, + _BMM150_MAG_RHALL_LSB_LSB_MASK = 63, + _BMM150_MAG_RHALL_LSB_LSB_SHIFT = 2 + } BMM150_MAG_RHALL_LSB_BITS_T; + + /** + * REG_INT_STATUS bits + */ + typedef enum { + BMM150_INT_STATUS_LOW_INT_X = 0x01, + BMM150_INT_STATUS_LOW_INT_Y = 0x02, + BMM150_INT_STATUS_LOW_INT_Z = 0x04, + BMM150_INT_STATUS_HIGH_INT_X = 0x08, + BMM150_INT_STATUS_HIGH_INT_Y = 0x10, + BMM150_INT_STATUS_HIGH_INT_Z = 0x20, + BMM150_INT_STATUS_OVERFLOW = 0x40, + BMM150_INT_STATUS_DATA_OVERRUN = 0x80 + } BMM150_INT_STATUS_BITS_T; + + /** + * REG_POWER_CTRL bits + */ + typedef enum { + _BMM150_POWER_CTRL_RESERVED_BITS = 0x40 | 0x20 | 0x10 | 0x08, + + BMM150_POWER_CTRL_POWER_CTRL_BIT = 0x01, + BMM150_POWER_CTRL_SOFT_RESET0 = 0x02, + BMM150_POWER_CTRL_SPI3EN = 0x04, // not supported + + BMM150_POWER_CTRL_SOFT_RESET1 = 0x80 + } POWER_CTRL_BITS_T; + + /** + * REG_OPMODE bits + */ + typedef enum { + BMM150_OPMODE_SELFTTEST = 0x01, + + BMM150_OPMODE_OPERATION_MODE0 = 0x02, + BMM150_OPMODE_OPERATION_MODE1 = 0x04, + _BMM150_OPMODE_OPERATION_MODE_MASK = 3, + _BMM150_OPMODE_OPERATION_MODE_SHIFT = 1, + + BMM150_OPMODE_DATA_RATE0 = 0x08, + BMM150_OPMODE_DATA_RATE1 = 0x10, + BMM150_OPMODE_DATA_RATE2 = 0x20, + _BMM150_OPMODE_DATA_RATE_MASK = 7, + _BMM150_OPMODE_DATA_RATE_SHIFT = 3, + + BMM150_OPMODE_ADV_SELFTEST0 = 0x40, + BMM150_OPMODE_ADV_SELFTEST1 = 0x80, + _BMM150_OPMODE_ADV_SELFTEST_MASK = 3, + _BMM150_OPMODE_ADV_SELFTEST_SHIFT = 6 + } OPMODE_BITS_T; + + /** + * OPMODE_OPERATION_MODE values + */ + typedef enum { + BMM150_OPERATION_MODE_NORMAL = 0, + BMM150_OPERATION_MODE_FORCED = 1, + BMM150_OPERATION_MODE_SLEEP = 3 + } BMM150_OPERATION_MODE_T; + + /** + * OPMODE_DATA_RATE values + */ + typedef enum { + BMM150_DATA_RATE_10HZ = 0, + BMM150_DATA_RATE_2HZ = 1, + BMM150_DATA_RATE_6HZ = 2, + BMM150_DATA_RATE_8HZ = 3, + BMM150_DATA_RATE_15HZ = 4, + BMM150_DATA_RATE_20HZ = 5, + BMM150_DATA_RATE_25HZ = 6, + BMM150_DATA_RATE_30HZ = 7 + } BMM150_DATA_RATE_T; + + /** + * REG_INT_EN bits + */ + typedef enum { + BMM150_INT_EN_LOW_INT_X_EN = 0x01, + BMM150_INT_EN_LOW_INT_Y_EN = 0x02, + BMM150_INT_EN_LOW_INT_Z_EN = 0x04, + BMM150_INT_EN_HIGH_INT_X_EN = 0x08, + BMM150_INT_EN_HIGH_INT_Y_EN = 0x10, + BMM150_INT_EN_HIGH_INT_Z_EN = 0x20, + BMM150_INT_EN_OVERFLOW_INT_EN = 0x40, + BMM150_INT_EN_DATA_OVERRUN_INT_EN = 0x80 + } BMM150_INT_EN_T; + + /** + * REG_INT_CONFIG bits + */ + typedef enum { + BMM150_INT_CONFIG_INT_POLARITY = 0x01, + BMM150_INT_CONFIG_INT_LATCH = 0x02, + BMM150_INT_CONFIG_DR_POLARITY = 0x04, + BMM150_INT_CONFIG_CHANNEL_X = 0x08, + BMM150_INT_CONFIG_CHANNEL_Y = 0x10, + BMM150_INT_CONFIG_CHANNEL_Z = 0x20, + BMM150_INT_CONFIG_INT_PIN_EN = 0x40, + BMM150_INT_CONFIG_DR_PIN_EN = 0x80 + } BMM150_INT_CONFIG_T; + + /** + * Interrupt selection for installISR() and uninstallISR() + */ + typedef enum { + BMM150_INTERRUPT_INT, + BMM150_INTERRUPT_DR + } BMM150_INTERRUPT_PINS_T; + + /** + * Bosch recommended usage preset modes + */ + typedef enum { + BMM150_USAGE_LOW_POWER, + BMM150_USAGE_REGULAR, + BMM150_USAGE_ENHANCED_REGULAR, + BMM150_USAGE_HIGH_ACCURACY + } BMM150_USAGE_PRESETS_T; + +#ifdef __cplusplus +} +#endif diff --git a/src/bmm150/bmm150_fti.c b/src/bmm150/bmm150_fti.c new file mode 100644 index 00000000..e1a4ae76 --- /dev/null +++ b/src/bmm150/bmm150_fti.c @@ -0,0 +1,106 @@ +/* + * Author: Jon Trulson + * Copyright (c) 2017 Intel Corporation. + * + * 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. + */ + +#include "bmm150.h" +#include "upm_fti.h" + +/** + * This file implements the Function Table Interface (FTI) for this sensor + */ + +const char upm_bmm150_name[] = "BMM150"; +const char upm_bmm150_description[] = "Triple Axis Digital Magnetometer"; +const upm_protocol_t upm_bmm150_protocol[] = {UPM_I2C, UPM_SPI, UPM_GPIO}; +const upm_sensor_t upm_bmm150_category[] = {UPM_MAGNETOMETER}; + +// forward declarations +const void* upm_bmm150_get_ft(upm_sensor_t sensor_type); +void* upm_bmm150_init_name(); +void upm_bmm150_close(void *dev); +upm_result_t upm_bmm150_get_value(void *dev, float *value); + +const upm_sensor_descriptor_t upm_bmm150_get_descriptor() +{ + upm_sensor_descriptor_t usd; + usd.name = upm_bmm150_name; + usd.description = upm_bmm150_description; + usd.protocol_size = 3; + usd.protocol = upm_bmm150_protocol; + usd.category_size = 1; + usd.category = upm_bmm150_category; + return usd; +} + +static const upm_sensor_ft ft = +{ + .upm_sensor_init_name = &upm_bmm150_init_name, + .upm_sensor_close = &upm_bmm150_close, +}; + +static const upm_magnetometer_ft mft = +{ + .upm_magnetometer_get_value = &upm_bmm150_get_value +}; + +const void* upm_bmm150_get_ft(upm_sensor_t sensor_type) +{ + switch(sensor_type) + { + case UPM_SENSOR: + return &ft; + + case UPM_MAGNETOMETER: + return &mft; + + default: + return NULL; + } +} + +void* upm_bmm150_init_name() +{ + return NULL; +} + + +void upm_bmm150_close(void *dev) +{ + bmm150_close((bmm150_context)dev); +} + +upm_result_t upm_bmm150_get_value(void *dev, float *value) +{ + if (bmm150_update((bmm150_context)dev)) + return UPM_ERROR_OPERATION_FAILED; + + float x, y, z; + + bmm150_get_magnetometer(dev, &x, &y, &z); + + value[0] = x; + value[1] = y; + value[2] = z; + + return UPM_SUCCESS; +} diff --git a/src/bmm150/javaupm_bmm150.i b/src/bmm150/javaupm_bmm150.i new file mode 100644 index 00000000..d9d49b56 --- /dev/null +++ b/src/bmm150/javaupm_bmm150.i @@ -0,0 +1,23 @@ +%module javaupm_bmm150 +%include "../upm.i" +%include "typemaps.i" +%include "../upm_vectortypes.i" + +%ignore getMagnetometer(float *, float *, float *); + +%include "bmm150_defs.h" +%include "bmm150.hpp" +%{ + #include "bmm150.hpp" +%} + +%pragma(java) jniclasscode=%{ + static { + try { + System.loadLibrary("javaupm_bmm150"); + } catch (UnsatisfiedLinkError e) { + System.err.println("Native code library failed to load. \n" + e); + System.exit(1); + } + } +%} diff --git a/src/bmm150/jsupm_bmm150.i b/src/bmm150/jsupm_bmm150.i new file mode 100644 index 00000000..234e07df --- /dev/null +++ b/src/bmm150/jsupm_bmm150.i @@ -0,0 +1,14 @@ +%module jsupm_bmm150 +%include "../upm.i" +%include "cpointer.i" +%include "../upm_vectortypes.i" + +/* Send "int *" and "float *" to JavaScript as intp and floatp */ +%pointer_functions(int, intp); +%pointer_functions(float, floatp); + +%include "bmm150_defs.h" +%include "bmm150.hpp" +%{ + #include "bmm150.hpp" +%} diff --git a/src/bmm150/license.txt b/src/bmm150/license.txt new file mode 100644 index 00000000..07a89c5e --- /dev/null +++ b/src/bmm150/license.txt @@ -0,0 +1,56 @@ +// The trimming algorithms used (bmm050_bosch_compensate_*()) were +// taken from the Bosch BMM050 driver code + +/**************************************************************************** +* Copyright (C) 2015 - 2016 Bosch Sensortec GmbH +* +* File : bmm050.h +* +* Date : 2016/03/17 +* +* Revision : 2.0.5 $ +* +* Usage: Sensor Driver for BMM050 and BMM150 sensor +* +**************************************************************************** +* +* section License +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* +* Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* +* Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* +* Neither the name of the copyright holder nor the names of the +* contributors may be used to endorse or promote products derived from +* this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND +* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER +* OR CONTRIBUTORS BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, +* OR CONSEQUENTIAL DAMAGES(INCLUDING, BUT NOT LIMITED TO, +* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +* ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE +* +* The information provided is believed to be accurate and reliable. +* The copyright holder assumes no responsibility +* for the consequences of use +* of such information nor for any infringement of patents or +* other rights of third parties which may result from its use. +* No license is granted by implication or otherwise under any patent or +* patent rights of the copyright holder. +**************************************************************************/ diff --git a/src/bmm150/pyupm_bmm150.i b/src/bmm150/pyupm_bmm150.i new file mode 100644 index 00000000..d1608d03 --- /dev/null +++ b/src/bmm150/pyupm_bmm150.i @@ -0,0 +1,22 @@ +// Include doxygen-generated documentation +%include "pyupm_doxy2swig.i" +%module pyupm_bmm150 +%include "../upm.i" +%include "cpointer.i" +%include "../upm_vectortypes.i" + +/* Send "int *" and "float *" to python as intp and floatp */ +%pointer_functions(int, intp); +%pointer_functions(float, floatp); + +%feature("autodoc", "3"); + +#ifdef DOXYGEN +%include "bmm150_doc.i" +#endif + +%include "bmm150_defs.h" +%include "bmm150.hpp" +%{ + #include "bmm150.hpp" +%}