diff --git a/examples/c++/lsm303agr.cxx b/examples/c++/lsm303agr.cxx new file mode 100644 index 00000000..21435ecd --- /dev/null +++ b/examples/c++/lsm303agr.cxx @@ -0,0 +1,87 @@ +/* + * 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 "lsm303agr.hpp" + +using namespace std; + +int shouldRun = true; + +void sig_handler(int signo) +{ + if (signo == SIGINT) + shouldRun = false; +} + + +int main(int argc, char **argv) +{ + signal(SIGINT, sig_handler); +//! [Interesting] + + // Instantiate an LSM303AGR using default I2C parameters + upm::LSM303AGR sensor; + + // now output data every 250 milliseconds + while (shouldRun) + { + float x, y, z; + + sensor.update(); + + sensor.getAccelerometer(&x, &y, &z); + cout << "Accelerometer x: " << x + << " y: " << y + << " z: " << z + << " g" + << endl; + + sensor.getMagnetometer(&x, &y, &z); + cout << "Magnetometer x: " << x + << " y: " << y + << " z: " << z + << " uT" + << endl; + + cout << "Temperature: " + << sensor.getTemperature() + << " C" + << endl; + + cout << endl; + + usleep(250000); + } + +//! [Interesting] + + cout << "Exiting..." << endl; + + return 0; +} diff --git a/examples/c/lsm303agr.c b/examples/c/lsm303agr.c new file mode 100644 index 00000000..91920bbb --- /dev/null +++ b/examples/c/lsm303agr.c @@ -0,0 +1,90 @@ +/* + * 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 "lsm303agr.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] + + // Instantiate a LSM303AGR instance using default i2c bus and addresses + lsm303agr_context sensor = lsm303agr_init(LSM303AGR_DEFAULT_I2C_BUS, + LSM303AGR_DEFAULT_ACC_ADDR, + LSM303AGR_DEFAULT_MAG_ADDR); + + if (!sensor) + { + printf("lsm303agr_init() failed.\n"); + return 1; + } + + // now output data every 250 milliseconds + while (shouldRun) + { + float x, y, z; + + if (lsm303agr_update(sensor)) + { + printf("lsm303agr_update() failed\n"); + return 1; + } + + lsm303agr_get_accelerometer(sensor, &x, &y, &z); + printf("Accelerometer x: %f y: %f z: %f g\n", + x, y, z); + + lsm303agr_get_magnetometer(sensor, &x, &y, &z); + printf("Magnetometer x: %f y: %f z: %f uT\n", + x, y, z); + + printf("Temperature: %f C\n\n", lsm303agr_get_temperature(sensor)); + + upm_delay_ms(250); + } + + printf("Exiting...\n"); + + lsm303agr_close(sensor); + +//! [Interesting] + + return 0; +} diff --git a/examples/java/CMakeLists.txt b/examples/java/CMakeLists.txt index ea827b8d..05400ff2 100644 --- a/examples/java/CMakeLists.txt +++ b/examples/java/CMakeLists.txt @@ -169,6 +169,7 @@ add_example(P9813Sample p9813) add_example(BMG160_Example bmg160) add_example(BMA250E_Example bma250e) add_example(BMM150_Example bmm150) +add_example(LSM303AGR_Example lsm303agr) add_example_with_path(Jhd1313m1_lcdSample jhd1313m1 jhd1313m1) add_example_with_path(Jhd1313m1Sample jhd1313m1 jhd1313m1) diff --git a/examples/java/LSM303AGR_Example.java b/examples/java/LSM303AGR_Example.java new file mode 100644 index 00000000..f3355f46 --- /dev/null +++ b/examples/java/LSM303AGR_Example.java @@ -0,0 +1,65 @@ +/* + * 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. + */ + +import upm_lsm303agr.LSM303AGR; + +public class LSM303AGR_Example +{ + public static void main(String[] args) throws InterruptedException + { +// ! [Interesting] + + // Instantiate a LSM303AGR instance using default i2c bus and address + LSM303AGR sensor = new LSM303AGR(); + + while (true) + { + // update our values from the sensor + sensor.update(); + + upm_lsm303agr.floatVector data = sensor.getAccelerometer(); + + System.out.println("Accelerometer x: " + data.get(0) + + " y: " + data.get(1) + + " z: " + data.get(2) + + " g"); + + data = sensor.getMagnetometer(); + System.out.println("Magnetometer x: " + data.get(0) + + " y: " + data.get(1) + + " z: " + data.get(2) + + " uT"); + + System.out.println("Temperature: " + + sensor.getTemperature()); + + System.out.println(); + Thread.sleep(250); + } + +// ! [Interesting] + } +} diff --git a/examples/javascript/lsm303agr.js b/examples/javascript/lsm303agr.js new file mode 100644 index 00000000..540abe9e --- /dev/null +++ b/examples/javascript/lsm303agr.js @@ -0,0 +1,67 @@ +/* + * 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. + */ + +var sensorObj = require('jsupm_lsm303agr'); + +// Instantiate a LSM303AGR instance using default i2c bus and address +var sensor = new sensorObj.LSM303AGR(); + +// now output data every 250 milliseconds +setInterval(function() +{ + // update our values from the sensor + sensor.update(); + + var data = sensor.getAccelerometer(); + console.log("Accelerometer x: " + + data.get(0) + + " y: " + data.get(1) + + " z: " + data.get(2) + + " g"); + + data = sensor.getMagnetometer(); + console.log("Magnetometer x: " + + data.get(0) + + " y: " + data.get(1) + + " z: " + data.get(2) + + " uT"); + + console.log("Temperature: " + + sensor.getTemperature()); + + console.log(); + +}, 250); + +// exit on ^C +process.on('SIGINT', function() +{ + sensor = null; + sensorObj.cleanUp(); + sensorObj = null; + console.log("Exiting."); + process.exit(0); +}); diff --git a/examples/python/lsm303agr.py b/examples/python/lsm303agr.py new file mode 100755 index 00000000..65ccbd7a --- /dev/null +++ b/examples/python/lsm303agr.py @@ -0,0 +1,71 @@ +#!/usr/bin/python +# 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. + +from __future__ import print_function +import time, sys, signal, atexit +from upm import pyupm_lsm303agr as sensorObj + +def main(): + # Instantiate a BMP250E instance using default i2c bus and address + sensor = sensorObj.LSM303AGR() + + ## Exit handlers ## + # This function stops python from printing a stacktrace when you + # hit control-C + def SIGINTHandler(signum, frame): + raise SystemExit + + # This function lets you run code on exit + def exitHandler(): + print("Exiting") + sys.exit(0) + + # Register exit handlers + atexit.register(exitHandler) + signal.signal(signal.SIGINT, SIGINTHandler) + + # now output data every 250 milliseconds + while (1): + sensor.update() + + data = sensor.getAccelerometer() + print("Accelerometer x:", data[0], end=' ') + print(" y:", data[1], end=' ') + print(" z:", data[2], end=' ') + print(" g") + + data = sensor.getMagnetometer() + print("Magnetometer x:", data[0], end=' ') + print(" y:", data[1], end=' ') + print(" z:", data[2], end=' ') + print(" uT") + + print("Temperature: ", sensor.getTemperature()) + + print() + time.sleep(.250) + +if __name__ == '__main__': + main() diff --git a/src/lsm303agr/CMakeLists.txt b/src/lsm303agr/CMakeLists.txt new file mode 100644 index 00000000..8f8fdbf9 --- /dev/null +++ b/src/lsm303agr/CMakeLists.txt @@ -0,0 +1,9 @@ +upm_mixed_module_init (NAME lsm303agr + DESCRIPTION "3-Axis eCompass module" + C_HDR lsm303agr.h lsm303agr_defs.h + C_SRC lsm303agr.c + CPP_HDR lsm303agr.hpp + CPP_SRC lsm303agr.cxx + FTI_SRC lsm303agr_fti.c + CPP_WRAPS_C + REQUIRES mraa) diff --git a/src/lsm303agr/javaupm_lsm303agr.i b/src/lsm303agr/javaupm_lsm303agr.i new file mode 100644 index 00000000..64eb37bd --- /dev/null +++ b/src/lsm303agr/javaupm_lsm303agr.i @@ -0,0 +1,24 @@ +%module javaupm_lsm303agr +%include "../upm.i" +%include "typemaps.i" +%include "../upm_vectortypes.i" + +%ignore getMagnetometer(float *, float *, float *); +%ignore getAccelerometer(float *, float *, float *); + +%include "lsm303agr_defs.h" +%include "lsm303agr.hpp" +%{ + #include "lsm303agr.hpp" +%} + +%pragma(java) jniclasscode=%{ + static { + try { + System.loadLibrary("javaupm_lsm303agr"); + } catch (UnsatisfiedLinkError e) { + System.err.println("Native code library failed to load. \n" + e); + System.exit(1); + } + } +%} diff --git a/src/lsm303agr/jsupm_lsm303agr.i b/src/lsm303agr/jsupm_lsm303agr.i new file mode 100644 index 00000000..2802da1a --- /dev/null +++ b/src/lsm303agr/jsupm_lsm303agr.i @@ -0,0 +1,9 @@ +%module jsupm_lsm303agr +%include "../upm.i" +%include "../upm_vectortypes.i" + +%include "lsm303agr_defs.h" +%include "lsm303agr.hpp" +%{ + #include "lsm303agr.hpp" +%} diff --git a/src/lsm303agr/lsm303agr.c b/src/lsm303agr/lsm303agr.c new file mode 100644 index 00000000..04c87766 --- /dev/null +++ b/src/lsm303agr/lsm303agr.c @@ -0,0 +1,781 @@ +/* + * 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 "lsm303agr.h" + +// init +lsm303agr_context lsm303agr_init(int bus, int acc_addr, int mag_addr) +{ + if (acc_addr <= 0 && mag_addr <= 0) + { + printf("%s: At least one device must be enabled\n", __FUNCTION__); + return NULL; + } + + lsm303agr_context dev = + (lsm303agr_context)malloc(sizeof(struct _lsm303agr_context)); + + if (!dev) + return NULL; + + // zero out context + memset((void *)dev, 0, sizeof(struct _lsm303agr_context)); + + // make sure MRAA is initialized + if (mraa_init() != MRAA_SUCCESS) + { + printf("%s: mraa_init() failed.\n", __FUNCTION__); + lsm303agr_close(dev); + return NULL; + } + + if (acc_addr > 0) + { + if (!(dev->i2cACC = mraa_i2c_init(bus))) + { + printf("%s: mraa_i2c_init(acc) failed.\n", __FUNCTION__); + lsm303agr_close(dev); + return NULL; + } + + if (mraa_i2c_address(dev->i2cACC, acc_addr)) + { + printf("%s: mraa_i2c_address(acc) failed.\n", __FUNCTION__); + lsm303agr_close(dev); + return NULL; + } + + // check the chip id + uint8_t chipID = lsm303agr_read_reg(dev, LSM303AGR_REG_WHO_AM_I_A); + + if (chipID != LSM303AGR_CHIPID_ACC) + { + printf("%s: invalid accelerometer chip id: %02x. Expected %02x\n", + __FUNCTION__, chipID, LSM303AGR_CHIPID_ACC); + lsm303agr_close(dev); + return NULL; + } + } + + // technically we could use a single i2c context since it is bus + // specific, but then we would need to call i2c_address() every + // time we wanted to talk to a specific device. In addition, we + // can use the i2c context pointer to determine if a subsystem + // (acc or mag) is actually enabled throughout this driver. + if (mag_addr > 0) + { + if (!(dev->i2cMAG = mraa_i2c_init(bus))) + { + printf("%s: mraa_i2c_init(mag) failed.\n", __FUNCTION__); + lsm303agr_close(dev); + return NULL; + } + + if (mraa_i2c_address(dev->i2cMAG, mag_addr)) + { + printf("%s: mraa_i2c_address(mag) failed.\n", __FUNCTION__); + lsm303agr_close(dev); + return NULL; + } + + // check the chip id + uint8_t chipID = lsm303agr_read_reg(dev, LSM303AGR_REG_WHO_AM_I_M); + + if (chipID != LSM303AGR_CHIPID_MAG) + { + printf("%s: invalid accelerometer chip id: %02x. Expected %02x\n", + __FUNCTION__, chipID, LSM303AGR_CHIPID_MAG); + lsm303agr_close(dev); + return NULL; + } + } + + // call devinit with a default high resolution mode + if (lsm303agr_devinit(dev, LSM303AGR_POWER_HIGH_RESOLUTION)) + { + printf("%s: lsm303agr_devinit() failed.\n", __FUNCTION__); + lsm303agr_close(dev); + return NULL; + } + + return dev; +} + +void lsm303agr_close(lsm303agr_context dev) +{ + assert(dev != NULL); + + lsm303agr_uninstall_isr(dev, LSM303AGR_INTERRUPT_ACC_1); + lsm303agr_uninstall_isr(dev, LSM303AGR_INTERRUPT_ACC_2); + lsm303agr_uninstall_isr(dev, LSM303AGR_INTERRUPT_MAG); + + if (dev->i2cACC) + mraa_i2c_stop(dev->i2cACC); + if (dev->i2cMAG) + mraa_i2c_stop(dev->i2cMAG); + + free(dev); +} + +upm_result_t lsm303agr_devinit(const lsm303agr_context dev, + LSM303AGR_POWER_MODE_T mode) +{ + assert(dev != NULL); + + // magnetometer + if (dev->i2cMAG) + { + // enable temp compensation and continuous mode + uint8_t reg = lsm303agr_read_reg(dev, LSM303AGR_REG_CFG_REG_A_M); + + reg &= ~_LSM303AGR_SHIFTMASK(CFG_REG_A_M_MD); + reg |= LSM303AGR_CFG_REG_A_M_COMP_TEMP_EN; + reg |= (LSM303AGR_CFG_A_M_MD_CONTINUOUS + << _LSM303AGR_SHIFT(CFG_REG_A_M_MD)); + + if (lsm303agr_write_reg(dev, LSM303AGR_REG_CFG_REG_A_M, reg)) + { + printf("%s: lsm303agr_write_reg() failed.\n", __FUNCTION__); + return UPM_ERROR_OPERATION_FAILED; + } + + // set MAG ODR to 10Hz by default + if (lsm303agr_set_mag_odr(dev, LSM303AGR_CFG_A_M_ODR_10HZ)) + { + printf("%s: lsm303agr_set_mag_odr() failed.\n", __FUNCTION__); + return UPM_ERROR_OPERATION_FAILED; + } + + upm_delay_ms(10); + } + + // accelerometer + if (dev->i2cACC) + { + // enable all axes + uint8_t reg = lsm303agr_read_reg(dev, LSM303AGR_REG_CTRL_REG1_A); + + reg |= LSM303AGR_CTRL_REG1_A_XEN + | LSM303AGR_CTRL_REG1_A_YEN + | LSM303AGR_CTRL_REG1_A_ZEN; + + if (lsm303agr_write_reg(dev, LSM303AGR_REG_CTRL_REG1_A, reg)) + { + printf("%s: lsm303agr_write_reg() failed.\n", __FUNCTION__); + return UPM_ERROR_OPERATION_FAILED; + } + + // enable BDU + reg = lsm303agr_read_reg(dev, LSM303AGR_REG_CTRL_REG4_A); + reg |= LSM303AGR_CTRL_REG4_A_BDU; + + if (lsm303agr_write_reg(dev, LSM303AGR_REG_CTRL_REG4_A, reg)) + { + printf("%s: lsm303agr_write_reg() failed.\n", __FUNCTION__); + return UPM_ERROR_OPERATION_FAILED; + } + + // enable temperature measurement + reg = lsm303agr_read_reg(dev, LSM303AGR_REG_TEMP_CFG_REG_A); + reg &= ~_LSM303AGR_SHIFTMASK(TEMP_CFG_REG_A_TEMP_EN); + reg |= (LSM303AGR_TEMP_EN_ON + << _LSM303AGR_SHIFT(TEMP_CFG_REG_A_TEMP_EN)); + + if (lsm303agr_write_reg(dev, LSM303AGR_REG_TEMP_CFG_REG_A, reg)) + { + printf("%s: lsm303agr_write_reg() failed.\n", __FUNCTION__); + return UPM_ERROR_OPERATION_FAILED; + } + + // set ACC ODR to 100Hz by default + if (lsm303agr_set_acc_odr(dev, LSM303AGR_A_ODR_100HZ)) + { + printf("%s: lsm303agr_set_acc_odr() failed.\n", __FUNCTION__); + return UPM_ERROR_OPERATION_FAILED; + } + + // default to 2G sensitivity + if (lsm303agr_set_full_scale(dev, LSM303AGR_A_FS_2G)) + { + printf("%s: lsm303agr_set_full_scale() failed.\n", __FUNCTION__); + return UPM_ERROR_OPERATION_FAILED; + } + + upm_delay_ms(10); + } + + if (lsm303agr_set_power_mode(dev, mode)) + { + printf("%s: lsm303agr_set_power_mode() failed.\n", __FUNCTION__); + return UPM_ERROR_OPERATION_FAILED; + } + + return UPM_SUCCESS; +} + +upm_result_t lsm303agr_set_power_mode(const lsm303agr_context dev, + LSM303AGR_POWER_MODE_T mode) +{ + assert(dev != NULL); + + // magnetometer + if (dev->i2cMAG) + { + uint8_t reg = lsm303agr_read_reg(dev, LSM303AGR_REG_CFG_REG_A_M); + + // only low power or hires supported here + if (mode == LSM303AGR_POWER_LOW_POWER) + reg |= LSM303AGR_CFG_REG_A_M_LP; + else + reg &= ~LSM303AGR_CFG_REG_A_M_LP; + + if (lsm303agr_write_reg(dev, LSM303AGR_REG_CFG_REG_A_M, reg)) + return UPM_ERROR_OPERATION_FAILED; + } + + // accelerometer + if (dev->i2cACC) + { + uint8_t reg1 = lsm303agr_read_reg(dev, LSM303AGR_REG_CTRL_REG1_A); + uint8_t reg4 = lsm303agr_read_reg(dev, LSM303AGR_REG_CTRL_REG4_A); + + switch (mode) + { + case LSM303AGR_POWER_LOW_POWER: + reg1 |= LSM303AGR_CTRL_REG1_A_LPEN; + reg4 &= ~LSM303AGR_CTRL_REG4_A_HR; + break; + + case LSM303AGR_POWER_NORMAL: + reg1 &= ~LSM303AGR_CTRL_REG1_A_LPEN; + reg4 &= ~LSM303AGR_CTRL_REG4_A_HR; + break; + + case LSM303AGR_POWER_HIGH_RESOLUTION: + reg1 &= ~LSM303AGR_CTRL_REG1_A_LPEN; + reg4 |= LSM303AGR_CTRL_REG4_A_HR; + break; + } + + if (lsm303agr_write_reg(dev, LSM303AGR_REG_CTRL_REG1_A, reg1)) + return UPM_ERROR_OPERATION_FAILED; + + if (lsm303agr_write_reg(dev, LSM303AGR_REG_CTRL_REG4_A, reg4)) + return UPM_ERROR_OPERATION_FAILED; + } + + // settle + upm_delay_ms(10); + + dev->powerMode = mode; + + return UPM_SUCCESS; +} + +upm_result_t lsm303agr_set_full_scale(const lsm303agr_context dev, + LSM303AGR_A_FS_T fs) +{ + assert(dev != NULL); + + // this only affects the accelerometer + if (dev->i2cACC) + { + uint8_t reg = lsm303agr_read_reg(dev, LSM303AGR_REG_CTRL_REG4_A); + + reg &= ~_LSM303AGR_SHIFTMASK(CTRL_REG4_A_FS); + reg |= (fs << _LSM303AGR_SHIFT(CTRL_REG4_A_FS)); + + if (lsm303agr_write_reg(dev, LSM303AGR_REG_CTRL_REG4_A, reg)) + return UPM_ERROR_OPERATION_FAILED; + + upm_delay_ms(50); + + // set our scaling factor depending on current power mode and + // FS + switch(dev->powerMode) + { + case LSM303AGR_POWER_LOW_POWER: + // 8b resolution + dev->accDivisor = 256.0; + + switch (fs) + { + case LSM303AGR_A_FS_2G: + dev->accScale = 15.63; + break; + + case LSM303AGR_A_FS_4G: + dev->accScale = 31.26; + break; + + case LSM303AGR_A_FS_8G: + dev->accScale = 62.52; + break; + + case LSM303AGR_A_FS_16G: + dev->accScale = 187.58; + break; + } + break; + + case LSM303AGR_POWER_NORMAL: + // 10b resolution + dev->accDivisor = 64.0; + + switch (fs) + { + case LSM303AGR_A_FS_2G: + dev->accScale = 3.9; + break; + + case LSM303AGR_A_FS_4G: + dev->accScale = 7.82; + break; + + case LSM303AGR_A_FS_8G: + dev->accScale = 15.63; + break; + + case LSM303AGR_A_FS_16G: + dev->accScale = 46.9; + break; + } + break; + + case LSM303AGR_POWER_HIGH_RESOLUTION: + // 12b resolution + dev->accDivisor = 16.0; + + switch (fs) + { + case LSM303AGR_A_FS_2G: + dev->accScale = 0.98; + break; + + case LSM303AGR_A_FS_4G: + dev->accScale = 1.95; + break; + + case LSM303AGR_A_FS_8G: + dev->accScale = 3.9; + break; + + case LSM303AGR_A_FS_16G: + dev->accScale = 11.72; + break; + } + break; + } + } + + return UPM_SUCCESS; +} + +upm_result_t lsm303agr_update(const lsm303agr_context dev) +{ + assert(dev != NULL); + + const int maxLen = 6; + uint8_t buf[maxLen]; + + if (dev->i2cACC) + { + // get the temperature first, only 2 bytes + if (lsm303agr_read_regs(dev, LSM303AGR_REG_OUT_TEMP_L_A, buf, 2) != 2) + { + printf("%s: lsm303agr_read_regs(temp) failed.\n", __FUNCTION__); + return UPM_ERROR_OPERATION_FAILED; + } + + dev->temperature = (float)((int16_t)(buf[0] | (buf[1] << 8))); + + // next, acc data + if (lsm303agr_read_regs(dev, LSM303AGR_REG_OUT_X_L_A, buf, + maxLen) != maxLen) + { + printf("%s: lsm303agr_read_regs(acc) failed.\n", __FUNCTION__); + return UPM_ERROR_OPERATION_FAILED; + } + + dev->accX = (float)((int16_t)(buf[0] | (buf[1] << 8))); + dev->accY = (float)((int16_t)(buf[2] | (buf[3] << 8))); + dev->accZ = (float)((int16_t)(buf[4] | (buf[5] << 8))); + } + + if (dev->i2cMAG) + { + // now mag data + if (lsm303agr_read_regs(dev,LSM303AGR_REG_OUTX_L_REG_M, buf, + maxLen) != maxLen) + { + printf("%s: lsm303agr_read_regs(mag) failed.\n", __FUNCTION__); + return UPM_ERROR_OPERATION_FAILED; + } + + dev->magX = (float)((int16_t)(buf[0] | (buf[1] << 8))); + dev->magY = (float)((int16_t)(buf[2] | (buf[3] << 8))); + dev->magZ = (float)((int16_t)(buf[4] | (buf[5] << 8))); + } + + return UPM_SUCCESS; +} + +uint8_t lsm303agr_read_reg(const lsm303agr_context dev, uint8_t reg) +{ + assert(dev != NULL); + + mraa_i2c_context i2c = NULL; + if (reg <= LSM303AGR_MAX_ACC_ADDR) + i2c = dev->i2cACC; + else + i2c = dev->i2cMAG; + + if (i2c) + { + int rv = mraa_i2c_read_byte_data(i2c, reg); + if (rv < 0) + { + printf("%s: mraa_i2c_read_byte_data() failed\n", __FUNCTION__); + return 0xff; + } + return (uint8_t)rv; + } + else // shouldn't happen, but... + return 0xff; +} + +int lsm303agr_read_regs(const lsm303agr_context dev, uint8_t reg, + uint8_t *buffer, int len) +{ + assert(dev != NULL); + + mraa_i2c_context i2c = NULL; + if (reg <= LSM303AGR_MAX_ACC_ADDR) + i2c = dev->i2cACC; + else + i2c = dev->i2cMAG; + + if (i2c) + { + reg |= 0x80; // enable auto-increment + if (mraa_i2c_read_bytes_data(i2c, reg, buffer, len) != len) + return -1; + } + else + return -1; + + return len; +} + +upm_result_t lsm303agr_write_reg(const lsm303agr_context dev, + uint8_t reg, uint8_t val) +{ + assert(dev != NULL); + + mraa_i2c_context i2c = NULL; + if (reg <= LSM303AGR_MAX_ACC_ADDR) + i2c = dev->i2cACC; + else + i2c = dev->i2cMAG; + + if (i2c) + { + if (mraa_i2c_write_byte_data(i2c, val, reg)) + { + printf("%s: mraa_i2c_write_byte_data() failed.\n", + __FUNCTION__); + return UPM_ERROR_OPERATION_FAILED; + } + } + else + return UPM_ERROR_NO_RESOURCES; + + return UPM_SUCCESS; +} + +float lsm303agr_get_temperature(const lsm303agr_context dev) +{ + assert(dev != NULL); + + // DS says 8 bits. It is not clear in the DS how to compute this, + // but the following seems to produce a reasonably correct + // temperature. + return (dev->temperature / 256.0) + 25.0; +} + +void lsm303agr_get_magnetometer(const lsm303agr_context dev, + float *x, float *y, float *z) +{ + assert(dev != NULL); + + // 1.5 comes from the datasheet. Output is in milli-Gauss - we + // convert and return it in uT (SI micro-teslas) instead. + if (x) + *x = (dev->magX * 1.5) / 10.0; + if (y) + *y = (dev->magY * 1.5) / 10.0; + if (z) + *z = (dev->magZ * 1.5) / 10.0; +} + +void lsm303agr_get_accelerometer(const lsm303agr_context dev, + float *x, float *y, float *z) +{ + assert(dev != NULL); + + if (x) + *x = ((dev->accX / dev->accDivisor) * dev->accScale) / 1000.0; + if (y) + *y = ((dev->accY / dev->accDivisor) * dev->accScale) / 1000.0; + if (z) + *z = ((dev->accZ / dev->accDivisor) * dev->accScale) / 1000.0; +} + +upm_result_t lsm303agr_set_acc_odr(const lsm303agr_context dev, + LSM303AGR_A_ODR_T odr) +{ + assert(dev != NULL); + + if (!dev->i2cACC) + return UPM_ERROR_NO_RESOURCES; + + uint8_t reg = lsm303agr_read_reg(dev, LSM303AGR_REG_CTRL_REG1_A); + reg &= ~_LSM303AGR_SHIFTMASK(CTRL_REG1_A_ODR); + reg |= (odr << _LSM303AGR_SHIFT(CTRL_REG1_A_ODR)); + + if (lsm303agr_write_reg(dev, LSM303AGR_REG_CTRL_REG1_A, reg)) + return UPM_ERROR_OPERATION_FAILED; + + return UPM_SUCCESS; +} + +upm_result_t lsm303agr_set_mag_odr(const lsm303agr_context dev, + LSM303AGR_CFG_A_M_ODR_T odr) +{ + assert(dev != NULL); + + if (!dev->i2cMAG) + return UPM_ERROR_NO_RESOURCES; + + uint8_t reg = lsm303agr_read_reg(dev, LSM303AGR_REG_CFG_REG_A_M); + reg &= ~_LSM303AGR_SHIFTMASK(CFG_REG_A_M_ODR); + reg |= (odr << _LSM303AGR_SHIFT(CFG_REG_A_M_ODR)); + + if (lsm303agr_write_reg(dev, LSM303AGR_REG_CFG_REG_A_M, reg)) + return UPM_ERROR_OPERATION_FAILED; + + return UPM_SUCCESS; +} + +uint8_t lsm303agr_get_acc_int1_config(const lsm303agr_context dev) +{ + assert(dev != NULL); + + if (!dev->i2cACC) + return 0; + + return lsm303agr_read_reg(dev, LSM303AGR_REG_INT1_CFG_A); +} + +upm_result_t lsm303agr_set_acc_int1_config(const lsm303agr_context dev, + uint8_t bits) +{ + assert(dev != NULL); + + if (!dev->i2cACC) + return UPM_ERROR_NO_RESOURCES; + + if (lsm303agr_write_reg(dev, LSM303AGR_REG_INT1_CFG_A, bits)) + return UPM_ERROR_OPERATION_FAILED; + + return UPM_SUCCESS; +} + +uint8_t lsm303agr_get_acc_int2_config(const lsm303agr_context dev) +{ + assert(dev != NULL); + + if (!dev->i2cACC) + return 0; + + return lsm303agr_read_reg(dev, LSM303AGR_REG_INT2_CFG_A); +} + +upm_result_t lsm303agr_set_acc_int2_config(const lsm303agr_context dev, + uint8_t bits) +{ + assert(dev != NULL); + + if (!dev->i2cACC) + return UPM_ERROR_NO_RESOURCES; + + if (lsm303agr_write_reg(dev, LSM303AGR_REG_INT2_CFG_A, bits)) + return UPM_ERROR_OPERATION_FAILED; + + return UPM_SUCCESS; +} + +uint8_t lsm303agr_get_mag_int_config(const lsm303agr_context dev) +{ + assert(dev != NULL); + + if (!dev->i2cMAG) + return 0; + + return lsm303agr_read_reg(dev, LSM303AGR_REG_INT_CTRL_REG_M); +} + +upm_result_t lsm303agr_set_mag_int_config(const lsm303agr_context dev, + uint8_t bits) +{ + assert(dev != NULL); + + if (!dev->i2cMAG) + return UPM_ERROR_NO_RESOURCES; + + if (lsm303agr_write_reg(dev, LSM303AGR_REG_INT_CTRL_REG_M, bits)) + return UPM_ERROR_OPERATION_FAILED; + + return UPM_SUCCESS; +} + +uint8_t lsm303agr_get_acc_int1_src(const lsm303agr_context dev) +{ + assert(dev != NULL); + + if (!dev->i2cACC) + return 0; + + return lsm303agr_read_reg(dev, LSM303AGR_REG_INT1_SRC_A); +} + +uint8_t lsm303agr_get_acc_int2_src(const lsm303agr_context dev) +{ + assert(dev != NULL); + + if (!dev->i2cACC) + return 0; + + return lsm303agr_read_reg(dev, LSM303AGR_REG_INT2_SRC_A); +} + +uint8_t lsm303agr_get_mag_int_src(const lsm303agr_context dev) +{ + assert(dev != NULL); + + if (!dev->i2cMAG) + return 0; + + return lsm303agr_read_reg(dev, LSM303AGR_REG_INT_SRC_REG_M); +} + +upm_result_t lsm303agr_install_isr(const lsm303agr_context dev, + LSM303AGR_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 + lsm303agr_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 LSM303AGR_INTERRUPT_ACC_1: + dev->gpioACC1 = gpio_isr; + break; + + case LSM303AGR_INTERRUPT_ACC_2: + dev->gpioACC2 = gpio_isr; + break; + + case LSM303AGR_INTERRUPT_MAG: + dev->gpioMAG = gpio_isr; + break; + } + + return UPM_SUCCESS; +} + +void lsm303agr_uninstall_isr(const lsm303agr_context dev, + LSM303AGR_INTERRUPT_PINS_T intr) +{ + assert(dev != NULL); + + switch (intr) + { + case LSM303AGR_INTERRUPT_ACC_1: + if (dev->gpioACC1) + { + mraa_gpio_isr_exit(dev->gpioACC1); + mraa_gpio_close(dev->gpioACC1); + dev->gpioACC1 = NULL; + } + break; + + case LSM303AGR_INTERRUPT_ACC_2: + if (dev->gpioACC2) + { + mraa_gpio_isr_exit(dev->gpioACC2); + mraa_gpio_close(dev->gpioACC2); + dev->gpioACC2 = NULL; + } + break; + + case LSM303AGR_INTERRUPT_MAG: + if (dev->gpioMAG) + { + mraa_gpio_isr_exit(dev->gpioMAG); + mraa_gpio_close(dev->gpioMAG); + dev->gpioMAG = NULL; + } + break; + } +} diff --git a/src/lsm303agr/lsm303agr.cxx b/src/lsm303agr/lsm303agr.cxx new file mode 100644 index 00000000..74b0cbdf --- /dev/null +++ b/src/lsm303agr/lsm303agr.cxx @@ -0,0 +1,210 @@ +/* + * 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 + +#include "lsm303agr.hpp" + +using namespace upm; +using namespace std; + +LSM303AGR::LSM303AGR(int bus, int acc_addr, int mag_addr) : + m_lsm303agr(lsm303agr_init(bus, acc_addr, mag_addr)) +{ + if (!m_lsm303agr) + throw std::runtime_error(string(__FUNCTION__) + + ": lsm303agr_init() failed"); +} + +LSM303AGR::~LSM303AGR() +{ + lsm303agr_close(m_lsm303agr); +} + +void LSM303AGR::init(LSM303AGR_POWER_MODE_T usage) +{ + if (lsm303agr_devinit(m_lsm303agr, usage)) + throw std::runtime_error(string(__FUNCTION__) + + ": lsm303agr_devinit() failed"); +} + +void LSM303AGR::update() +{ + if (lsm303agr_update(m_lsm303agr)) + throw std::runtime_error(string(__FUNCTION__) + + ": lsm303agr_update() failed"); +} + +uint8_t LSM303AGR::readReg(uint8_t reg) +{ + return lsm303agr_read_reg(m_lsm303agr, reg); +} + +int LSM303AGR::readRegs(uint8_t reg, uint8_t *buffer, int len) +{ + int rv = lsm303agr_read_regs(m_lsm303agr, reg, buffer, len); + if (rv < 0) + throw std::runtime_error(string(__FUNCTION__) + + ": lsm303agr_read_regs() failed"); + + return rv; +} + +void LSM303AGR::writeReg(uint8_t reg, uint8_t val) +{ + if (lsm303agr_write_reg(m_lsm303agr, reg, val)) + throw std::runtime_error(string(__FUNCTION__) + + ": lsm303agr_write_reg() failed"); +} + +void LSM303AGR::getMagnetometer(float *x, float *y, float *z) +{ + lsm303agr_get_magnetometer(m_lsm303agr, x, y, z); +} + +std::vector LSM303AGR::getMagnetometer() +{ + float v[3]; + + getMagnetometer(&v[0], &v[1], &v[2]); + return std::vector(v, v+3); +} + +void LSM303AGR::getAccelerometer(float *x, float *y, float *z) +{ + lsm303agr_get_accelerometer(m_lsm303agr, x, y, z); +} + +std::vector LSM303AGR::getAccelerometer() +{ + float v[3]; + + getAccelerometer(&v[0], &v[1], &v[2]); + return std::vector(v, v+3); +} + +float LSM303AGR::getTemperature() +{ + return lsm303agr_get_temperature(m_lsm303agr); +} + +void LSM303AGR::setFullScale(LSM303AGR_A_FS_T fs) +{ + if (lsm303agr_set_full_scale(m_lsm303agr, fs)) + throw std::runtime_error(string(__FUNCTION__) + + ": lsm303agr_set_full_scale() failed"); +} + +void LSM303AGR::setPowerMode(LSM303AGR_POWER_MODE_T mode) +{ + if (lsm303agr_set_power_mode(m_lsm303agr, mode)) + throw std::runtime_error(string(__FUNCTION__) + + ": lsm303agr_set_power_mode() failed"); +} + +void LSM303AGR::setAccelerometerODR(LSM303AGR_A_ODR_T odr) +{ + if (lsm303agr_set_acc_odr(m_lsm303agr, odr)) + throw std::runtime_error(string(__FUNCTION__) + + ": lsm303agr_set_acc_odr() failed"); +} + +void LSM303AGR::setMagnetometerODR(LSM303AGR_CFG_A_M_ODR_T odr) +{ + if (lsm303agr_set_mag_odr(m_lsm303agr, odr)) + throw std::runtime_error(string(__FUNCTION__) + + ": lsm303agr_set_mag_odr() failed"); +} + +uint8_t LSM303AGR::getAccelerometerInt1Config() +{ + return lsm303agr_get_acc_int1_config(m_lsm303agr); +} + +uint8_t LSM303AGR::getAccelerometerInt2Config() +{ + return lsm303agr_get_acc_int2_config(m_lsm303agr); +} + +void LSM303AGR::setAccelerometerInt1Config(uint8_t bits) +{ + if (lsm303agr_set_acc_int1_config(m_lsm303agr, bits)) + throw std::runtime_error(string(__FUNCTION__) + + ": lsm303agr_set_acc_int1_config() failed"); +} + +void LSM303AGR::setAccelerometerInt2Config(uint8_t bits) +{ + if (lsm303agr_set_acc_int2_config(m_lsm303agr, bits)) + throw std::runtime_error(string(__FUNCTION__) + + ": lsm303agr_set_acc_int2_config() failed"); +} + +uint8_t LSM303AGR::getMagnetometerIntConfig() +{ + return lsm303agr_get_mag_int_config(m_lsm303agr); +} + +void LSM303AGR::setMagnetometerIntConfig(uint8_t bits) +{ + if (lsm303agr_set_mag_int_config(m_lsm303agr, bits)) + throw std::runtime_error(string(__FUNCTION__) + + ": lsm303agr_set_mag_int_config() failed"); +} + +uint8_t LSM303AGR::getAccelerometerInt1Src() +{ + return lsm303agr_get_acc_int1_src(m_lsm303agr); +} + +uint8_t LSM303AGR::getAccelerometerInt2Src() +{ + return lsm303agr_get_acc_int2_src(m_lsm303agr); +} + +uint8_t LSM303AGR::getMagnetometerIntSrc() +{ + return lsm303agr_get_mag_int_src(m_lsm303agr); +} + + +void LSM303AGR::installISR(LSM303AGR_INTERRUPT_PINS_T intr, int gpio, + mraa::Edge level, + void (*isr)(void *), void *arg) +{ + if (lsm303agr_install_isr(m_lsm303agr, intr, gpio, (mraa_gpio_edge_t)level, + isr, arg)) + throw std::runtime_error(string(__FUNCTION__) + + ": lsm303agr_install_isr() failed"); +} + +void LSM303AGR::uninstallISR(LSM303AGR_INTERRUPT_PINS_T intr) +{ + lsm303agr_uninstall_isr(m_lsm303agr, intr); +} diff --git a/src/lsm303agr/lsm303agr.h b/src/lsm303agr/lsm303agr.h new file mode 100644 index 00000000..e238a671 --- /dev/null +++ b/src/lsm303agr/lsm303agr.h @@ -0,0 +1,382 @@ +/* + * 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 "upm.h" + +#include "lsm303agr_defs.h" + +#ifdef __cplusplus +extern "C" { +#endif + + /** + * @file lsm303agr.h + * @library lsm303agr + * @brief C API for the lsm303agr driver + * + * @include lsm303agr.c + */ + + /** + * Device context + */ + typedef struct _lsm303agr_context { + mraa_i2c_context i2cACC; // accelerometer + mraa_i2c_context i2cMAG; // magnetometer + mraa_gpio_context gpioACC1; // acc intr + mraa_gpio_context gpioACC2; // acc intr + mraa_gpio_context gpioMAG; // mag intr + + // currently set power mode + LSM303AGR_POWER_MODE_T powerMode; + + // uncompensated temperature in C + float temperature; + + // accelerometer scaling, depends on full scale sensitivity and + // power mode + float accScale; + float accDivisor; + + // uncompensated acc data + float accX; + float accY; + float accZ; + + // uncompensated mag data + float magX; + float magY; + float magZ; + } *lsm303agr_context; + + + /** + * LSM303AGR initialization + * + * This driver can only support I2C. SPI requires support for + * 3-wire SPI which we cannot currently handle. Only the basic + * capabilities of the device are supported, however there is a + * full register map defined (lsm303agr_defs.h), and with access + * to the bus read/write functions, any desired additional + * functionality can be implemented. + * + * @param bus I2C bus to use + * @param acc_addr The I2C address of the accelerometer. Use -1 + * if you do not wish to use the accelerometer. + * @param mag_addr The I2C address of the magnetometer. Use -1 + * if you do not wish to use the magnetometer. + * @return The device context, or NULL if an error occurred + */ + lsm303agr_context lsm303agr_init(int bus, int acc_addr, int mag_addr); + + /** + * LSM303AGR Destructor + * + * @param dev The device context + */ + void lsm303agr_close(lsm303agr_context dev); + + /** + * Update the internal stored values from sensor data + * + * @param dev The device context + * @return UPM result + */ + upm_result_t lsm303agr_update(const lsm303agr_context dev); + + /** + * Return magnetometer data in micro-Teslas (uT). update() must + * have been called prior to calling this function. + * + * @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 lsm303agr_get_magnetometer(const lsm303agr_context dev, + float *x, float *y, float *z); + + /** + * Return accelerometer data in gravities. update() must have + * been called prior to calling this function. + * + * @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 lsm303agr_get_accelerometer(const lsm303agr_context dev, + float *x, float *y, float *z); + + /** + * Return temperature data in degrees Celsius.. update() must + * have been called prior to calling this function. + * + * @param dev The device context + * @return Temperature in degrees Celsius + */ + float lsm303agr_get_temperature(const lsm303agr_context dev); + + /** + * Set the full scale (sensitivity) value for the accelerometer. + * This device supports 2G, 4G, 8G, and 16G full scale modes. + * + * @param dev The device context + * @param fs One of the LSM303AGR_A_FS_T values + * @return UPM result + */ + upm_result_t lsm303agr_set_full_scale(const lsm303agr_context dev, + LSM303AGR_A_FS_T fs); + + /** + * 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. It will + * initialize the accelerometer and magnetometer (if enabled) to + * certain default running modes. + * + * For the accelerometer, the full scale will be set to 2G, mode + * continuous, all axes enabled, BDU enabled, temperature + * measurement enabled, and an output data rate (ODR) of 100Hz + * with the power mode set at high resolution. + * + * For the magnetometer, temperature compensation will be enabled, + * mode continuous, and an output data rate of 10Hz with the power + * mode set at high resolution. + * + * @param dev The device context + * @param usage One of the LSM303AGR_POWER_MODE_T values. This + * parameter is passed to a call to lsm303agr_set_power_mode() and + * will set the appropriate mode for both the accelerometer and + * magnetometer. The default set by the constructor is + * LSM303AGR_POWER_HIGH_RESOLUTION. + * @return UPM result + */ + upm_result_t lsm303agr_devinit(const lsm303agr_context dev, + LSM303AGR_POWER_MODE_T mode); + + /** + * Set an operating power mode. There are 3 modes available: low + * power, normal, and high resolution. + * + * @param dev The device context + * @param usage One of the LSM303AGR_POWER_MODE_T values. The + * default set at initialization time is + * LSM303AGR_POWER_HIGH_RESOLUTION. + * @return UPM result + */ + upm_result_t lsm303agr_set_power_mode(const lsm303agr_context dev, + LSM303AGR_POWER_MODE_T mode); + + /** + * Set the accelerometer (acc) output data rate (odr) + * + * @param dev The device context + * @param usage One of the LSM303AGR_A_ODR_T values. The default + * set at initialization time is LSM303AGR_A_ODR_100HZ. + * @return UPM result + */ + upm_result_t lsm303agr_set_acc_odr(const lsm303agr_context dev, + LSM303AGR_A_ODR_T odr); + + /** + * Set the magnetometer (mag) output data rate (odr) + * + * @param dev The device context + * @param usage One of the LSM303AGR_CFG_A_M_ODR_T values. The default + * set at initialization time is LSM303AGR_CFG_A_M_ODR_10HZ. + * @return UPM result + */ + upm_result_t lsm303agr_set_mag_odr(const lsm303agr_context dev, + LSM303AGR_CFG_A_M_ODR_T odr); + + /** + * Return the accelerometer (acc) interrupt 1 config register. + * This register allows you to enable various interrupt + * conditions. See the datasheet for details. + * + * @param dev The device context + * @return A bitmask of LSM303AGR_INT_CFG_A_BITS_T bits + */ + uint8_t lsm303agr_get_acc_int1_config(const lsm303agr_context dev); + + /** + * Return the accelerometer (acc) interrupt 2 config register. + * This register allows you to enable various interrupt + * conditions. See the datasheet for details. + * + * @param dev The device context + * @return A bitmask of LSM303AGR_INT_CFG_A_BITS_T bits + */ + uint8_t lsm303agr_get_acc_int2_config(const lsm303agr_context dev); + + /** + * Set the accelerometer (acc) interrupt 1 config register. See + * the datasheet for details. + * + * @param dev The device context + * @param bits A bitmask of LSM303AGR_INT_CFG_A_BITS_T bits + * @return UPM result + */ + upm_result_t lsm303agr_set_acc_int1_config(const lsm303agr_context dev, + uint8_t bits); + + /** + * Set the accelerometer (acc) interrupt 2 config register. See + * the datasheet for details. + * + * @param dev The device context + * @param bits A bitmask of LSM303AGR_INT_CFG_A_BITS_T bits + * @return UPM result + */ + upm_result_t lsm303agr_set_acc_int2_config(const lsm303agr_context dev, + uint8_t bits); + + /** + * Return the magnetometer (mag) interrupt config register. See + * the datasheet for details. + * + * @param dev The device context + * @return A bitmask of LSM303AGR_INT_CTRL_REG_M_BITS_T bits + */ + uint8_t lsm303agr_get_mag_int_config(const lsm303agr_context dev); + + /** + * Set the magnetometer (mag) interrupt config register. See + * the datasheet for details. + * + * @param dev The device context + * @param bits A bitmask of LSM303AGR_INT_CTRL_REG_M_BITS_T bits + */ + upm_result_t lsm303agr_set_mag_int_config(const lsm303agr_context dev, + uint8_t bits); + + /** + * Return the accelerometer (acc) interrupt 1 source register. + * This register indicates which interrupts have been triggered. + * See the datasheet for details. + * + * @param dev The device context + * @return a bitmask of LSM303AGR_INT_SRC_A_BITS_T bits + */ + uint8_t lsm303agr_get_acc_int1_src(const lsm303agr_context dev); + + /** + * Return the accelerometer (acc) interrupt 2 source register. + * This register indicates which interrupts have been triggered. + * See the datasheet for details. + * + * @param dev The device context + * @return a bitmask of LSM303AGR_INT_SRC_A_BITS_T bits + */ + uint8_t lsm303agr_get_acc_int2_src(const lsm303agr_context dev); + + /** + * Return the magnetometer (mag) interrupt source register. + * This register indicates which interrupts have been triggered. + * See the datasheet for details. + * + * @param dev The device context + * @return a bitmask of LSM303AGR_INT_SRC_REG_M_BITS_T bits + */ + uint8_t lsm303agr_get_mag_int_src(const lsm303agr_context dev); + + /** + * Install an interrupt handler + * + * @param dev The device context + * @param intr One of the LSM303AGR_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 lsm303agr_install_isr(const lsm303agr_context dev, + LSM303AGR_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 LSM303AGR_INTERRUPT_PINS_T values + * specifying which interrupt pin you are removing + */ + void lsm303agr_uninstall_isr(const lsm303agr_context dev, + LSM303AGR_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 lsm303agr_read_reg(const lsm303agr_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 lsm303agr_read_regs(const lsm303agr_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 lsm303agr_write_reg(const lsm303agr_context dev, + uint8_t reg, uint8_t val); + +#ifdef __cplusplus +} +#endif diff --git a/src/lsm303agr/lsm303agr.hpp b/src/lsm303agr/lsm303agr.hpp new file mode 100644 index 00000000..733ccd16 --- /dev/null +++ b/src/lsm303agr/lsm303agr.hpp @@ -0,0 +1,379 @@ +/* + * 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 "lsm303agr.h" + +namespace upm { + + /** + * @brief ST Microelectronics Ultra-compact high-performance + * eCompass module + * @defgroup lsm303agr libupm-lsm303agr + * @ingroup i2c gpio stmicro compass accelerometer + */ + + /** + * @library lsm303agr + * @sensor lsm303agr + * @comname Ultra-compact high-performance eCompass module + * @type compass + * @man stmicro + * @con i2c gpio + * @web http://www.st.com/en/mems-and-sensors/lsm303agr.html + * + * @brief API for the LSM303AGR 3-Axis Geomagnetic Sensor + * + * The LSM303AGR is an ultra-low-power high-performance + * system-in-package featuring a 3D digital linear acceleration + * sensor and a 3D digital magnetic sensor. The LSM303AGR has + * linear acceleration full scales of 2g/4g/8g/16g and a + * magnetic field dynamic range of 50 Gauss. + * + * 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 only I2C operation. + * + * This device requires 3.3v operation. + * + * @snippet lsm303agr.cxx Interesting + */ + + class LSM303AGR { + public: + /** + * LSM303AGR constructor + * + * This driver can only support I2C. SPI requires support for + * 3-wire SPI which we cannot currently handle. Only the + * basic capabilities of the device are supported, however + * there is a full register map defined (lsm303agr_defs.h), + * and with access to the bus read/write functions, any + * desired additional functionality can be implemented. + * + * @param bus I2C bus to use + * @param acc_addr The I2C address of the accelerometer. Use -1 + * if you do not wish to use the accelerometer. + * @param mag_addr The I2C address of the magnetometer. Use -1 + * if you do not wish to use the magnetometer. + * @return The device context, or NULL if an error occurred + * @throws std::runtime_error on failure. + */ + LSM303AGR(int bus=LSM303AGR_DEFAULT_I2C_BUS, + int acc_addr=LSM303AGR_DEFAULT_ACC_ADDR, + int mag_addr=LSM303AGR_DEFAULT_MAG_ADDR); + + /** + * LSM303AGR Destructor + */ + ~LSM303AGR(); + + /** + * Update the internal stored values from sensor data. This + * method must be called before querying the acceleration, + * magnetometer, or temperature. + * + * @throws std::runtime_error on failure + */ + void update(); + + /** + * 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(); + + /** + * Return acceleration data in gravities. 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 getAccelerometer(float *x, float *y, float *z); + + /** + * Return acceleration data in gravities 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 getAccelerometer(); + + /** + * Return temperature data in degrees Celsius. NOTE: This is + * not the ambient room temperature. update() must have been + * called prior to calling this method. + * + * @return Temperature in degrees Celsius + */ + float getTemperature(); + + /** + * 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. It + * will initialize the accelerometer and magnetometer (if + * enabled) to certain default running modes. + * + * For the accelerometer, the full scale will be set to 2G, + * mode continuous, all axes enabled, BDU enabled, temperature + * measurement enabled, and an output data rate (ODR) of 100Hz + * with the power mode set at high resolution. + * + * For the magnetometer, temperature compensation will be + * enabled, mode continuous, and an output data rate of 10Hz + * with the power mode set at high resolution. + * + * @param usage One of the LSM303AGR_POWER_MODE_T values. The + * default is LSM303AGR_POWER_HIGH_RESOLUTION. + * @throws std::runtime_error on failure + */ + void init(LSM303AGR_POWER_MODE_T usage=LSM303AGR_POWER_HIGH_RESOLUTION); + + /** + * Set the full scale (sensitivity) value for the accelerometer. + * This device supports 2G, 4G, 8G, and 16G full scale modes. + * + * @param fs One of the LSM303AGR_A_FS_T values + * @throws std::runtime_error on failure + */ + void setFullScale(LSM303AGR_A_FS_T fs); + + /** + * Set an operating power mode. There are 3 modes available: low + * power, normal, and high resolution. + * + * @param usage One of the LSM303AGR_POWER_MODE_T values. The + * default set at initialization time is + * LSM303AGR_POWER_HIGH_RESOLUTION. + * @throws std::runtime_error on failure + */ + void setPowerMode(LSM303AGR_POWER_MODE_T mode); + + /** + * Set the accelerometer output data rate (ODR) + * + * @param usage One of the LSM303AGR_A_ODR_T values. The default + * set at initialization time is LSM303AGR_A_ODR_100HZ. + * @throws std::runtime_error on failure + */ + void setAccelerometerODR(LSM303AGR_A_ODR_T odr); + + /** + * Set the magnetometer output data rate (ODR) + * + * @param usage One of the LSM303AGR_CFG_A_M_ODR_T values. + * The default set at initialization time is + * LSM303AGR_CFG_A_M_ODR_10HZ. + * @throws std::runtime_error on failure + */ + void setMagnetometerODR(LSM303AGR_CFG_A_M_ODR_T odr); + + /** + * Return the accelerometer interrupt 1 config register. + * This register allows you to enable various interrupt + * conditions. See the datasheet for details. + * + * @return A bitmask of LSM303AGR_INT_CFG_A_BITS_T bits + */ + uint8_t getAccelerometerInt1Config(); + + /** + * Return the accelerometer interrupt 2 config register. + * This register allows you to enable various interrupt + * conditions. See the datasheet for details. + * + * @return A bitmask of LSM303AGR_INT_CFG_A_BITS_T bits + */ + uint8_t getAccelerometerInt2Config(); + + /** + * Set the accelerometer interrupt 1 config register. See the + * datasheet for details. + * + * @param dev The device context + * @param bits A bitmask of LSM303AGR_INT_CFG_A_BITS_T bits + * @throws std::runtime_error on failure + */ + void setAccelerometerInt1Config(uint8_t bits); + + /** + * Set the accelerometer interrupt 2 config register. See the + * datasheet for details. + * + * @param dev The device context + * @param bits A bitmask of LSM303AGR_INT_CFG_A_BITS_T bits + * @throws std::runtime_error on failure + */ + void setAccelerometerInt2Config(uint8_t bits); + + /** + * Return the magnetometer interrupt config register. See the + * datasheet for details. + * + * @return A bitmask of LSM303AGR_INT_CTRL_REG_M_BITS_T bits + */ + uint8_t getMagnetometerIntConfig(); + + /** + * Set the magnetometer (mag) interrupt config register. See + * the datasheet for details. + * + * @param bits A bitmask of LSM303AGR_INT_CTRL_REG_M_BITS_T bits + * @throws std::runtime_error on failure + */ + void setMagnetometerIntConfig(uint8_t bits); + + /** + * Return the accelerometer interrupt 1 source register. This + * register indicates which interrupts have been triggered. + * See the datasheet for details. + * + * @return a bitmask of LSM303AGR_INT_SRC_A_BITS_T bits + */ + uint8_t getAccelerometerInt1Src(); + + /** + * Return the accelerometer interrupt 2 source register. This + * register indicates which interrupts have been triggered. + * See the datasheet for details. + * + * @return a bitmask of LSM303AGR_INT_SRC_A_BITS_T bits + */ + uint8_t getAccelerometerInt2Src(); + + /** + * Return the magnetometer (mag) interrupt source register. + * This register indicates which interrupts have been triggered. + * See the datasheet for details. + * + * @param dev The device context + * @return a bitmask of LSM303AGR_INT_SRC_REG_M_BITS_T bits + */ + uint8_t getMagnetometerIntSrc(); + +#if defined(SWIGJAVA) || defined(JAVACALLBACK) + void installISR(LSM303AGR_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 LSM303AGR_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(LSM303AGR_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 LSM303AGR_INTERRUPT_PINS_T values + * specifying which interrupt pin you are removing + */ + void uninstallISR(LSM303AGR_INTERRUPT_PINS_T intr); + + protected: + // our underlying device context + lsm303agr_context m_lsm303agr; + + /** + * 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); + + private: + // Adding a private function definition for java bindings +#if defined(SWIGJAVA) || defined(JAVACALLBACK) + void installISR(LSM303AGR_INTERRUPT_PINS_T intr, int gpio, + mraa::Edge level, + void (*isr)(void *), void *arg); +#endif + }; +} diff --git a/src/lsm303agr/lsm303agr_defs.h b/src/lsm303agr/lsm303agr_defs.h new file mode 100644 index 00000000..08796d83 --- /dev/null +++ b/src/lsm303agr/lsm303agr_defs.h @@ -0,0 +1,686 @@ +/* + * 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 LSM303AGR_DEFAULT_I2C_BUS 0 +#define LSM303AGR_DEFAULT_ACC_ADDR 0x19 +#define LSM303AGR_DEFAULT_MAG_ADDR 0x1e + +// from the WHO_AM_I_* registers +#define LSM303AGR_CHIPID_ACC 0x33 +#define LSM303AGR_CHIPID_MAG 0x40 + +// This device has 2 I2C addresses - one for the accelerometer (ACC) +// and one for the magnetometer (MAG). But, it uses a single register +// map. The MAG registers start at 0x40, while the ACC registers +// start at 0 and go to 0x3f. We define the cutoff point here so the +// register access functions can "automatically" choose the correct +// i2c context (MAG or ACC) to use depending on what register is +// being accessed. + +#define LSM303AGR_MAX_ACC_ADDR 0x3f + + + // NOTE: Reserved registers must not be written into or permanent + // damage to the device can result. Reading from them may return + // indeterminate values. Registers containing reserved bitfields + // must be written as 0. + + /** + * LSM303AGR registers + */ + typedef enum { + + // accelerometer registers + + // 0x00-0x06 reserved + + LSM303AGR_REG_STATUS_REG_AUX_A = 0x07, + + // 0x08-0x0b reserved + + LSM303AGR_REG_OUT_TEMP_L_A = 0x0c, + LSM303AGR_REG_OUT_TEMP_H_A = 0x0d, + + LSM303AGR_REG_INT_COUNTER_REG_A = 0x0e, + + LSM303AGR_REG_WHO_AM_I_A = 0x0f, + + // 0x10-0x1e reserved + + LSM303AGR_REG_TEMP_CFG_REG_A = 0x1f, + + LSM303AGR_REG_CTRL_REG1_A = 0x20, + LSM303AGR_REG_CTRL_REG2_A = 0x21, + LSM303AGR_REG_CTRL_REG3_A = 0x22, + LSM303AGR_REG_CTRL_REG4_A = 0x23, + LSM303AGR_REG_CTRL_REG5_A = 0x24, + LSM303AGR_REG_CTRL_REG6_A = 0x25, + + LSM303AGR_REG_REFERENCE = 0x26, + + LSM303AGR_REG_STATUS_REG_A = 0x27, + + LSM303AGR_REG_OUT_X_L_A = 0x28, + LSM303AGR_REG_OUT_X_H_A = 0x29, + LSM303AGR_REG_OUT_Y_L_A = 0x2a, + LSM303AGR_REG_OUT_Y_H_A = 0x2b, + LSM303AGR_REG_OUT_Z_L_A = 0x2c, + LSM303AGR_REG_OUT_Z_H_A = 0x2d, + + LSM303AGR_REG_FIFO_CTRL_REG_A = 0x2e, + LSM303AGR_REG_FIFO_SRC_REG_A = 0x2f, + + LSM303AGR_REG_INT1_CFG_A = 0x30, + LSM303AGR_REG_INT1_SRC_A = 0x31, + LSM303AGR_REG_INT1_THS_A = 0x32, + LSM303AGR_REG_INT1_DUR_A = 0x33, + + LSM303AGR_REG_INT2_CFG_A = 0x34, + LSM303AGR_REG_INT2_SRC_A = 0x35, + LSM303AGR_REG_INT2_THS_A = 0x36, + LSM303AGR_REG_INT2_DUR_A = 0x37, + + LSM303AGR_REG_CLICK_CFG_A = 0x38, + LSM303AGR_REG_CLICK_SRC_A = 0x39, + LSM303AGR_REG_CLICK_THS_A = 0x3a, + + LSM303AGR_REG_TIME_LIMIT = 0x3b, + LSM303AGR_REG_TIME_LATENCY_A = 0x3c, + LSM303AGR_REG_TIME_WINDOW_A = 0x3d, + + LSM303AGR_REG_ACT_THS_A = 0x3e, + LSM303AGR_REG_ACT_DUR_A = 0x3f, + + // magnetometer registers + + // 0x40-0x44 reserved + + LSM303AGR_REG_OFFSET_X_REG_L_M = 0x45, + LSM303AGR_REG_OFFSET_X_REG_H_M = 0x46, + LSM303AGR_REG_OFFSET_Y_REG_L_M = 0x47, + LSM303AGR_REG_OFFSET_Y_REG_H_M = 0x48, + LSM303AGR_REG_OFFSET_Z_REG_L_M = 0x49, + LSM303AGR_REG_OFFSET_Z_REG_H_M = 0x4a, + + // 0x4b-0x4d reserved + + LSM303AGR_REG_WHO_AM_I_M = 0x4f, + + // 0x50-0x5f reserved + + LSM303AGR_REG_CFG_REG_A_M = 0x60, + LSM303AGR_REG_CFG_REG_B_M = 0x61, + LSM303AGR_REG_CFG_REG_C_M = 0x62, + + LSM303AGR_REG_INT_CTRL_REG_M = 0x63, + LSM303AGR_REG_INT_SRC_REG_M = 0x64, + LSM303AGR_REG_INT_THS_L_REG_M = 0x65, + LSM303AGR_REG_INT_THS_H_REG_M = 0x66, + + LSM303AGR_REG_STATUS_REG_M = 0x67, + + LSM303AGR_REG_OUTX_L_REG_M = 0x68, + LSM303AGR_REG_OUTX_H_REG_M = 0x69, + LSM303AGR_REG_OUTY_L_REG_M = 0x6a, + LSM303AGR_REG_OUTY_H_REG_M = 0x6b, + LSM303AGR_REG_OUTZ_L_REG_M = 0x6c, + LSM303AGR_REG_OUTZ_H_REG_M = 0x6d + + // 0x6e-0x6f reserved + } LSM303AGR_REGS_T; + + // Accelerometer registers + + /** + * STATUS_REG_AUX_A bits + */ + typedef enum { + // 0x01-0x02 reserved + + LSM303AGR_STATUS_REG_AUX_A_TDA = 0x04, // temp data avail + + // 0x08-0x20 reserved + + LSM303AGR_STATUS_REG_AUX_A_TOR = 0x40, // temp data overrun + + // 0x80 reserved + } LSM303AGR_STATUS_REG_AUX_A_BITS_T; + + /** + * TEMP_CFG_REG_A bits + */ + typedef enum { + // 0x01-0x20 reserved + + LSM303AGR_TEMP_CFG_REG_A_TEMP_EN0 = 0x40, + LSM303AGR_TEMP_CFG_REG_A_TEMP_EN1 = 0x80, + _LSM303AGR_TEMP_CFG_REG_A_TEMP_EN_MASK = 3, + _LSM303AGR_TEMP_CFG_REG_A_TEMP_EN_SHIFT = 6, + } LSM303AGR_TEMP_CFG_REG_A_BITS_T; + + /** + * TEMP_CFG_REG_A_TEMP_EN values + */ + typedef enum { + LSM303AGR_TEMP_EN_OFF = 0, + LSM303AGR_TEMP_EN_ON = 3, + } LSM303AGR_TEMP_EN_T; + + /** + * CTRL_REG1_A bits + */ + typedef enum { + LSM303AGR_CTRL_REG1_A_XEN = 0x01, // axis enables + LSM303AGR_CTRL_REG1_A_YEN = 0x02, + LSM303AGR_CTRL_REG1_A_ZEN = 0x04, + LSM303AGR_CTRL_REG1_A_LPEN = 0x08, // low power enable + + LSM303AGR_CTRL_REG1_A_ODR0 = 0x10, // output data rate + LSM303AGR_CTRL_REG1_A_ODR1 = 0x20, + LSM303AGR_CTRL_REG1_A_ODR2 = 0x40, + LSM303AGR_CTRL_REG1_A_ODR3 = 0x80, + _LSM303AGR_CTRL_REG1_A_ODR_MASK = 15, + _LSM303AGR_CTRL_REG1_A_ODR_SHIFT = 4, + } LSM303AGR_CTRL_REG1_A_BITS_T; + + /** + * CTRL_REG1_A_ODR values (and power mode) + */ + typedef enum { + LSM303AGR_A_ODR_POWER_DOWN = 0, + LSM303AGR_A_ODR_1HZ = 1, // 1Hz, HR/Normal/LP + LSM303AGR_A_ODR_10HZ = 2, + LSM303AGR_A_ODR_25HZ = 3, + LSM303AGR_A_ODR_50HZ = 4, + LSM303AGR_A_ODR_100HZ = 5, + LSM303AGR_A_ODR_200HZ = 6, + LSM303AGR_A_ODR_400HZ = 7, + LSM303AGR_A_ODR_1_620KHZ = 8, // 1.620kHz, low power + LSM303AGR_A_ODR_1_344KHZ = 9, // 1.344kHZ + // HR/Normal, + // 5.376kHZ low + // power + } LSM303AGR_A_ODR_T; + + /** + * CTRL_REG2_A bits + */ + typedef enum { + LSM303AGR_CTRL_REG2_A_HPIS1 = 0x01, + LSM303AGR_CTRL_REG2_A_HPIS2 = 0x02, + LSM303AGR_CTRL_REG2_A_HPCLICK = 0x04, + LSM303AGR_CTRL_REG2_A_FDS = 0x08, + LSM303AGR_CTRL_REG2_A_HPCF1 = 0x10, + LSM303AGR_CTRL_REG2_A_HPCF2 = 0x20, + + LSM303AGR_CTRL_REG2_A_HPM0 = 0x40, + LSM303AGR_CTRL_REG2_A_HPM1 = 0x80, + _LSM303AGR_CTRL_REG2_A_HPM_MASK = 3, + _LSM303AGR_CTRL_REG2_A_HPM_SHIFT = 6, + } LSM303AGR_CTRL_REG2_A_BITS_T; + + /** + * CTRL_REG2_A_HPM values + */ + typedef enum { + LSM303AGR_A_HPM_NORMAL = 0, + LSM303AGR_A_HPM_REF = 1, + LSM303AGR_A_HPM_NORMAL2 = 2, + LSM303AGR_A_HPM_AUTORESET = 3, + } LSM303AGR_A_HPM_T; + + /** + * CTRL_REG3_A bits + */ + typedef enum { + // 0x01 reserved + + LSM303AGR_CTRL_REG3_A_I1_OVERRUN = 0x02, + LSM303AGR_CTRL_REG3_A_I1_WTM = 0x04, + LSM303AGR_CTRL_REG3_A_I1_DRDY2 = 0x08, + LSM303AGR_CTRL_REG3_A_I1_DRDY1 = 0x10, + LSM303AGR_CTRL_REG3_A_I1_AOI2 = 0x20, + LSM303AGR_CTRL_REG3_A_I1_AOI1 = 0x40, + LSM303AGR_CTRL_REG3_A_I1_CLICK = 0x80, + } LSM303AGR_CTRL_REG3_A_BITS_T; + + /** + * CTRL_REG4_A bits + */ + typedef enum { + LSM303AGR_CTRL_REG4_A_SPI_ENABLE = 0x01, // 3-wire + // spi (no + // supported!) + LSM303AGR_CTRL_REG4_A_ST0 = 0x02, + LSM303AGR_CTRL_REG4_A_ST1 = 0x04, + _LSM303AGR_CTRL_REG4_A_ST_MASK = 3, + _LSM303AGR_CTRL_REG4_A_ST_SHIFT = 1, + + LSM303AGR_CTRL_REG4_A_HR = 0x08, // high res + + LSM303AGR_CTRL_REG4_A_FS0 = 0x10, // FS selection + LSM303AGR_CTRL_REG4_A_FS1 = 0x20, + _LSM303AGR_CTRL_REG4_A_FS_MASK = 3, + _LSM303AGR_CTRL_REG4_A_FS_SHIFT = 4, + + LSM303AGR_CTRL_REG4_A_BLE = 0x40, + LSM303AGR_CTRL_REG4_A_BDU = 0x80, // block update + } LSM303AGR_CTRL_REG4_A_BITS_T; + + /** + * CTRL_REG4_A_ST values (self-test) + */ + typedef enum { + LSM303AGR_A_ST_NORMAL = 0, + LSM303AGR_A_ST_0 = 1, + LSM303AGR_A_ST_1 = 2, + } LSM303AGR_A_ST_T; + + /** + * CTRL_REG4_A_FS values (full-scale) + */ + typedef enum { + LSM303AGR_A_FS_2G = 0, // 2G + LSM303AGR_A_FS_4G = 1, + LSM303AGR_A_FS_8G = 2, + LSM303AGR_A_FS_16G = 3, // 16G + } LSM303AGR_A_FS_T; + + /** + * CTRL_REG5_A bits + */ + typedef enum { + LSM303AGR_CTRL_REG5_A_D4D_INT2 = 0x01, + LSM303AGR_CTRL_REG5_A_LIR_INT2 = 0x02, + LSM303AGR_CTRL_REG5_A_D4D_INT1 = 0x04, + LSM303AGR_CTRL_REG5_A_LIR_INT1 = 0x08, + + // 0x10-0x20 reserved + + LSM303AGR_CTRL_REG5_A_FIFO_EN = 0x40, + LSM303AGR_CTRL_REG5_A_BOOT = 0x80, + } LSM303AGR_CTRL_REG5_A_BITS_T; + + /** + * CTRL_REG6_A bits + */ + typedef enum { + // 0x01 reserved + + LSM303AGR_CTRL_REG6_A_H_LACTIVE = 0x02, + + // 0x04 reserved + + LSM303AGR_CTRL_REG6_A_P2_ACT = 0x08, + LSM303AGR_CTRL_REG6_A_BOOT_I2 = 0x10, + LSM303AGR_CTRL_REG6_A_I2_INT2 = 0x20, + LSM303AGR_CTRL_REG6_A_I2_INT1 = 0x40, + LSM303AGR_CTRL_REG6_A_I2_CLICK_EN = 0x80, + } LSM303AGR_CTRL_REG6_A_BITS_T; + + /** + * STATUS_REG_A bits + */ + typedef enum { + LSM303AGR_STATUS_REG_A_XDA = 0x01, + LSM303AGR_STATUS_REG_A_YDA = 0x02, + LSM303AGR_STATUS_REG_A_ZDA = 0x04, + LSM303AGR_STATUS_REG_A_ZYXDA = 0x08, + LSM303AGR_STATUS_REG_A_XOR = 0x10, + LSM303AGR_STATUS_REG_A_YOR = 0x20, + LSM303AGR_STATUS_REG_A_ZOR = 0x40, + LSM303AGR_STATUS_REG_A_ZYXOR = 0x80, + } LSM303AGR_STATUS_REG_A_BITS_T; + + /** + * FIFO_CTRL_REG_A bits + */ + typedef enum { + LSM303AGR_FIFO_CTRL_REG_A_FTH0 = 0x01, // fifo threshold + LSM303AGR_FIFO_CTRL_REG_A_FTH1 = 0x02, + LSM303AGR_FIFO_CTRL_REG_A_FTH2 = 0x04, + LSM303AGR_FIFO_CTRL_REG_A_FTH3 = 0x08, + LSM303AGR_FIFO_CTRL_REG_A_FTH4 = 0x10, + _LSM303AGR_FIFO_CTRL_REG_A_FTH_MASK = 31, + _LSM303AGR_FIFO_CTRL_REG_A_FTH_SHIF = 0, + + LSM303AGR_FIFO_CTRL_REG_A_TR = 0x20, // trigger select + + LSM303AGR_FIFO_CTRL_REG_A_FM0 = 0x40, // fifo mode + LSM303AGR_FIFO_CTRL_REG_A_FM1 = 0x80, + _LSM303AGR_FIFO_CTRL_REG_A_FM_MASK = 3, + _LSM303AGR_FIFO_CTRL_REG_A_FM_SHIFT = 6, + } LSM303AGR_FIFO_CTRL_REG_A_BITS_T; + + /** + * FIFO_CTRL_REG_A_FM values (fifo mode) + */ + typedef enum { + LSM303AGR_A_FM_BYPASS = 0, + LSM303AGR_A_FM_FIFO = 1, + LSM303AGR_A_FM_STREAM = 2, + LSM303AGR_A_FM_STREAM_TO_FIFO = 3, + } LSM303AGR_A_FM_T; + + /** + * FIFO_SRC_REG_A bits + */ + typedef enum { + LSM303AGR_FIFO_SRC_REG_A_FSS0 = 0x01, // fifo unread samples + LSM303AGR_FIFO_SRC_REG_A_FSS1 = 0x02, + LSM303AGR_FIFO_SRC_REG_A_FSS2 = 0x04, + LSM303AGR_FIFO_SRC_REG_A_FSS3 = 0x08, + LSM303AGR_FIFO_SRC_REG_A_FSS4 = 0x10, + _LSM303AGR_FIFO_SRC_REG_A_FSS_MASK = 31, + _LSM303AGR_FIFO_SRC_REG_A_FSS_SHIFT = 0, + + LSM303AGR_FIFO_SRC_REG_A_EMPTY = 0x20, + LSM303AGR_FIFO_SRC_REG_A_OVRN_FIFO = 0x40, + LSM303AGR_FIFO_SRC_REG_A_WTM = 0x80, + } LSM303AGR_FIFO_SRC_REG_A_BITS_T; + + /** + * INT_CFG_A bits. This definition is used for INT1 and INT2. + */ + typedef enum { + LSM303AGR_INT_CFG_A_XLIE = 0x01, + LSM303AGR_INT_CFG_A_XHIE = 0x02, + LSM303AGR_INT_CFG_A_YLIE = 0x04, + LSM303AGR_INT_CFG_A_YHIE = 0x08, + LSM303AGR_INT_CFG_A_ZLIE = 0x10, + LSM303AGR_INT_CFG_A_ZHIE = 0x20, + LSM303AGR_INT_CFG_A_6D = 0x40, + LSM303AGR_INT_CFG_A_AOI = 0x80, + } LSM303AGR_INT_CFG_A_BITS_T; + + /** + * INT_SRC_A bits. This definition is used for INT1 and INT2. + */ + typedef enum { + LSM303AGR_INT_SRC_A_XL = 0x01, + LSM303AGR_INT_SRC_A_XH = 0x02, + LSM303AGR_INT_SRC_A_YL = 0x04, + LSM303AGR_INT_SRC_A_YH = 0x08, + LSM303AGR_INT_SRC_A_ZL = 0x10, + LSM303AGR_INT_SRC_A_ZH = 0x20, + LSM303AGR_INT_SRC_A_IA = 0x40, + + // 0x80 reserved + } LSM303AGR_INT_SRC_A_BITS_T; + + /** + * INT_THS_A bits. This definition is used for INT1 and INT2. + */ + typedef enum { + LSM303AGR_INT_THS0 = 0x01, + LSM303AGR_INT_THS1 = 0x02, + LSM303AGR_INT_THS2 = 0x04, + LSM303AGR_INT_THS3 = 0x08, + LSM303AGR_INT_THS4 = 0x10, + LSM303AGR_INT_THS5 = 0x20, + LSM303AGR_INT_THS6 = 0x40, + _LSM303AGR_INT_THS_MASK = 127, + _LSM303AGR_INT_THS_SHIFT = 0, + + // 0x80 reserved + } LSM303AGR_INT_THS_BITS_T; + + /** + * INT_DUR_A bits. This definition is used for INT1 and INT2. + */ + typedef enum { + LSM303AGR_INT_DUR0 = 0x01, + LSM303AGR_INT_DUR1 = 0x02, + LSM303AGR_INT_DUR2 = 0x04, + LSM303AGR_INT_DUR3 = 0x08, + LSM303AGR_INT_DUR4 = 0x10, + LSM303AGR_INT_DUR5 = 0x20, + LSM303AGR_INT_DUR6 = 0x40, + _LSM303AGR_INT_DUR_MASK = 127, + _LSM303AGR_INT_DUR_SHIFT = 0, + + // 0x80 reserved + } LSM303AGR_INT_DUR_BITS_T; + + /** + * CLICK_CFG_A bits + */ + typedef enum { + LSM303AGR_CLICK_CFG_A_XS = 0x01, + LSM303AGR_CLICK_CFG_A_XD = 0x02, + LSM303AGR_CLICK_CFG_A_YS = 0x04, + LSM303AGR_CLICK_CFG_A_YD = 0x08, + LSM303AGR_CLICK_CFG_A_ZS = 0x10, + LSM303AGR_CLICK_CFG_A_ZD = 0x20, + + // 0x40-0x80 reserved + } LSM303AGR_CLICK_CFG_A_BITS_T; + + /** + * CLICK_SRC_A bits + */ + typedef enum { + LSM303AGR_CLICK_SRC_A_X = 0x01, + LSM303AGR_CLICK_SRC_A_Y = 0x02, + LSM303AGR_CLICK_SRC_A_Z = 0x04, + LSM303AGR_CLICK_SRC_A_SIGN = 0x08, + LSM303AGR_CLICK_SRC_A_SCLICK = 0x10, + LSM303AGR_CLICK_SRC_A_DCLICK = 0x20, + LSM303AGR_CLICK_SRC_A_IA = 0x40, + + // 0x80 reserved + } LSM303AGR_CLICK_SRC_A_BITS_T; + + /** + * CLICK_THS_A bits + */ + typedef enum { + LSM303AGR_CLICK_A_THS0 = 0x01, + LSM303AGR_CLICK_A_THS1 = 0x02, + LSM303AGR_CLICK_A_THS2 = 0x04, + LSM303AGR_CLICK_A_THS3 = 0x08, + LSM303AGR_CLICK_A_THS4 = 0x10, + LSM303AGR_CLICK_A_THS5 = 0x20, + LSM303AGR_CLICK_A_THS6 = 0x40, + _LSM303AGR_CLICK_A_THS_MASK = 127, + _LSM303AGR_CLICK_A_THS_SHIFT = 0, + + // 0x80 reserved + } LSM303AGR_CLICK_THS_A_BITS_T; + + /** + * TIME_LIMIT_A bits + */ + typedef enum { + LSM303AGR_TIME_LIMIT_A_TLI0 = 0x01, + LSM303AGR_TIME_LIMIT_A_TLI1 = 0x02, + LSM303AGR_TIME_LIMIT_A_TLI2 = 0x04, + LSM303AGR_TIME_LIMIT_A_TLI3 = 0x08, + LSM303AGR_TIME_LIMIT_A_TLI4 = 0x10, + LSM303AGR_TIME_LIMIT_A_TLI5 = 0x20, + LSM303AGR_TIME_LIMIT_A_TLI6 = 0x40, + _LSM303AGR_TIME_LIMIT_A_TLI_MASK = 127, + _LSM303AGR_TIME_LIMIT_A_TLI_SHIFT = 0, + + // 0x80 reserved + } LSM303AGR_TIME_LIMIT_A_BITS_T; + + // Magnetometer registers + + /** + * CFG_REG_A_M bits + */ + typedef enum { + LSM303AGR_CFG_REG_A_M_MD0 = 0x01, // mode select + LSM303AGR_CFG_REG_A_M_MD1 = 0x02, + _LSM303AGR_CFG_REG_A_M_MD_MASK = 3, + _LSM303AGR_CFG_REG_A_M_MD_SHIFT = 0, + + LSM303AGR_CFG_REG_A_M_ODR0 = 0x04, // output data rate + LSM303AGR_CFG_REG_A_M_ODR1 = 0x08, + _LSM303AGR_CFG_REG_A_M_ODR_MASK = 3, + _LSM303AGR_CFG_REG_A_M_ODR_SHIFT = 2, + + LSM303AGR_CFG_REG_A_M_LP = 0x10, // low power/hi res + LSM303AGR_CFG_REG_A_M_SOFT_RESET = 0x20, + LSM303AGR_CFG_REG_A_M_REBOOT = 0x40, + LSM303AGR_CFG_REG_A_M_COMP_TEMP_EN = 0x80, // temp compensation + } LSM303AGR_CFG_REG_A_M_BITS_T; + + /** + * CFG_REG_A_M_MD values + */ + typedef enum { + LSM303AGR_CFG_A_M_MD_CONTINUOUS = 0, + LSM303AGR_CFG_A_M_MD_SINGLE = 1, + LSM303AGR_CFG_A_M_MD_IDLE = 2, + } LSM303AGR_CFG_A_M_MD_T; + + /** + * CFG_REG_A_M_ODR values + */ + typedef enum { + LSM303AGR_CFG_A_M_ODR_10HZ = 0, + LSM303AGR_CFG_A_M_ODR_20HZ = 1, + LSM303AGR_CFG_A_M_ODR_50HZ = 2, + LSM303AGR_CFG_A_M_ODR_100HZ = 3, + } LSM303AGR_CFG_A_M_ODR_T; + + /** + * CFG_REG_B_M bits + */ + typedef enum { + LSM303AGR_CFG_REG_B_M_LPF = 0x01, + LSM303AGR_CFG_REG_B_M_OFF_CANC = 0x02, + LSM303AGR_CFG_REG_B_M_SET_FREQ = 0x04, + LSM303AGR_CFG_REG_B_M_INT_ON_DATA_OFF = 0x08, + LSM303AGR_CFG_REG_B_M_OFF_CANC_ONE_SHOT = 0x10, + + // 0x20-0x80 reserved + } LSM303AGR_CFG_REG_B_M_BITS_T; + + /** + * CFG_REG_C_M bits + */ + typedef enum { + LSM303AGR_CFG_REG_C_M_INT_MAG = 0x01, + LSM303AGR_CFG_REG_C_M_SELF_TEST = 0x02, + + // 0x04 reserved and MUST be 0 for correct operation + + LSM303AGR_CFG_REG_C_M_BLE = 0x08, + LSM303AGR_CFG_REG_C_M_BDU = 0x10, + LSM303AGR_CFG_REG_C_M_I2C_DIS = 0x20, // don't do it... + LSM303AGR_CFG_REG_C_M_INT_MAG_PIN = 0x40, + + // 0x80 reserved + } LSM303AGR_CFG_REG_C_M_BITS_T; + + /** + * INT_CTRL_REG_M bits + */ + typedef enum { + LSM303AGR_INT_CTRL_REG_M_IEN = 0x01, + LSM303AGR_INT_CTRL_REG_M_IEL = 0x02, + LSM303AGR_INT_CTRL_REG_M_IEA = 0x04, + + // 0x08-0x10 reserved and MUST be 0 for correct operation + + LSM303AGR_INT_CTRL_REG_M_ZIEN = 0x20, + LSM303AGR_INT_CTRL_REG_M_YIEN = 0x40, + LSM303AGR_INT_CTRL_REG_M_XIEN = 0x80, + } LSM303AGR_INT_CTRL_REG_M_BITS_T; + + /** + * INT_SRC_REG_M bits + */ + typedef enum { + LSM303AGR_INT_SRC_REG_M_MROI = 0x02, + LSM303AGR_INT_SRC_REG_M_N_TH_S_Z = 0x04, + LSM303AGR_INT_SRC_REG_M_N_TH_S_Y = 0x08, + LSM303AGR_INT_SRC_REG_M_N_TH_S_X = 0x10, + LSM303AGR_INT_SRC_REG_M_P_TH_S_Z = 0x20, + LSM303AGR_INT_SRC_REG_M_P_TH_S_Y = 0x40, + LSM303AGR_INT_SRC_REG_M_P_TH_S_X = 0x80, + } LSM303AGR_INT_SRC_REG_M_BITS_T; + + /** + * STATUS_REG_M bits + */ + typedef enum { + LSM303AGR_STATUS_REG_M_XDA = 0x01, + LSM303AGR_STATUS_REG_M_YDA = 0x02, + LSM303AGR_STATUS_REG_M_ZDA = 0x04, + LSM303AGR_STATUS_REG_M_ZYXDA = 0x08, + LSM303AGR_STATUS_REG_M_XOR = 0x10, + LSM303AGR_STATUS_REG_M_YOR = 0x20, + LSM303AGR_STATUS_REG_M_ZOR = 0x40, + LSM303AGR_STATUS_REG_M_ZYXOR = 0x80, + } LSM303AGR_STATUS_REG_M_BITS_T; + + // The following enums are used for driver functionality. + + /** + * INTERRUPT_PINS_T values. There are 3 interrupt pins available; + * two for the accelerometer, and one for the magnetometer. + */ + typedef enum { + LSM303AGR_INTERRUPT_ACC_1 = 0, // ACC intr 1 + LSM303AGR_INTERRUPT_ACC_2 = 1, // ACC intr 2 + LSM303AGR_INTERRUPT_MAG = 2, // MAG intr + } LSM303AGR_INTERRUPT_PINS_T; + + /** + * LSM303AGR_POWER_MODE_T values. These set the basic operating + * power modes to one of low power, normal, and high resolution. + */ + typedef enum { + LSM303AGR_POWER_LOW_POWER = 0, + LSM303AGR_POWER_NORMAL = 1, + LSM303AGR_POWER_HIGH_RESOLUTION = 2, + } LSM303AGR_POWER_MODE_T; + + // some useful macros to save on typing and text wrapping +#define _LSM303AGR_SHIFT(x) \ + (_LSM303AGR_##x##_SHIFT) + +#define _LSM303AGR_MASK(x) \ + (_LSM303AGR_##x##_MASK) + +#define _LSM303AGR_SHIFTMASK(x) \ + (_LSM303AGR_MASK(x) << _LSM303AGR_SHIFT(x)) + + + +#ifdef __cplusplus +} +#endif diff --git a/src/lsm303agr/lsm303agr_fti.c b/src/lsm303agr/lsm303agr_fti.c new file mode 100644 index 00000000..0406360e --- /dev/null +++ b/src/lsm303agr/lsm303agr_fti.c @@ -0,0 +1,137 @@ +/* + * 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 "lsm303agr.h" +#include "upm_fti.h" + +/** + * This file implements the Function Table Interface (FTI) for this sensor + */ + +const char upm_lsm303agr_name[] = "LSM303AGR"; +const char upm_lsm303agr_description[] = + "3D Accelerometer and 3D Magnetometer"; +const upm_protocol_t upm_lsm303agr_protocol[] = {UPM_I2C, UPM_GPIO}; +const upm_sensor_t upm_lsm303agr_category[] = {UPM_MAGNETOMETER, + UPM_ACCELEROMETER}; + +// forward declarations +const void* upm_lsm303agr_get_ft(upm_sensor_t sensor_type); +void* upm_lsm303agr_init_name(); +void upm_lsm303agr_close(void *dev); +upm_result_t upm_lsm303agr_get_acc_value(void *dev, float *value, + upm_acceleration_u unit); +upm_result_t upm_lsm303agr_get_mag_value(void *dev, float *value); + +const upm_sensor_descriptor_t upm_lsm303agr_get_descriptor() +{ + upm_sensor_descriptor_t usd; + usd.name = upm_lsm303agr_name; + usd.description = upm_lsm303agr_description; + usd.protocol_size = 2; + usd.protocol = upm_lsm303agr_protocol; + usd.category_size = 2; + usd.category = upm_lsm303agr_category; + return usd; +} + +static const upm_sensor_ft ft = +{ + .upm_sensor_init_name = &upm_lsm303agr_init_name, + .upm_sensor_close = &upm_lsm303agr_close, +}; + +static const upm_magnetometer_ft mft = +{ + .upm_magnetometer_get_value = &upm_lsm303agr_get_mag_value +}; + +static const upm_acceleration_ft aft = +{ + .upm_acceleration_get_value = &upm_lsm303agr_get_acc_value +}; + +const void* upm_lsm303agr_get_ft(upm_sensor_t sensor_type) +{ + switch(sensor_type) + { + case UPM_SENSOR: + return &ft; + + case UPM_MAGNETOMETER: + return &mft; + + case UPM_ACCELEROMETER: + return &aft; + + default: + return NULL; + } +} + +void* upm_lsm303agr_init_name() +{ + return NULL; +} + + +void upm_lsm303agr_close(void *dev) +{ + lsm303agr_close((lsm303agr_context)dev); +} + +upm_result_t upm_lsm303agr_get_acc_value(void *dev, float *value, + upm_acceleration_u unit) +{ + if (lsm303agr_update((lsm303agr_context)dev)) + return UPM_ERROR_OPERATION_FAILED; + + float x, y, z; + + lsm303agr_get_magnetometer(dev, &x, &y, &z); + + value[0] = x; + value[1] = y; + value[2] = z; + + return UPM_SUCCESS; +} + +upm_result_t upm_lsm303agr_get_mag_value(void *dev, float *value) +{ + if (lsm303agr_update((lsm303agr_context)dev)) + return UPM_ERROR_OPERATION_FAILED; + + float x, y, z; + + lsm303agr_get_accelerometer(dev, &x, &y, &z); + + value[0] = x; + value[1] = y; + value[2] = z; + + return UPM_SUCCESS; +} diff --git a/src/lsm303agr/pyupm_lsm303agr.i b/src/lsm303agr/pyupm_lsm303agr.i new file mode 100644 index 00000000..e04c295e --- /dev/null +++ b/src/lsm303agr/pyupm_lsm303agr.i @@ -0,0 +1,17 @@ +// Include doxygen-generated documentation +%include "pyupm_doxy2swig.i" +%module pyupm_lsm303agr +%include "../upm.i" +%include "../upm_vectortypes.i" + +%feature("autodoc", "3"); + +#ifdef DOXYGEN +%include "lsm303agr_doc.i" +#endif + +%include "lsm303agr_defs.h" +%include "lsm303agr.hpp" +%{ + #include "lsm303agr.hpp" +%}