From a842898bd5d3de45c3839d677073ab88c4faaf1e Mon Sep 17 00:00:00 2001 From: Alex Tereschenko Date: Sun, 3 Dec 2017 18:46:15 +0000 Subject: [PATCH] lis3dh: add sensor support based on lis2ds12 module Adding STMicro LIS3DH sensor support. This module is based on the one for lis2ds12 (thanks, jontrulson!), but as sensors are noticeably different, the contents underwent major rework. Examples and basic API are left the same. Tested on Intel Edison with Arduino board using both I2C and SPI. Signed-off-by: Alex Tereschenko --- examples/c++/lis3dh.cxx | 81 +++ examples/c/lis3dh.c | 97 ++++ examples/java/LIS3DH_Example.java | 68 +++ examples/javascript/lis3dh.js | 69 +++ examples/python/lis3dh.py | 72 +++ src/lis3dh/CMakeLists.txt | 9 + src/lis3dh/javaupm_lis3dh.i | 23 + src/lis3dh/jsupm_lis3dh.i | 10 + src/lis3dh/lis3dh.c | 809 ++++++++++++++++++++++++++++++ src/lis3dh/lis3dh.cxx | 276 ++++++++++ src/lis3dh/lis3dh.h | 382 ++++++++++++++ src/lis3dh/lis3dh.hpp | 384 ++++++++++++++ src/lis3dh/lis3dh.json | 38 ++ src/lis3dh/lis3dh_defs.h | 505 +++++++++++++++++++ src/lis3dh/lis3dh_fti.c | 116 +++++ src/lis3dh/pyupm_lis3dh.i | 15 + 16 files changed, 2954 insertions(+) create mode 100644 examples/c++/lis3dh.cxx create mode 100644 examples/c/lis3dh.c create mode 100644 examples/java/LIS3DH_Example.java create mode 100644 examples/javascript/lis3dh.js create mode 100755 examples/python/lis3dh.py create mode 100644 src/lis3dh/CMakeLists.txt create mode 100644 src/lis3dh/javaupm_lis3dh.i create mode 100644 src/lis3dh/jsupm_lis3dh.i create mode 100644 src/lis3dh/lis3dh.c create mode 100644 src/lis3dh/lis3dh.cxx create mode 100644 src/lis3dh/lis3dh.h create mode 100644 src/lis3dh/lis3dh.hpp create mode 100644 src/lis3dh/lis3dh.json create mode 100644 src/lis3dh/lis3dh_defs.h create mode 100644 src/lis3dh/lis3dh_fti.c create mode 100644 src/lis3dh/pyupm_lis3dh.i diff --git a/examples/c++/lis3dh.cxx b/examples/c++/lis3dh.cxx new file mode 100644 index 000000000..4e54d82c6 --- /dev/null +++ b/examples/c++/lis3dh.cxx @@ -0,0 +1,81 @@ +/* + * Author: Alex Tereschenko + * Copyright (c) 2018 Alex Tereschenko. + * + * Based on LIS2DS12 example by + * 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 +#include + +#include "lis3dh.hpp" +#include "upm_utilities.h" + +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 LIS3DH using default I2C parameters + upm::LIS3DH sensor; + + // For SPI, bus 0, you would pass -1 as the address, and a valid pin + // for CS: LIS3DH(0, -1, 10); + + // 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; + + // we show both C and F for temperature + cout << "Compensation Temperature: " << sensor.getTemperature() << " C / " + << sensor.getTemperature(true) << " F" << endl; + + cout << endl; + + upm_delay_us(250000); + } + + //! [Interesting] + + cout << "Exiting..." << endl; + + return 0; +} diff --git a/examples/c/lis3dh.c b/examples/c/lis3dh.c new file mode 100644 index 000000000..a75fa9e85 --- /dev/null +++ b/examples/c/lis3dh.c @@ -0,0 +1,97 @@ +/* + * Author: Alex Tereschenko + * Copyright (c) 2018 Alex Tereschenko. + * + * Based on LIS2DS12 example by + * 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 "lis3dh.h" +#include "upm_utilities.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 LIS3DH instance using default i2c bus and address + lis3dh_context sensor = lis3dh_init(LIS3DH_DEFAULT_I2C_BUS, LIS3DH_DEFAULT_I2C_ADDR, -1); +#elif defined(CONFIG_BOARD_ARDUINO_101) + // ARDUINO_101 (Quark core) must use SPI + // Instantiate a LIS3DH instance using default SPI bus and pin 10 as CS + lis3dh_context sensor = lis3dh_init(LIS3DH_DEFAULT_SPI_BUS, -1, 10); +#else + // everything else use I2C by default + // Instantiate a LIS3DH instance using default i2c bus and address + lis3dh_context sensor = lis3dh_init(LIS3DH_DEFAULT_I2C_BUS, LIS3DH_DEFAULT_I2C_ADDR, -1); +#endif + + if (!sensor) { + printf("lis3dh_init() failed.\n"); + return 1; + } + + // now output data every 250 milliseconds + while (shouldRun) { + float x, y, z; + + if (lis3dh_update(sensor)) { + printf("lis3dh_update() failed\n"); + lis3dh_close(sensor); + return 1; + } + + lis3dh_get_accelerometer(sensor, &x, &y, &z); + printf("Acceleration x: %f y: %f z: %f g\n", x, y, z); + + printf("Compensation Temperature: %f C\n\n", lis3dh_get_temperature(sensor)); + + upm_delay_ms(250); + } + + printf("Exiting...\n"); + + lis3dh_close(sensor); + + //! [Interesting] + + return 0; +} diff --git a/examples/java/LIS3DH_Example.java b/examples/java/LIS3DH_Example.java new file mode 100644 index 000000000..beb563a6e --- /dev/null +++ b/examples/java/LIS3DH_Example.java @@ -0,0 +1,68 @@ +/* + * Author: Alex Tereschenko + * Copyright (c) 2018 Alex Tereschenko. + * + * Based on LIS2DS12 example by + * 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. + */ + +import upm_lis3dh.*; + +public class LIS3DH_Example +{ + public static void main(String[] args) throws InterruptedException + { +// ! [Interesting] + + // Instantiate a LIS3DH instance using default i2c bus and address + LIS3DH sensor = new LIS3DH(); + + // For SPI, bus 0, you would pass -1 as the address, and a + // valid pin for CS: + // LIS3DH(0, -1, 10); + + while (true) + { + // update our values from the sensor + sensor.update(); + + floatVector dataA = sensor.getAccelerometer(); + + System.out.println("Accelerometer x: " + dataA.get(0) + + " y: " + dataA.get(1) + + " z: " + dataA.get(2) + + " g"); + + System.out.println("Compensation Temperature: " + + sensor.getTemperature() + + " C / " + + sensor.getTemperature(true) + + " F"); + + System.out.println(); + Thread.sleep(250); + } + +// ! [Interesting] + } +} diff --git a/examples/javascript/lis3dh.js b/examples/javascript/lis3dh.js new file mode 100644 index 000000000..a67c007ac --- /dev/null +++ b/examples/javascript/lis3dh.js @@ -0,0 +1,69 @@ +/* + * Author: Alex Tereschenko + * Copyright (c) 2018 Alex Tereschenko. + * + * Based on LIS2DS12 example by + * 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. + */ + +var sensorObj = require('jsupm_lis3dh'); + +// Instantiate a LIS3DH instance using default i2c bus and address +var sensor = new sensorObj.LIS3DH(); + +// For SPI, bus 0, you would pass -1 as the address, and a valid pin for CS: +// LIS3DH(0, -1, 10); + +// 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"); + + // we show both C and F for temperature + console.log("Compensation Temperature: " + + sensor.getTemperature() + + " C / " + + sensor.getTemperature(true) + + " F"); + + 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/lis3dh.py b/examples/python/lis3dh.py new file mode 100755 index 000000000..37d4215ef --- /dev/null +++ b/examples/python/lis3dh.py @@ -0,0 +1,72 @@ +#!/usr/bin/env python +# Author: Alex Tereschenko +# Copyright (c) 2018 Alex Tereschenko. +# +# Based on LIS2DS12 example by +# 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. + +from __future__ import print_function +import time, sys, signal, atexit +from upm import pyupm_lis3dh as sensorObj + +def main(): + # Instantiate a LIS3DH instance using default i2c bus and address + sensor = sensorObj.LIS3DH() + + # For SPI, bus 0, you would pass -1 as the address, and a valid pin for CS: + # LIS3DH(0, -1, 10); + + ## 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") + + # we show both C and F for temperature + print("Compensation Temperature:", sensor.getTemperature(), "C /", end=' ') + print(sensor.getTemperature(True), "F") + + print() + time.sleep(.250) + +if __name__ == '__main__': + main() diff --git a/src/lis3dh/CMakeLists.txt b/src/lis3dh/CMakeLists.txt new file mode 100644 index 000000000..67fac43b9 --- /dev/null +++ b/src/lis3dh/CMakeLists.txt @@ -0,0 +1,9 @@ +upm_mixed_module_init (NAME lis3dh + DESCRIPTION "3-Axis Digital Accelerometer" + C_HDR lis3dh.h lis3dh_defs.h + C_SRC lis3dh.c + CPP_HDR lis3dh.hpp + CPP_SRC lis3dh.cxx + FTI_SRC lis3dh_fti.c + CPP_WRAPS_C + REQUIRES mraa utilities-c) diff --git a/src/lis3dh/javaupm_lis3dh.i b/src/lis3dh/javaupm_lis3dh.i new file mode 100644 index 000000000..1fe3f5dce --- /dev/null +++ b/src/lis3dh/javaupm_lis3dh.i @@ -0,0 +1,23 @@ +%module javaupm_lis3dh +%include "../upm.i" +%include "typemaps.i" +%include "../upm_vectortypes.i" + +%ignore getAccelerometer(float *, float *, float *); + +%include "lis3dh_defs.h" +%include "lis3dh.hpp" +%{ + #include "lis3dh.hpp" +%} + +%pragma(java) jniclasscode=%{ + static { + try { + System.loadLibrary("javaupm_lis3dh"); + } catch (UnsatisfiedLinkError e) { + System.err.println("Native code library failed to load. \n" + e); + System.exit(1); + } + } +%} diff --git a/src/lis3dh/jsupm_lis3dh.i b/src/lis3dh/jsupm_lis3dh.i new file mode 100644 index 000000000..ff9efd771 --- /dev/null +++ b/src/lis3dh/jsupm_lis3dh.i @@ -0,0 +1,10 @@ +%module jsupm_lis3dh +%include "../upm.i" +%include "../upm_vectortypes.i" + + +%include "lis3dh_defs.h" +%include "lis3dh.hpp" +%{ + #include "lis3dh.hpp" +%} diff --git a/src/lis3dh/lis3dh.c b/src/lis3dh/lis3dh.c new file mode 100644 index 000000000..d276c6da0 --- /dev/null +++ b/src/lis3dh/lis3dh.c @@ -0,0 +1,809 @@ +/* + * Author: Alex Tereschenko + * Copyright (c) 2018 Alex Tereschenko. + * + * Based on LIS2DS12 module by + * 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 "lis3dh.h" +#include "upm_utilities.h" + +// Macro for converting a uint8_t low/high pair into a float +#define INT16_TO_FLOAT(h, l) (float) ((int16_t)((l) | ((h) << 8))) + +// Some useful macros to save on typing and text wrapping +#undef _SHIFT +#define _SHIFT(x) (_LIS3DH_##x##_SHIFT) + +#undef _MASK +#define _MASK(x) (_LIS3DH_##x##_MASK) + +#undef _SHIFTMASK +#define _SHIFTMASK(x) (_MASK(x) << _SHIFT(x)) + +// SPI CS on and off functions +static void +_csOn(const lis3dh_context dev) +{ + assert(dev != NULL); + + if (dev->gpioCS) { + mraa_gpio_write(dev->gpioCS, 0); + } +} + +static void +_csOff(const lis3dh_context dev) +{ + assert(dev != NULL); + + if (dev->gpioCS) { + mraa_gpio_write(dev->gpioCS, 1); + } +} + +// Init +lis3dh_context +lis3dh_init(int bus, int addr, int cs) +{ + lis3dh_context dev = (lis3dh_context) malloc(sizeof(struct _lis3dh_context)); + + if (!dev) { + return NULL; + } + + // Zero out context + memset((void*) dev, 0, sizeof(struct _lis3dh_context)); + + // Make sure MRAA is initialized + if (mraa_init() != MRAA_SUCCESS) { + printf("%s: mraa_init() failed\n", __FUNCTION__); + lis3dh_close(dev); + return NULL; + } + + if (addr < 0) { + // SPI + if (!(dev->spi = mraa_spi_init(bus))) { + printf("%s: mraa_spi_init() for bus %d failed\n", __FUNCTION__, bus); + lis3dh_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() for CS pin %d failed\n", __FUNCTION__, cs); + lis3dh_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__); + lis3dh_close(dev); + return NULL; + } + } else { + // I2C + if (!(dev->i2c = mraa_i2c_init(bus))) { + printf("%s: mraa_i2c_init() for bus %d failed\n", __FUNCTION__, bus); + lis3dh_close(dev); + return NULL; + } + + if (mraa_i2c_address(dev->i2c, addr)) { + printf("%s: mraa_i2c_address() for address 0x%x failed\n", __FUNCTION__, addr); + lis3dh_close(dev); + return NULL; + } + } + + // Check the chip id + uint8_t chipID = lis3dh_get_chip_id(dev); + if (chipID != LIS3DH_CHIPID) { + printf("%s: invalid chip id: %02x, expected %02x\n", __FUNCTION__, chipID, LIS3DH_CHIPID); + lis3dh_close(dev); + return NULL; + } + + // Call devinit with default options + if (lis3dh_devinit(dev, LIS3DH_ODR_100HZ, LIS3DH_FS_2G, true)) { + printf("%s: lis3dh_devinit() failed\n", __FUNCTION__); + lis3dh_close(dev); + return NULL; + } + + return dev; +} + +void +lis3dh_close(lis3dh_context dev) +{ + assert(dev != NULL); + + lis3dh_uninstall_isr(dev, LIS3DH_INTERRUPT_INT1); + lis3dh_uninstall_isr(dev, LIS3DH_INTERRUPT_INT2); + + 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 +lis3dh_devinit(const lis3dh_context dev, LIS3DH_ODR_T odr, LIS3DH_FS_T fs, bool high_res) +{ + assert(dev != NULL); + + // Set high resolution mode, ODR and FS using passed values. + // Also unconditionally enable X, Y and Z axes, temperature sensor (and ADC), + // BDU mode as well as disable output high-pass filter. + if (lis3dh_enable_lp_mode(dev, false) || + lis3dh_enable_hr_mode(dev, high_res) || + lis3dh_enable_axes(dev, true, true, true) || + lis3dh_enable_bdu_mode(dev, true) || + lis3dh_set_odr(dev, odr) || + lis3dh_set_full_scale(dev, fs) || + lis3dh_enable_hp_filtering(dev, false) || + lis3dh_enable_temperature(dev, true)) { + + printf("%s: failed to set configuration parameters\n", __FUNCTION__); + return UPM_ERROR_OPERATION_FAILED; + } + + // Settle + upm_delay_ms(50); + + return UPM_SUCCESS; +} + +upm_result_t +lis3dh_enable_axes(const lis3dh_context dev, + bool x_axis_enable, + bool y_axis_enable, + bool z_axis_enable) +{ + assert(dev != NULL); + + uint8_t reg = lis3dh_read_reg(dev, LIS3DH_REG_CTRL_REG1); + + // X axis + if (x_axis_enable) { + reg |= LIS3DH_CTRL_REG1_XEN; + } else { + reg &= ~LIS3DH_CTRL_REG1_XEN; + } + + // Y axis + if (y_axis_enable) { + reg |= LIS3DH_CTRL_REG1_YEN; + } else { + reg &= ~LIS3DH_CTRL_REG1_YEN; + } + + // Z axis + if (z_axis_enable) { + reg |= LIS3DH_CTRL_REG1_ZEN; + } else { + reg &= ~LIS3DH_CTRL_REG1_ZEN; + } + + if (lis3dh_write_reg(dev, LIS3DH_REG_CTRL_REG1, reg)) { + printf("%s: failed to enable axes\n", __FUNCTION__); + return UPM_ERROR_OPERATION_FAILED; + } + + return UPM_SUCCESS; +} + +upm_result_t +lis3dh_enable_bdu_mode(const lis3dh_context dev, bool bdu_enable) +{ + assert(dev != NULL); + + uint8_t reg = lis3dh_read_reg(dev, LIS3DH_REG_CTRL_REG4); + + if (bdu_enable) { + reg |= LIS3DH_CTRL_REG4_BDU; + } else { + reg &= ~LIS3DH_CTRL_REG4_BDU; + } + + if (lis3dh_write_reg(dev, LIS3DH_REG_CTRL_REG4, reg)) { + printf("%s: failed to set BDU mode\n", __FUNCTION__); + return UPM_ERROR_OPERATION_FAILED; + } + + return UPM_SUCCESS; +} + +upm_result_t +lis3dh_enable_lp_mode(const lis3dh_context dev, bool lp_enable) +{ + assert(dev != NULL); + + uint8_t reg = lis3dh_read_reg(dev, LIS3DH_REG_CTRL_REG1); + + if (lp_enable) { + // Check whether high resolution mode is enabled - enabling both LP and HR is not allowed + uint8_t tmp_reg = lis3dh_read_reg(dev, LIS3DH_REG_CTRL_REG4); + if (tmp_reg & LIS3DH_CTRL_REG4_HR) { + printf("%s: can't enable low power mode, high resolution mode is already enabled\n", + __FUNCTION__); + return UPM_ERROR_INVALID_PARAMETER; + } else { + // We are good - enable low power mode + reg |= LIS3DH_CTRL_REG1_LPEN; + // Set temperatureFactor according to LP mode bit width (8b). + // This is needed to account for left alignment of the temperature data. + // We have to shift the data right (== divide by a factor in case of float) + // to eliminate "dead" bits. + dev->temperatureFactor = 256; + } + } else { + reg &= ~LIS3DH_CTRL_REG1_LPEN; + // Set temperatureFactor according to Normal mode bit width (10b) + dev->temperatureFactor = 64; + } + + if (lis3dh_write_reg(dev, LIS3DH_REG_CTRL_REG1, reg)) { + printf("%s: failed to set low power mode\n", __FUNCTION__); + return UPM_ERROR_OPERATION_FAILED; + } + + return UPM_SUCCESS; +} + +upm_result_t +lis3dh_enable_hr_mode(const lis3dh_context dev, bool hr_enable) +{ + assert(dev != NULL); + + uint8_t reg = lis3dh_read_reg(dev, LIS3DH_REG_CTRL_REG4); + + if (hr_enable) { + // Check whether low power mode is enabled - enabling both LP and HR is not allowed + uint8_t tmp_reg = lis3dh_read_reg(dev, LIS3DH_REG_CTRL_REG1); + if (tmp_reg & LIS3DH_CTRL_REG1_LPEN) { + printf("%s: can't enable high resolution mode, low power mode is already enabled\n", + __FUNCTION__); + return UPM_ERROR_INVALID_PARAMETER; + } else { + // We are good - enable high resolution mode + reg |= LIS3DH_CTRL_REG4_HR; + } + } else { + reg &= ~LIS3DH_CTRL_REG4_HR; + } + + // Set the temperature sensor scaling factor appropriately. + // Its max is 10 bit for both normal and HR modes. + dev->temperatureFactor = 64; + + if (lis3dh_write_reg(dev, LIS3DH_REG_CTRL_REG4, reg)) { + printf("%s: failed to set high resolution mode\n", __FUNCTION__); + return UPM_ERROR_OPERATION_FAILED; + } + + return UPM_SUCCESS; +} + +upm_result_t +lis3dh_enable_normal_mode(const lis3dh_context dev) +{ + assert(dev != NULL); + + // There's no special mode bit for Normal - just disable LP and HR + if (lis3dh_enable_lp_mode(dev, false) || lis3dh_enable_hr_mode(dev, false)) { + printf("%s: failed to enable normal mode\n", __FUNCTION__); + return UPM_ERROR_OPERATION_FAILED; + } + + return UPM_SUCCESS; +} + +upm_result_t +lis3dh_enable_hp_filtering(const lis3dh_context dev, bool filter) +{ + assert(dev != NULL); + + uint8_t reg = lis3dh_read_reg(dev, LIS3DH_REG_CTRL_REG2); + + if (filter) { + reg |= LIS3DH_CTRL_REG2_FDS; + } else { + reg &= ~LIS3DH_CTRL_REG2_FDS; + } + + if (lis3dh_write_reg(dev, LIS3DH_REG_CTRL_REG2, reg)) { + printf("%s: failed to set HP filter mode\n", __FUNCTION__); + return UPM_ERROR_OPERATION_FAILED; + } + + return UPM_SUCCESS; +} + +upm_result_t +lis3dh_enable_interrupt_latching(const lis3dh_context dev, bool int1_latch, bool int2_latch) +{ + assert(dev != NULL); + + uint8_t reg = lis3dh_read_reg(dev, LIS3DH_REG_CTRL_REG5); + + if (int1_latch) { + reg |= LIS3DH_CTRL_REG5_LIR_INT1; + } else { + reg &= ~LIS3DH_CTRL_REG5_LIR_INT1; + } + + if (int2_latch) { + reg |= LIS3DH_CTRL_REG5_LIR_INT2; + } else { + reg &= ~LIS3DH_CTRL_REG5_LIR_INT2; + } + + if (lis3dh_write_reg(dev, LIS3DH_REG_CTRL_REG5, reg)) { + printf("%s: failed to set interrupt latching mode\n", __FUNCTION__); + return UPM_ERROR_OPERATION_FAILED; + } + + return UPM_SUCCESS; +} + +upm_result_t +lis3dh_enable_adc(const lis3dh_context dev, bool adc_enable) +{ + assert(dev != NULL); + + // BDU mode is a prerequisite + if (adc_enable && lis3dh_enable_bdu_mode(dev, true)) { + printf("%s: failed to enable BDU mode - a prerequisite for enabling ADC\n", __FUNCTION__); + return UPM_ERROR_OPERATION_FAILED; + } + + uint8_t reg = lis3dh_read_reg(dev, LIS3DH_REG_TEMP_CFG_REG); + + if (adc_enable) { + reg |= LIS3DH_TEMP_CFG_REG_ADC_EN; + } else { + reg &= ~LIS3DH_TEMP_CFG_REG_ADC_EN; + } + + if (lis3dh_write_reg(dev, LIS3DH_REG_TEMP_CFG_REG, reg)) { + printf("%s: failed to set ADC mode\n", __FUNCTION__); + return UPM_ERROR_OPERATION_FAILED; + } + + return UPM_SUCCESS; +} + +upm_result_t +lis3dh_enable_temperature(const lis3dh_context dev, bool temperature_enable) +{ + assert(dev != NULL); + + // ADC must be enabled for temperature readings to work + if (temperature_enable && lis3dh_enable_adc(dev, true)) { + printf("%s: failed to enable ADC - a prerequisite for enabling temperature sensor\n", + __FUNCTION__); + return UPM_ERROR_OPERATION_FAILED; + } + + uint8_t reg = lis3dh_read_reg(dev, LIS3DH_REG_TEMP_CFG_REG); + + if (temperature_enable) { + reg |= LIS3DH_TEMP_CFG_REG_TEMP_EN; + } else { + reg &= ~LIS3DH_TEMP_CFG_REG_TEMP_EN; + } + + if (lis3dh_write_reg(dev, LIS3DH_REG_TEMP_CFG_REG, reg)) { + printf("%s: failed to set temperature sensor mode\n", __FUNCTION__); + return UPM_ERROR_OPERATION_FAILED; + } + + return UPM_SUCCESS; +} + +upm_result_t +lis3dh_set_odr(const lis3dh_context dev, LIS3DH_ODR_T odr) +{ + assert(dev != NULL); + + bool lp_mode = false; + uint8_t reg = lis3dh_read_reg(dev, LIS3DH_REG_CTRL_REG1); + + // Zero out ODR bits + reg &= ~_SHIFTMASK(CTRL_REG1_ODR); + + // We encoded an extra bit in LIS3DH_ODR_T indicating an LP mode. Check for it here. + if ((int) odr > (int) _MASK(CTRL_REG1_ODR)) { + lp_mode = true; + } + + // Mask it off and set it + odr &= _MASK(CTRL_REG1_ODR); + reg |= (odr << _SHIFT(CTRL_REG1_ODR)); + + // Set the LPEN bit appropriately + lis3dh_enable_lp_mode(dev, lp_mode); + + // Commit our changes + if (lis3dh_write_reg(dev, LIS3DH_REG_CTRL_REG1, reg)) { + printf("%s: failed to set ODR configuration\n", __FUNCTION__); + return UPM_ERROR_OPERATION_FAILED; + } + + return UPM_SUCCESS; +} + +upm_result_t +lis3dh_set_full_scale(const lis3dh_context dev, LIS3DH_FS_T fs) +{ + assert(dev != NULL); + + uint8_t reg = lis3dh_read_reg(dev, LIS3DH_REG_CTRL_REG4); + + // Mask out FS bits, add our own + reg &= ~_SHIFTMASK(CTRL_REG4_FS); + reg |= (fs << _SHIFT(CTRL_REG4_FS)); + + if (lis3dh_write_reg(dev, LIS3DH_REG_CTRL_REG4, reg)) { + printf("%s: failed to set FS configuration\n", __FUNCTION__); + return UPM_ERROR_OPERATION_FAILED; + } + + // Basic sensitivity in g/LSB, calculated for a full 16b resolution. + switch (fs) { + case LIS3DH_FS_2G: + // (2*2) / 2^16 + dev->accScale = 0.000061; + break; + + case LIS3DH_FS_4G: + // (4*2) / 2^16 + dev->accScale = 0.000122; + break; + + case LIS3DH_FS_8G: + // (8*2) / 2^16 + dev->accScale = 0.000244; + break; + + case LIS3DH_FS_16G: + // (16*2) / 2^16 + dev->accScale = 0.000488; + break; + } + + return UPM_SUCCESS; +} + +upm_result_t +lis3dh_set_interrupt_active_high(const lis3dh_context dev, bool high) +{ + assert(dev != NULL); + + uint8_t reg = lis3dh_read_reg(dev, LIS3DH_REG_CTRL_REG6); + + if (high) { + reg &= ~LIS3DH_CTRL_REG6_INT_POLARITY; + } else { + reg |= LIS3DH_CTRL_REG6_INT_POLARITY; + } + + if (lis3dh_write_reg(dev, LIS3DH_REG_CTRL_REG6, reg)) { + printf("%s: failed to set interrupt polarity mode\n", __FUNCTION__); + return UPM_ERROR_OPERATION_FAILED; + } + + return UPM_SUCCESS; +} + +upm_result_t +lis3dh_set_int1_config(const lis3dh_context dev, uint8_t cfg) +{ + assert(dev != NULL); + + if (lis3dh_write_reg(dev, LIS3DH_REG_CTRL_REG3, cfg)) { + printf("%s: failed to set interrupt 1 configuration\n", __FUNCTION__); + return UPM_ERROR_OPERATION_FAILED; + } + + return UPM_SUCCESS; +} + +upm_result_t +lis3dh_set_int2_config(const lis3dh_context dev, uint8_t cfg) +{ + assert(dev != NULL); + + if (lis3dh_write_reg(dev, LIS3DH_REG_CTRL_REG6, cfg)) { + printf("%s: failed to set interrupt 2 configuration\n", __FUNCTION__); + return UPM_ERROR_OPERATION_FAILED; + } + + return UPM_SUCCESS; +} + +uint8_t +lis3dh_read_reg(const lis3dh_context dev, uint8_t reg) +{ + assert(dev != NULL); + + if (dev->spi) { + // Needed for read + reg |= 0x80; + 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 +lis3dh_read_regs(const lis3dh_context dev, uint8_t reg, uint8_t* buffer, int len) +{ + assert(dev != NULL); + + if (dev->spi) { + // Needed for read with address autoincrement + reg |= 0xC0; + + 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; i < len; i++) { + buffer[i] = sbuf[i + 1]; + } + } else { + // Needed for read with address autoincrement + reg |= 0x80; + if (mraa_i2c_read_bytes_data(dev->i2c, reg, buffer, len) != len) { + return -1; + } + } + + return len; +} + +upm_result_t +lis3dh_write_reg(const lis3dh_context dev, uint8_t reg, uint8_t val) +{ + assert(dev != NULL); + + if (dev->spi) { + // Mask off 0x80 for writing + reg &= 0x7F; + 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.", __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.", __FUNCTION__); + return UPM_ERROR_OPERATION_FAILED; + } + } + + return UPM_SUCCESS; +} + +upm_result_t +lis3dh_update(const lis3dh_context dev) +{ + assert(dev != NULL); + + // Max axes data length, 2 bytes per axis * 3 axes + const int bufLen = 6; + // Max temperature data length + const int temperatureBufLen = 2; + // We reuse the same array when reading acceleration and then temperature data + uint8_t buf[bufLen]; + + if (lis3dh_read_regs(dev, LIS3DH_REG_OUT_X_L, buf, bufLen) != bufLen) { + printf("%s: lis3dh_read_regs() failed to read %d bytes of axes data\n", __FUNCTION__, bufLen); + return UPM_ERROR_OPERATION_FAILED; + } + + // X MSB LSB + dev->accX = INT16_TO_FLOAT(buf[1], buf[0]); + + // Y + dev->accY = INT16_TO_FLOAT(buf[3], buf[2]); + + // Z + dev->accZ = INT16_TO_FLOAT(buf[5], buf[4]); + + // Get the temperature + if (lis3dh_read_regs(dev, LIS3DH_REG_OUT_ADC3_L, buf, temperatureBufLen) != temperatureBufLen) { + printf("%s: lis3dh_read_regs() failed to read %d bytes of temperature data\n", + __FUNCTION__, + temperatureBufLen); + return UPM_ERROR_OPERATION_FAILED; + } + + dev->temperature = INT16_TO_FLOAT(buf[1], buf[0]); + + return UPM_SUCCESS; +} + +uint8_t +lis3dh_get_chip_id(const lis3dh_context dev) +{ + assert(dev != NULL); + + return lis3dh_read_reg(dev, LIS3DH_REG_WHO_AM_I); +} + +void +lis3dh_get_accelerometer(const lis3dh_context dev, float* x, float* y, float* z) +{ + assert(dev != NULL); + + if (x) { + *x = dev->accX * dev->accScale; + } + + if (y) { + *y = dev->accY * dev->accScale; + } + + if (z) { + *z = dev->accZ * dev->accScale; + } +} + +float +lis3dh_get_temperature(const lis3dh_context dev) +{ + assert(dev != NULL); + + return (dev->temperature / dev->temperatureFactor); +} + +uint8_t +lis3dh_get_status(const lis3dh_context dev) +{ + assert(dev != NULL); + + return lis3dh_read_reg(dev, LIS3DH_REG_STATUS_REG); +} + +uint8_t +lis3dh_get_status_aux(const lis3dh_context dev) +{ + assert(dev != NULL); + + return lis3dh_read_reg(dev, LIS3DH_REG_STATUS_REG_AUX); +} + +upm_result_t +lis3dh_install_isr(const lis3dh_context dev, + LIS3DH_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 + lis3dh_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 LIS3DH_INTERRUPT_INT1: + dev->gpioINT1 = gpio_isr; + break; + + case LIS3DH_INTERRUPT_INT2: + dev->gpioINT2 = gpio_isr; + break; + } + + return UPM_SUCCESS; +} + +void +lis3dh_uninstall_isr(const lis3dh_context dev, LIS3DH_INTERRUPT_PINS_T intr) +{ + assert(dev != NULL); + + switch (intr) { + case LIS3DH_INTERRUPT_INT1: + if (dev->gpioINT1) { + mraa_gpio_isr_exit(dev->gpioINT1); + mraa_gpio_close(dev->gpioINT1); + dev->gpioINT1 = NULL; + } + break; + + case LIS3DH_INTERRUPT_INT2: + if (dev->gpioINT2) { + mraa_gpio_isr_exit(dev->gpioINT2); + mraa_gpio_close(dev->gpioINT2); + dev->gpioINT2 = NULL; + } + break; + } +} diff --git a/src/lis3dh/lis3dh.cxx b/src/lis3dh/lis3dh.cxx new file mode 100644 index 000000000..64049ce4f --- /dev/null +++ b/src/lis3dh/lis3dh.cxx @@ -0,0 +1,276 @@ +/* + * Author: Alex Tereschenko + * Copyright (c) 2018 Alex Tereschenko. + * + * Based on LIS2DS12 module by + * 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 "lis3dh.hpp" + +using namespace upm; +using namespace std; + +// conversion from Celsius to Fahrenheit + +static float +c2f(float c) +{ + return (c * (9.0 / 5.0) + 32.0); +} + +LIS3DH::LIS3DH(int bus, int addr, int cs) : m_lis3dh(lis3dh_init(bus, addr, cs)) +{ + if (!m_lis3dh) { + throw std::runtime_error(string(__FUNCTION__) + ": lis3dh_init() failed"); + } +} + +LIS3DH::~LIS3DH() +{ + lis3dh_close(m_lis3dh); +} + +void +LIS3DH::init(LIS3DH_ODR_T odr, LIS3DH_FS_T fs, bool high_res) +{ + if (lis3dh_devinit(m_lis3dh, odr, fs, high_res)) { + throw std::runtime_error(string(__FUNCTION__) + ": lis3dh_devinit() failed"); + } +} + +void +LIS3DH::enableAxes(bool x_axis_enable, bool y_axis_enable, bool z_axis_enable) +{ + if (lis3dh_enable_axes(m_lis3dh, x_axis_enable, y_axis_enable, z_axis_enable)) { + throw std::runtime_error(string(__FUNCTION__) + ": lis3dh_enable_axes() failed"); + } +} + +void +LIS3DH::enableBDUMode(bool bdu_enable) +{ + if (lis3dh_enable_bdu_mode(m_lis3dh, bdu_enable)) { + throw std::runtime_error(string(__FUNCTION__) + ": lis3dh_enable_bdu_mode() failed"); + } +} + +void +LIS3DH::enableLPMode(bool lp_enable) +{ + if (lis3dh_enable_lp_mode(m_lis3dh, lp_enable)) { + throw std::runtime_error(string(__FUNCTION__) + ": lis3dh_enable_lp_mode() failed"); + } +} + +void +LIS3DH::enableHRMode(bool hr_enable) +{ + if (lis3dh_enable_hr_mode(m_lis3dh, hr_enable)) { + throw std::runtime_error(string(__FUNCTION__) + ": lis3dh_enable_hr_mode() failed"); + } +} + +void +LIS3DH::enableNormalMode() +{ + if (lis3dh_enable_normal_mode(m_lis3dh)) { + throw std::runtime_error(string(__FUNCTION__) + ": lis3dh_enable_normal_mode() failed"); + } +} + +void +LIS3DH::enableHPFiltering(bool filter) +{ + if (lis3dh_enable_hp_filtering(m_lis3dh, filter)) { + throw std::runtime_error(string(__FUNCTION__) + ": lis3dh_enable_hp_filtering() failed"); + } +} + +void +LIS3DH::enableInterruptLatching(bool int1_latch, bool int2_latch) +{ + if (lis3dh_enable_interrupt_latching(m_lis3dh, int1_latch, int2_latch)) { + throw std::runtime_error(string(__FUNCTION__) + + ": lis3dh_enable_interrupt_latching() failed"); + } +} + +void +LIS3DH::enableADC(bool adc_enable) +{ + if (lis3dh_enable_adc(m_lis3dh, adc_enable)) { + throw std::runtime_error(string(__FUNCTION__) + ": lis3dh_enable_adc() failed"); + } +} + +void +LIS3DH::enableTemperature(bool temperature_enable) +{ + if (lis3dh_enable_temperature(m_lis3dh, temperature_enable)) { + throw std::runtime_error(string(__FUNCTION__) + ": lis3dh_enable_temperature() failed"); + } +} + +void +LIS3DH::setODR(LIS3DH_ODR_T odr) +{ + if (lis3dh_set_odr(m_lis3dh, odr)) { + throw std::runtime_error(string(__FUNCTION__) + ": lis3dh_set_odr() failed"); + } +} + +void +LIS3DH::setFullScale(LIS3DH_FS_T fs) +{ + if (lis3dh_set_full_scale(m_lis3dh, fs)) { + throw std::runtime_error(string(__FUNCTION__) + ": lis3dh_set_full_scale() failed"); + } +} + +void +LIS3DH::setInterruptActiveHigh(bool high) +{ + if (lis3dh_set_interrupt_active_high(m_lis3dh, high)) { + throw std::runtime_error(string(__FUNCTION__) + + ": lis3dh_set_interrupt_active_high() failed"); + } +} + +void +LIS3DH::setInt1Config(uint8_t cfg) +{ + if (lis3dh_set_int1_config(m_lis3dh, cfg)) { + throw std::runtime_error(string(__FUNCTION__) + ": lis3dh_set_int1_config() failed"); + } +} + +void +LIS3DH::setInt2Config(uint8_t cfg) +{ + if (lis3dh_set_int2_config(m_lis3dh, cfg)) { + throw std::runtime_error(string(__FUNCTION__) + ": lis3dh_set_int2_config() failed"); + } +} + +uint8_t +LIS3DH::readReg(uint8_t reg) +{ + return lis3dh_read_reg(m_lis3dh, reg); +} + +int +LIS3DH::readRegs(uint8_t reg, uint8_t* buffer, int len) +{ + int rv = lis3dh_read_regs(m_lis3dh, reg, buffer, len); + if (rv != len) { + throw std::runtime_error(string(__FUNCTION__) + ": lis3dh_read_regs() failed"); + } + + return rv; +} + +void +LIS3DH::writeReg(uint8_t reg, uint8_t val) +{ + if (lis3dh_write_reg(m_lis3dh, reg, val)) { + throw std::runtime_error(string(__FUNCTION__) + ": lis3dh_write_reg() failed"); + } +} + +void +LIS3DH::update() +{ + if (lis3dh_update(m_lis3dh)) { + throw std::runtime_error(string(__FUNCTION__) + ": lis3dh_update() failed"); + } +} + +uint8_t +LIS3DH::getChipID() +{ + return lis3dh_get_chip_id(m_lis3dh); +} + +void +LIS3DH::getAccelerometer(float* x, float* y, float* z) +{ + lis3dh_get_accelerometer(m_lis3dh, x, y, z); +} + +std::vector +LIS3DH::getAccelerometer() +{ + float v[3]; + + getAccelerometer(&v[0], &v[1], &v[2]); + return std::vector(v, v + 3); +} + +float +LIS3DH::getTemperature(bool fahrenheit) +{ + float temperature = lis3dh_get_temperature(m_lis3dh); + if (fahrenheit) { + return c2f(temperature); + } else { + return temperature; + } +} + +uint8_t +LIS3DH::getStatus() +{ + return lis3dh_get_status(m_lis3dh); +} + +uint8_t +LIS3DH::getStatusAux() +{ + return lis3dh_get_status_aux(m_lis3dh); +} + +void +LIS3DH::installISR(LIS3DH_INTERRUPT_PINS_T intr, + int gpio, + mraa::Edge level, + void (*isr)(void*), + void* arg) +{ + if (lis3dh_install_isr(m_lis3dh, intr, gpio, (mraa_gpio_edge_t) level, isr, arg)) { + throw std::runtime_error(string(__FUNCTION__) + ": lis3dh_install_isr() failed"); + } +} + +void +LIS3DH::uninstallISR(LIS3DH_INTERRUPT_PINS_T intr) +{ + lis3dh_uninstall_isr(m_lis3dh, intr); +} diff --git a/src/lis3dh/lis3dh.h b/src/lis3dh/lis3dh.h new file mode 100644 index 000000000..7db3b33d2 --- /dev/null +++ b/src/lis3dh/lis3dh.h @@ -0,0 +1,382 @@ +/* + * Author: Alex Tereschenko + * Copyright (c) 2018 Alex Tereschenko. + * + * Based on LIS2DS12 module by + * 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 "lis3dh_defs.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @file lis3dh.h + * @library lis3dh + * @brief C API for the lis3dh driver + * + * @include lis3dh.c + */ + +/** + * Device context + */ +typedef struct _lis3dh_context { + mraa_i2c_context i2c; + mraa_spi_context spi; + + mraa_gpio_context gpioCS; // SPI CS pin + mraa_gpio_context gpioINT1; // Interrupt 1 + mraa_gpio_context gpioINT2; // Interrupt 2 + + // Uncompensated temperature data + float temperature; + + // Temperature scaling factor. It accounts for storing 8b or 10b actual + // data in 16b register pair. + float temperatureFactor; + + // Uncompensated acceleration data + float accX; + float accY; + float accZ; + + // Acceleration scaling - used to calculate actual acceleration, + // depending on sensor working mode (low power/normal/high resolution) + float accScale; +} * lis3dh_context; + +/** + * LIS3DH 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 Intel Edison with Arduino breakout), then + * you can connect the proper pin to the hardware CS pin on your + * MCU and supply -1 for cs. + * + * @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 on error + */ +lis3dh_context lis3dh_init(int bus, int addr, int cs); + +/** + * LIS3DH Destructor + * + * @param dev The device context + */ +void lis3dh_close(lis3dh_context dev); + +/** + * Initialize the device and start operation. This function is + * called from lis3dh_init(), so it will not need to be called by + * a user unless the device is reset. + * + * @param dev The device context + * @param odr One of the LIS3DH_ODR_T values + * @param fs One of the LIS3DH_FS_T values + * @param high_res true to enable high resolution mode, false to disable + * @return UPM result + */ +upm_result_t +lis3dh_devinit(const lis3dh_context dev, LIS3DH_ODR_T odr, LIS3DH_FS_T fs, bool high_res); + +/** + * Enable or disable specific axes. + * + * @param dev The device context + * @param x_axis_enable true to enable X axis, false to disable + * @param y_axis_enable true to enable Y axis, false to disable + * @param z_axis_enable true to enable Z axis, false to disable + * @return UPM result + */ +upm_result_t lis3dh_enable_axes(const lis3dh_context dev, + bool x_axis_enable, + bool y_axis_enable, + bool z_axis_enable); + +/** + * Enable or disable Block Data Update (BDU) mode. + * + * @param dev The device context + * @param bdu_enable true to enable BDU mode, false to disable + * @return UPM result + */ +upm_result_t lis3dh_enable_bdu_mode(const lis3dh_context dev, bool bdu_enable); + +/** + * Enable or disable Low Power (LP) mode. Checks if mutually exclusive + * High Resolution (HR) mode is enabled and bails out if yes. + * + * @param dev The device context + * @param lp_enable true to enable LP mode, false to disable + * @return UPM result + */ +upm_result_t lis3dh_enable_lp_mode(const lis3dh_context dev, bool lp_enable); + +/** + * Enable or disable High Resolution (HR) mode. Checks if mutually exclusive + * Low Power (LP) mode is enabled and bails out if yes. + * + * @param dev The device context + * @param hr_enable true to enable HR mode, false to disable + * @return UPM result + */ +upm_result_t lis3dh_enable_hr_mode(const lis3dh_context dev, bool hr_enable); + +/** + * Enable Normal mode by explicitly disabling LP and HR ones. + * Note that there's no "disable" part as it's generally unknown, which mode + * we were in previously. To get out of Normal mode, just enable HR or LP one. + * + * @param dev The device context + * @return UPM result + */ +upm_result_t lis3dh_enable_normal_mode(const lis3dh_context dev); + +/** + * Enable high pass filtering of the accelerometer axis data. + * lis3dh_devinit() disables this by default. See the datasheet + * for details. + * + * @param dev The device context + * @param filter true to enable filtering, false to disable + * @return UPM result + */ +upm_result_t lis3dh_enable_hp_filtering(const lis3dh_context dev, bool filter); + +/** + * Enable or disable built-in Analog-to-Digital Converter (ADC). + * + * @param dev The device context + * @param adc_enable true to enable ADC, false to disable + * @return UPM result + */ +upm_result_t lis3dh_enable_adc(const lis3dh_context dev, bool adc_enable); + +/** + * Enable or disable built-in temperature sensor. It depends on ADC being + * enabled, so we enable it unconditionally. See datasheet for details. + * + * @param dev The device context + * @param temperature_enable true to enable temp sensor, false to disable + * @return UPM result + */ +upm_result_t lis3dh_enable_temperature(const lis3dh_context dev, bool temperature_enable); + +/** + * Enable or disable interrupt latching for INT1 and INT2. + * See the datasheet for details. + * + * @param dev The device context + * @param int1_latch true to enable latching for INT1, false to disable + * @param int2_latch true to enable latching for INT2, false to disable + * @return UPM result + */ +upm_result_t +lis3dh_enable_interrupt_latching(const lis3dh_context dev, bool int1_latch, bool int2_latch); + +/** + * Set the output data rate (ODR) of the device + * + * @param dev The device context + * @param odr One of the LIS3DH_ODR_T values + * @return UPM result + */ +upm_result_t lis3dh_set_odr(const lis3dh_context dev, LIS3DH_ODR_T odr); + +/** + * Set the full scale (FS) of the device. This device supports a + * full scale of 2, 4, 8, and 16G. + * + * @param dev The device context + * @param fs One of the LIS3DH_FS_T values + * @return UPM result + */ +upm_result_t lis3dh_set_full_scale(const lis3dh_context dev, LIS3DH_FS_T fs); + +/** + * Indicate whether INT1 and INT2 interrupts should be active high (default) + * or active low. See the datasheet for details. + * + * @param dev The device context + * @param high true for active high, false for active low + * @return UPM result + */ +upm_result_t lis3dh_set_interrupt_active_high(const lis3dh_context dev, bool high); + +/** + * Set interrupt 1 configuration. See the datasheet for details. + * + * @param dev The device context + * @param cfg A bitmask of values from LIS3DH_CTRL_REG3_BITS_T + * @return UPM result + */ +upm_result_t lis3dh_set_int1_config(const lis3dh_context dev, uint8_t cfg); + +/** + * Set interrupt 2 configuration. See the datasheet for details. + * + * @param dev The device context + * @param cfg A bitmask of values from LIS3DH_CTRL_REG6_BITS_T + * @return UPM result + */ +upm_result_t lis3dh_set_int2_config(const lis3dh_context dev, uint8_t cfg); + +/** + * Read a register + * + * @param dev The device context + * @param reg The register to read + * @return The value of the register + */ +uint8_t lis3dh_read_reg(const lis3dh_context dev, uint8_t reg); + +/** + * Read contiguous registers into a buffer + * + * @param dev The device context + * @param reg The register to start the read from + * @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 lis3dh_read_regs(const lis3dh_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 lis3dh_write_reg(const lis3dh_context dev, uint8_t reg, uint8_t val); + +/** + * Update the internal stored values from sensor data + * + * @param dev The device context + * @return UPM result + */ +upm_result_t lis3dh_update(const lis3dh_context dev); + +/** + * Return the chip ID + * + * @param dev The device context + * @return The chip ID (LIS3DH_CHIPID) + */ +uint8_t lis3dh_get_chip_id(const lis3dh_context dev); + +/** + * Return accelerometer data gravities (g). lis3dh_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 lis3dh_get_accelerometer(const lis3dh_context dev, float* x, float* y, float* z); + +/** + * Return the current measured temperature. Note, this is not + * ambient temperature. lis3dh_update() must have been called + * prior to calling this method. + * + * @param dev The device context + * @return The temperature in degrees Celsius + */ +float lis3dh_get_temperature(const lis3dh_context dev); + +/** + * Return the contents of the status register + * + * @param dev The device context + * @return A bitmask of values from LIS3DH_STATUS_REG_BITS_T + */ +uint8_t lis3dh_get_status(const lis3dh_context dev); + +/** + * Return the contents of the status aux register + * + * @param dev The device context + * @return A bitmask of values from LIS3DH_STATUS_REG_AUX_BITS_T + */ +uint8_t lis3dh_get_status_aux(const lis3dh_context dev); + +/** + * Install an interrupt handler + * + * @param dev The device context + * @param intr One of the LIS3DH_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_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 lis3dh_install_isr(const lis3dh_context dev, + LIS3DH_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 LIS3DH_INTERRUPT_PINS_T values + * specifying which interrupt pin you are removing + */ +void lis3dh_uninstall_isr(const lis3dh_context dev, LIS3DH_INTERRUPT_PINS_T intr); + +#ifdef __cplusplus +} +#endif diff --git a/src/lis3dh/lis3dh.hpp b/src/lis3dh/lis3dh.hpp new file mode 100644 index 000000000..50dd6dce1 --- /dev/null +++ b/src/lis3dh/lis3dh.hpp @@ -0,0 +1,384 @@ +/* + * Author: Alex Tereschenko + * Copyright (c) 2018 Alex Tereschenko. + * + * Based on LIS2DS12 module by + * 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 "lis3dh.h" +#include + +namespace upm +{ +/** + * @brief ST Micro 3-axis Accelerometer + * @defgroup lis3dh libupm-lis3dh + * @ingroup i2c spi gpio stmicro accelerometer + */ + +/** + * @library lis3dh + * @sensor lis3dh + * @comname Digital 3-axis Accelerometer + * @type accelerometer + * @man stmicro + * @con i2c spi gpio + * @web http://www.st.com/en/mems-and-sensors/lis3dh.html + * + * @brief API for the LIS3DH 3-axis Accelerometer + * + * The LIS3DH is an ultra-low-power high performance three-axis + * linear accelerometer belonging to the "nano" family which + * leverages on the robust and mature manufacturing processes + * already used for the production of micromachined + * accelerometers. + * + * The LIS3DH has user-selectable full scales of + * 2g/4g/8g/16g and is capable of measuring accelerations with + * output data rates from 1 Hz to 5300 Hz. + + * 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. + * + * @snippet lis3dh.cxx Interesting + */ + +class LIS3DH +{ + public: + /** + * LIS3DH 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 initialization failure + */ + LIS3DH(int bus = LIS3DH_DEFAULT_I2C_BUS, int addr = LIS3DH_DEFAULT_I2C_ADDR, int cs = -1); + + /** + * LIS3DH destructor + */ + virtual ~LIS3DH(); + + /** + * 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. + * + * @param odr One of the LIS3DH_ODR_T values. The default is + * LIS3DH_ODR_100HZ + * @param fs One of the LIS3DH_FS_T values. The default is + * LIS3DH_FS_2G + * @param high_res true to enable high resolution mode, + * false to disable. The default is true. + * @throws std::runtime_error on failure + */ + void + init(LIS3DH_ODR_T odr = LIS3DH_ODR_100HZ, LIS3DH_FS_T fs = LIS3DH_FS_2G, bool high_res = true); + + /** + * Enable or disable specific axes. + * init() enables all three by default. + * + * @param x_axis_enable true to enable X axis, false to disable + * @param y_axis_enable true to enable Y axis, false to disable + * @param z_axis_enable true to enable Z axis, false to disable + * @throws std::runtime_error on failure + */ + void enableAxes(bool x_axis_enable, bool y_axis_enable, bool z_axis_enable); + + /** + * Enable or disable Block Data Update (BDU) mode. + * + * @param bdu_enable true to enable BDU mode, false to disable + * @throws std::runtime_error on failure + */ + void enableBDUMode(bool bdu_enable); + + /** + * Enable or disable Low Power (LP) mode. Checks if mutually exclusive + * High Resolution (HR) mode is enabled and bails out if yes. + * + * @param lp_enable true to enable LP mode, false to disable + * @throws std::runtime_error on failure + */ + void enableLPMode(bool lp_enable); + + /** + * Enable or disable High Resolution (HR) mode. Checks if mutually exclusive + * Low Power (LP) mode is enabled and bails out if yes. + * + * @param hr_enable true to enable HR mode, false to disable + * @throws std::runtime_error on failure + */ + void enableHRMode(bool hr_enable); + + /** + * Enable Normal mode by explicitly disabling LP and HR ones. + * Note that there's no "disable" part as it's generally unknown, which mode + * we were in previously. To get out of Normal mode, just enable HR or LP one. + * + * @throws std::runtime_error on failure + */ + void enableNormalMode(); + + /** + * Enable high pass filtering of the accelerometer axis data. + * init() disables this by default. See the datasheet for + * details. + * + * @param filter true to enable filtering, false to disable + * @throws std::runtime_error on failure + */ + void enableHPFiltering(bool filter); + + /** + * Enable or disable interrupt latching for INT1 and INT2. + * See the datasheet for details. + * + * @param int1_latch true to enable latching for INT1, false to disable + * @param int2_latch true to enable latching for INT2, false to disable + * @throws std::runtime_error on failure + */ + void enableInterruptLatching(bool int1_latch, bool int2_latch); + + /** + * Enable or disable built-in Analog-to-Digital Converter (ADC). + * + * @param adc_enable true to enable ADC, false to disable + * @throws std::runtime_error on failure + */ + void enableADC(bool adc_enable); + + /** + * Enable or disable built-in temperature sensor. It depends on ADC being + * enabled, so we enable it unconditionally. See datasheet for details. + * + * @param temperature_enable true to enable temp sensor, false to disable + * @throws std::runtime_error on failure + */ + void enableTemperature(bool temperature_enable); + + /** + * Set the output data rate (ODR) of the device + * + * @param odr One of the LIS3DH_ODR_T values + * @throws std::runtime_error on failure + */ + void setODR(LIS3DH_ODR_T odr); + + /** + * Set the full scale (FS) of the device. This device supports a + * full scale of 2, 4, 8 and 16G. + * + * @param fs One of the LIS3DH_FS_T values + * @throws std::runtime_error on failure + */ + void setFullScale(LIS3DH_FS_T fs); + + /** + * Indicate whether INT1 and INT2 interrupts should be active high (default) + * or active low. See the datasheet for details. + * + * @param high true for active high, false for active low + * @throws std::runtime_error on failure + */ + void setInterruptActiveHigh(bool high); + + /** + * Set interrupt 1 configuration. See the datasheet for details. + * + * @param cfg A bitmask of values from LIS3DH_CTRL_REG3_BITS_T + * @throws std::runtime_error on failure + */ + void setInt1Config(uint8_t cfg); + + /** + * Set interrupt 2 configuration. See the datasheet for details. + * + * @param cfg A bitmask of values from LIS3DH_CTRL_REG6_BITS_T + * @throws std::runtime_error on failure + */ + void setInt2Config(uint8_t cfg); + + /** + * 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 reg The register to start the read from + * @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); + + /** + * 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 accelerometer 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 accelerometer 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 the current measured temperature. Note, this is not + * ambient temperature. update() must have been called prior to + * calling this method. + * + * @param fahrenheit true to return data in Fahrenheit, false for + * Celicus. Celsius is the default. + * @return The temperature in degrees Celsius or Fahrenheit + */ + float getTemperature(bool fahrenheit = false); + + /** + * Return the contents of the status register + * + * @return A bitmask of values from LIS3DH_STATUS_REG_BITS_T + */ + uint8_t getStatus(); + + /** + * Return the contents of the status aux register + * + * @return A bitmask of values from LIS3DH_STATUS_REG_AUX_BITS_T + */ + uint8_t getStatusAux(); + +#if defined(SWIGJAVA) || defined(JAVACALLBACK) + void + installISR(LIS3DH_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 LIS3DH_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(LIS3DH_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 LIS3DH_INTERRUPT_PINS_T values, + * specifying which interrupt pin you are removing + */ + void uninstallISR(LIS3DH_INTERRUPT_PINS_T intr); + + protected: + lis3dh_context m_lis3dh; + + private: + // Disable implicit copy and assignment operators + LIS3DH(const LIS3DH&) = delete; + LIS3DH& operator=(const LIS3DH&) = delete; + +// Adding a private function definition for Java bindings +#if defined(SWIGJAVA) || defined(JAVACALLBACK) + void installISR(LIS3DH_INTERRUPT_PINS_T intr, + int gpio, + mraa::Edge level, + void (*isr)(void*), + void* arg); +#endif +}; +} diff --git a/src/lis3dh/lis3dh.json b/src/lis3dh/lis3dh.json new file mode 100644 index 000000000..90af316c4 --- /dev/null +++ b/src/lis3dh/lis3dh.json @@ -0,0 +1,38 @@ +{ + "Library": "lis3dh", + "Description": "API for the LIS3DH 3-axis Accelerometer", + "Sensor Class": + { + "lis3dh": + { + "Name": "Digital 3-axis Accelerometer", + "Description": "The LIS3DH is an ultra-low-power high performance three-axis linear accelerometer belonging to the \"nano\" family which leverages the robust and mature manufacturing processes already used for the production of micromachined accelerometers. The LIS3DH has user-selectable full scales of 2g/4g/8g/16g and is capable of measuring accelerations with output data rates from 1 Hz to 5300 Hz. 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.", + "Aliases": ["lis3dh"], + "Categories": ["accelerometer"], + "Connections": ["i2c", "spi", "gpio"], + "Project Type": ["imu", "prototyping"], + "Manufacturers": ["stmicro"], + "Kits": [], + "Examples": + { + "Java": ["LIS3DH_Example.java"], + "Python": ["lis3dh.py"], + "Node.js": ["lis3dh.js"], + "C++": ["lis3dh.cxx"], + "C": ["lis3dh.c"] + }, + "Specifications": + { + "Operating Voltage": {"unit": "V", "low": 1.71, "high": 3.6}, + "Operating Current": {"unit": "uA", "low": 0.5, "high": 11}, + "Operating Temperature": {"unit": "degC", "low": -40, "high": 85} + }, + "Urls" : + { + "Product Pages": ["http://www.st.com/en/mems-and-sensors/lis3dh.html"], + "Datasheets": ["http://www.st.com/resource/en/datasheet/lis3dh.pdf"], + "Schematics": [] + } + } + } +} \ No newline at end of file diff --git a/src/lis3dh/lis3dh_defs.h b/src/lis3dh/lis3dh_defs.h new file mode 100644 index 000000000..81a54b12b --- /dev/null +++ b/src/lis3dh/lis3dh_defs.h @@ -0,0 +1,505 @@ +/* + * Author: Alex Tereschenko + * Copyright (c) 2018 Alex Tereschenko. + * + * Based on LIS2DS12 module by + * 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 LIS3DH_DEFAULT_I2C_BUS 0 +#define LIS3DH_DEFAULT_SPI_BUS 0 +#define LIS3DH_DEFAULT_I2C_ADDR 0x18 + +#define LIS3DH_CHIPID 0x33 + +// NOTE: Reserved registers must not be written into or permanent +// damage can result. Reading from them may return indeterminate +// values. Registers containing reserved bitfields must be +// written as 0, except for CTRL_REG0 - see datasheet for that one. + +/** + * LIS3DH registers + */ +typedef enum { + // 0x00-0x06 reserved + + LIS3DH_REG_STATUS_REG_AUX = 0x07, + + LIS3DH_REG_OUT_ADC1_L = 0x08, + LIS3DH_REG_OUT_ADC1_H = 0x09, + LIS3DH_REG_OUT_ADC2_L = 0x0A, + LIS3DH_REG_OUT_ADC2_H = 0x0B, + LIS3DH_REG_OUT_ADC3_L = 0x0C, + LIS3DH_REG_OUT_ADC3_H = 0x0D, + + // 0x0E reserved + + LIS3DH_REG_WHO_AM_I = 0x0F, + + // 0x10-0x1D reserved + + LIS3DH_REG_CTRL_REG0 = 0x1E, + LIS3DH_REG_TEMP_CFG_REG = 0x1F, + LIS3DH_REG_CTRL_REG1 = 0x20, + LIS3DH_REG_CTRL_REG2 = 0x21, + LIS3DH_REG_CTRL_REG3 = 0x22, + LIS3DH_REG_CTRL_REG4 = 0x23, + LIS3DH_REG_CTRL_REG5 = 0x24, + LIS3DH_REG_CTRL_REG6 = 0x25, + + LIS3DH_REG_REFERENCE = 0x26, + + LIS3DH_REG_STATUS_REG = 0x27, + + LIS3DH_REG_OUT_X_L = 0x28, + LIS3DH_REG_OUT_X_H = 0x29, + LIS3DH_REG_OUT_Y_L = 0x2A, + LIS3DH_REG_OUT_Y_H = 0x2B, + LIS3DH_REG_OUT_Z_L = 0x2C, + LIS3DH_REG_OUT_Z_H = 0x2D, + + LIS3DH_REG_FIFO_CTRL_REG = 0x2E, + LIS3DH_REG_FIFO_SRC_REG = 0x2F, + + LIS3DH_REG_INT1_CFG = 0x30, + LIS3DH_REG_INT1_SRC = 0x31, + LIS3DH_REG_INT1_THS = 0x32, + LIS3DH_REG_INT1_DURATION = 0x33, + + LIS3DH_REG_INT2_CFG = 0x34, + LIS3DH_REG_INT2_SRC = 0x35, + LIS3DH_REG_INT2_THS = 0x36, + LIS3DH_REG_INT2_DURATION = 0x37, + + LIS3DH_REG_CLICK_CFG = 0x38, + LIS3DH_REG_CLICK_SRC = 0x39, + LIS3DH_REG_CLICK_THS = 0x3A, + + LIS3DH_REG_TIME_LIMIT = 0x3B, + LIS3DH_REG_TIME_LATENCY = 0x3C, + LIS3DH_REG_TIME_WINDOW = 0x3D, + + LIS3DH_REG_ACT_THS = 0x3E, + LIS3DH_REG_ACT_DUR = 0x3F, +} LIS3DH_REGS_T; + +/** + * REG_STATUS_REG_AUX bits + */ +typedef enum { + LIS3DH_STATUS_REG_AUX_1DA = 0x01, + LIS3DH_STATUS_REG_AUX_2DA = 0x02, + LIS3DH_STATUS_REG_AUX_3DA = 0x04, + LIS3DH_STATUS_REG_AUX_321DA = 0x08, + + LIS3DH_STATUS_REG_AUX_1OR = 0x10, + LIS3DH_STATUS_REG_AUX_2OR = 0x20, + LIS3DH_STATUS_REG_AUX_3OR = 0x40, + LIS3DH_STATUS_REG_AUX_321OR = 0x80, +} LIS3DH_STATUS_REG_AUX_BITS_T; + +/** + * REG_CTRL_REG0 bits + */ +typedef enum { + // 0x01-0x40 reserved + + LIS3DH_CTRL_REG0_SDO_PU_DISC = 0x80, +} LIS3DH_CTRL_REG0_BITS_T; + +/** + * REG_TEMP_CFG_REG bits + */ +typedef enum { + // 0x01-0x20 reserved + + LIS3DH_TEMP_CFG_REG_TEMP_EN = 0x40, + LIS3DH_TEMP_CFG_REG_ADC_EN = 0x80, +} LIS3DH_TEMP_CFG_REG_BITS_T; + +/** + * REG_CTRL_REG1 bits + */ +typedef enum { + LIS3DH_CTRL_REG1_XEN = 0x01, + LIS3DH_CTRL_REG1_YEN = 0x02, + LIS3DH_CTRL_REG1_ZEN = 0x04, + LIS3DH_CTRL_REG1_LPEN = 0x08, + + LIS3DH_CTRL_REG1_ODR0 = 0x10, + LIS3DH_CTRL_REG1_ODR1 = 0x20, + LIS3DH_CTRL_REG1_ODR2 = 0x40, + LIS3DH_CTRL_REG1_ODR3 = 0x80, + _LIS3DH_CTRL_REG1_ODR_MASK = 0xF, + _LIS3DH_CTRL_REG1_ODR_SHIFT = 4, +} LIS3DH_CTRL_REG1_BITS_T; + +/** + * CTRL_REG1_ODR (Output Data Rate) values + */ +typedef enum { + LIS3DH_ODR_POWER_DOWN = 0x0, // 0b0000 + + // These are allowed in all modes (high resolution/normal/low power) + LIS3DH_ODR_1HZ = 0x1, // 0b0001 + LIS3DH_ODR_10HZ = 0x2, // 0b0010 + LIS3DH_ODR_25HZ = 0x3, // 0b0011 + LIS3DH_ODR_50HZ = 0x4, // 0b0100 + LIS3DH_ODR_100HZ = 0x5, // 0b0101 + LIS3DH_ODR_200HZ = 0x6, // 0b0110 + LIS3DH_ODR_400HZ = 0x7, // 0b0111 + + // The following two items have a dual meaning depending + // on whether the LPEN bit is set, but they use the same + // overlapping ODR values for the ODR bitfield. Since the + // bitfield is only 4 bits wide, we add a "virtual" 5th bit to + // indicate the LP versions. This is then screened out in the + // code and will set the LPEN bit according to what is selected + // here. + + // CTRL_REG1_LPEN == 0 (high resolution/normal mode) + LIS3DH_ODR_1344HZ = 0x9, // 0b1001 + + // CTRL_REG1_LPEN == 1 (low power mode). Add 'virtual' bit 5 + // value (0x10) for these LP modes, which we will detect and + // screen out in the driver. This simplifies the ODR API. + LIS3DH_ODR_5376HZ = (0x10 + 0x9), // 0b1001 + + // Low power-only mode, requires LPEN == 1, so add the virtual bit + LIS3DH_ODR_LP_1600HZ = (0x10 + 0x8), // 0b1000 +} LIS3DH_ODR_T; + +/** + * REG_CTRL_REG2 bits + */ +typedef enum { + LIS3DH_CTRL_REG2_HP_IA1 = 0x01, + LIS3DH_CTRL_REG2_HP_IA2 = 0x02, + LIS3DH_CTRL_REG2_HPCLICK = 0x04, + LIS3DH_CTRL_REG2_FDS = 0x08, + + LIS3DH_CTRL_REG2_HPCF0 = 0x10, + LIS3DH_CTRL_REG2_HPCF1 = 0x20, + _LIS3DH_CTRL_REG2_HPCF_MASK = 0x3, // 0b11 + _LIS3DH_CTRL_REG2_HPCF_SHIFT = 4, + + LIS3DH_CTRL_REG2_HPM0 = 0x40, + LIS3DH_CTRL_REG2_HPM1 = 0x80, + _LIS3DH_CTRL_REG2_HPM_MASK = 0x3, // 0b11 + _LIS3DH_CTRL_REG2_HPM_SHIFT = 6, +} LIS3DH_CTRL_REG2_BITS_T; + +/** + * CTRL_REG2_HPM (High-pass filter mode) values + */ +typedef enum { + LIS3DH_HPM_NORMAL_RST_REF_READ = 0x0, // 0b00, Normal mode (reset by reading REFERENCE) + LIS3DH_HPM_REF_SIGNAL = 0x1, // 0b01, Reference signal for filtering + LIS3DH_HPM_NORMAL = 0x2, // 0b10, Normal mode + LIS3DH_HPM_AUTORST = 0x3, // 0b11, Autoreset on interrupt event +} LIS3DH_HPM_T; + +/** + * REG_CTRL_REG3 bits + */ +typedef enum { + // 0x01 reserved + + LIS3DH_CTRL_REG3_I1_OVERRUN = 0x02, + LIS3DH_CTRL_REG3_I1_WTM = 0x04, + LIS3DH_CTRL_REG3_I1_321DA = 0x08, + LIS3DH_CTRL_REG3_I1_ZYXDA = 0x10, + LIS3DH_CTRL_REG3_I1_IA2 = 0x20, + LIS3DH_CTRL_REG3_I1_IA1 = 0x40, + LIS3DH_CTRL_REG3_I1_CLICK = 0x80, +} LIS3DH_CTRL_REG3_BITS_T; + +/** + * REG_CTRL_REG4 bits + */ +typedef enum { + LIS3DH_CTRL_REG4_SIM = 0x01, + + LIS3DH_CTRL_REG4_ST0 = 0x02, + LIS3DH_CTRL_REG4_ST1 = 0x04, + _LIS3DH_CTRL_REG4_ST_MASK = 0x3, // 0b11 + _LIS3DH_CTRL_REG4_ST_SHIFT = 1, + + LIS3DH_CTRL_REG4_HR = 0x08, + + LIS3DH_CTRL_REG4_FS0 = 0x10, + LIS3DH_CTRL_REG4_FS1 = 0x20, + _LIS3DH_CTRL_REG4_FS_MASK = 0x3, // 0b11 + _LIS3DH_CTRL_REG4_FS_SHIFT = 4, + + LIS3DH_CTRL_REG4_BLE = 0x40, + LIS3DH_CTRL_REG4_BDU = 0x80, +} LIS3DH_CTRL_REG4_BITS_T; + +/** + * CTRL_REG4_ST (Self Test) values + */ +typedef enum { + LIS3DH_ST_NORMAL = 0x0, // 0b00 + LIS3DH_ST_ST0 = 0x1, // 0b01 + LIS3DH_ST_ST1 = 0x2, // 0b10 +} LIS3DH_ST_T; + +/** + * CTRL_REG4_FS (Full Scale) values + */ +typedef enum { + LIS3DH_FS_2G = 0x0, // 0b00 + LIS3DH_FS_4G = 0x1, // 0b01 + LIS3DH_FS_8G = 0x2, // 0b10 + LIS3DH_FS_16G = 0x3, // 0b11 +} LIS3DH_FS_T; + +/** + * REG_CTRL_REG5 bits + */ +typedef enum { + LIS3DH_CTRL_REG5_D4D_INT2 = 0x01, + LIS3DH_CTRL_REG5_LIR_INT2 = 0x02, + LIS3DH_CTRL_REG5_D4D_INT1 = 0x04, + LIS3DH_CTRL_REG5_LIR_INT1 = 0x08, + + // 0x10-0x20 reserved + + LIS3DH_CTRL_REG5_FIFO_EN = 0x40, + LIS3DH_CTRL_REG5_BOOT = 0x80, +} LIS3DH_CTRL_REG5_BITS_T; + +/** + * REG_CTRL_REG6 bits + */ +typedef enum { + // 0x01 reserved + + LIS3DH_CTRL_REG6_INT_POLARITY = 0x02, + + // 0x04 reserved + + LIS3DH_CTRL_REG6_I2_ACT = 0x08, + LIS3DH_CTRL_REG6_I2_BOOT = 0x10, + LIS3DH_CTRL_REG6_I2_IA2 = 0x20, + LIS3DH_CTRL_REG6_I2_IA1 = 0x40, + LIS3DH_CTRL_REG6_I2_CLICK = 0x80, +} LIS3DH_CTRL_REG6_BITS_T; + +/** + * REG_STATUS_REG bits + */ +typedef enum { + LIS3DH_STATUS_REG_XDA = 0x01, + LIS3DH_STATUS_REG_YDA = 0x02, + LIS3DH_STATUS_REG_ZDA = 0x04, + LIS3DH_STATUS_REG_ZYXDA = 0x08, + + LIS3DH_STATUS_REG_XOR = 0x10, + LIS3DH_STATUS_REG_YOR = 0x20, + LIS3DH_STATUS_REG_ZOR = 0x40, + LIS3DH_STATUS_REG_ZYXOR = 0x80, +} LIS3DH_STATUS_REG_BITS_T; + +/** + * REG_FIFO_CTRL_REG bits + */ +typedef enum { + LIS3DH_FIFO_CTRL_REG_FTH0 = 0x01, + LIS3DH_FIFO_CTRL_REG_FTH1 = 0x02, + LIS3DH_FIFO_CTRL_REG_FTH2 = 0x04, + LIS3DH_FIFO_CTRL_REG_FTH3 = 0x08, + LIS3DH_FIFO_CTRL_REG_FTH4 = 0x10, + _LIS3DH_FIFO_CTRL_REG_FTH_MASK = 0x1F, // 0b11111 + _LIS3DH_FIFO_CTRL_REG_FTH_SHIFT = 0, + + LIS3DH_FIFO_CTRL_REG_TR = 0x20, + + LIS3DH_FIFO_CTRL_REG_FM0 = 0x40, + LIS3DH_FIFO_CTRL_REG_FM1 = 0x80, + _LIS3DH_FIFO_CTRL_REG_FM_MASK = 0x3, // 0b11 + _LIS3DH_FIFO_CTRL_REG_FM_SHIFT = 6, +} LIS3DH_FIFO_CTRL_REG_BITS_T; + +/** + * FIFO_CTRL_REG_FM (FIFO Mode) values + */ +typedef enum { + LIS3DH_FM_BYPASS = 0x0, // 0b00 + LIS3DH_FM_FIFO = 0x1, // 0b01 + LIS3DH_FM_STREAM = 0x2, // 0b10 + LIS3DH_FM_STREAM_TO_FIFO = 0x3, // 0b11 +} LIS3DH_FM_T; + +/** + * REG_FIFO_SRC_REG bits + */ +typedef enum { + LIS3DH_FIFO_SRC_REG_FSS0 = 0x01, + LIS3DH_FIFO_SRC_REG_FSS1 = 0x02, + LIS3DH_FIFO_SRC_REG_FSS2 = 0x04, + LIS3DH_FIFO_SRC_REG_FSS3 = 0x08, + LIS3DH_FIFO_SRC_REG_FSS4 = 0x10, + _LIS3DH_FIFO_SRC_REG_FSS_MASK = 0x1F, // 0b11111 + _LIS3DH_FIFO_SRC_REG_FSS_SHIFT = 0, + + LIS3DH_FIFO_SRC_REG_EMPTY = 0x20, + LIS3DH_FIFO_SRC_REG_OVRN_FIFO = 0x40, + LIS3DH_FIFO_SRC_REG_WTM = 0x80, +} LIS3DH_FIFO_SRC_REG_BITS_T; + +/** + * REG_INT1_CFG bits + */ +typedef enum { + LIS3DH_INT1_CFG_XLIE = 0x01, + LIS3DH_INT1_CFG_XHIE = 0x02, + + LIS3DH_INT1_CFG_YLIE = 0x04, + LIS3DH_INT1_CFG_YHIE = 0x08, + + LIS3DH_INT1_CFG_ZLIE = 0x10, + LIS3DH_INT1_CFG_ZHIE = 0x20, + + LIS3DH_INT1_CFG_6D = 0x40, + LIS3DH_INT1_CFG_AOI = 0x80, +} LIS3DH_INT1_CFG_BITS_T; + +/** + * REG_INT1_SRC bits + */ +typedef enum { + LIS3DH_INT1_SRC_XL = 0x01, + LIS3DH_INT1_SRC_XH = 0x02, + + LIS3DH_INT1_SRC_YL = 0x04, + LIS3DH_INT1_SRC_YH = 0x08, + + LIS3DH_INT1_SRC_ZL = 0x10, + LIS3DH_INT1_SRC_ZH = 0x20, + + LIS3DH_INT1_SRC_IA = 0x40, + + // 0x80 reserved +} LIS3DH_INT1_SRC_BITS_T; + +/** + * REG_INT2_CFG bits + */ +typedef enum { + LIS3DH_INT2_CFG_XLIE = 0x01, + LIS3DH_INT2_CFG_XHIE = 0x02, + + LIS3DH_INT2_CFG_YLIE = 0x04, + LIS3DH_INT2_CFG_YHIE = 0x08, + + LIS3DH_INT2_CFG_ZLIE = 0x10, + LIS3DH_INT2_CFG_ZHIE = 0x20, + + LIS3DH_INT2_CFG_6D = 0x40, + LIS3DH_INT2_CFG_AOI = 0x80, +} LIS3DH_INT2_CFG_BITS_T; + +/** + * REG_INT2_SRC bits + */ +typedef enum { + LIS3DH_INT2_SRC_XL = 0x01, + LIS3DH_INT2_SRC_XH = 0x02, + + LIS3DH_INT2_SRC_YL = 0x04, + LIS3DH_INT2_SRC_YH = 0x08, + + LIS3DH_INT2_SRC_ZL = 0x10, + LIS3DH_INT2_SRC_ZH = 0x20, + + LIS3DH_INT2_SRC_IA = 0x40, + + // 0x80 reserved +} LIS3DH_INT2_SRC_BITS_T; + +/** + * REG_CLICK_CFG bits + */ +typedef enum { + LIS3DH_CLICK_CFG_XS = 0x01, + LIS3DH_CLICK_CFG_XD = 0x02, + + LIS3DH_CLICK_CFG_YS = 0x04, + LIS3DH_CLICK_CFG_YD = 0x08, + + LIS3DH_CLICK_CFG_ZS = 0x10, + LIS3DH_CLICK_CFG_ZD = 0x20, + + // 0x40-0x80 reserved +} LIS3DH_CLICK_CFG_BITS_T; + +/** + * REG_CLICK_SRC bits + */ +typedef enum { + LIS3DH_CLICK_SRC_X = 0x01, + LIS3DH_CLICK_SRC_Y = 0x02, + LIS3DH_CLICK_SRC_Z = 0x04, + + LIS3DH_CLICK_SRC_SIGN = 0x08, + + LIS3DH_CLICK_SRC_SCLICK = 0x10, + LIS3DH_CLICK_SRC_DCLICK = 0x20, + + LIS3DH_CLICK_SRC_IA = 0x40, + + // 0x80 reserved +} LIS3DH_CLICK_SRC_BITS_T; + +/** + * REG_CLICK_THS bits + */ +typedef enum { + LIS3DH_CLICK_THS_THS0 = 0x01, + LIS3DH_CLICK_THS_THS1 = 0x02, + LIS3DH_CLICK_THS_THS2 = 0x04, + LIS3DH_CLICK_THS_THS3 = 0x08, + LIS3DH_CLICK_THS_THS4 = 0x10, + LIS3DH_CLICK_THS_THS5 = 0x20, + LIS3DH_CLICK_THS_THS6 = 0x40, + _LIS3DH_CLICK_THS_THS_MASK = 0x7F, // 0b1111111 + _LIS3DH_CLICK_THS_THS_SHIFT = 0, + + LIS3DH_CLICK_THS_LIR_CLICK = 0x80, +} LIS3DH_CLICK_THS_BITS_T; + +// Interrupt selection for installISR() and uninstallISR() +typedef enum { LIS3DH_INTERRUPT_INT1, LIS3DH_INTERRUPT_INT2 } LIS3DH_INTERRUPT_PINS_T; + +#ifdef __cplusplus +} +#endif diff --git a/src/lis3dh/lis3dh_fti.c b/src/lis3dh/lis3dh_fti.c new file mode 100644 index 000000000..cb96c8bae --- /dev/null +++ b/src/lis3dh/lis3dh_fti.c @@ -0,0 +1,116 @@ +/* + * Author: Alex Tereschenko + * Copyright (c) 2018 Alex Tereschenko. + * + * Based on LIS2DS12 module by + * 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 "lis3dh.h" +#include "upm_fti.h" + +/** + * This file implements the Function Table Interface (FTI) for this sensor + */ + +const char upm_lis3dh_name[] = "LIS3DH"; +const char upm_lis3dh_description[] = "Triple Axis Digital Accelerometer"; +const upm_protocol_t upm_lis3dh_protocol[] = { UPM_I2C, UPM_SPI, UPM_GPIO }; +const upm_sensor_t upm_lis3dh_category[] = { UPM_ACCELEROMETER }; + +// Forward declarations +const void* upm_lis3dh_get_ft(upm_sensor_t sensor_type); +void* upm_lis3dh_init_name(); +void upm_lis3dh_close(void* dev); +upm_result_t upm_lis3dh_get_value(void* dev, float* value, upm_acceleration_u unit); + +const upm_sensor_descriptor_t +upm_lis3dh_get_descriptor() +{ + upm_sensor_descriptor_t usd; + usd.name = upm_lis3dh_name; + usd.description = upm_lis3dh_description; + usd.protocol_size = 3; + usd.protocol = upm_lis3dh_protocol; + usd.category_size = 1; + usd.category = upm_lis3dh_category; + return usd; +} + +static const upm_sensor_ft ft = { + .upm_sensor_init_name = upm_lis3dh_init_name, + .upm_sensor_close = upm_lis3dh_close, +}; + +static const upm_acceleration_ft aft = {.upm_acceleration_get_value = upm_lis3dh_get_value }; + +const void* +upm_lis3dh_get_ft(upm_sensor_t sensor_type) +{ + switch (sensor_type) { + case UPM_SENSOR: + return &ft; + + case UPM_ACCELEROMETER: + return &aft; + + default: + return NULL; + } +} + +void* +upm_lis3dh_init_name() +{ + return NULL; +} + + +void +upm_lis3dh_close(void* dev) +{ + lis3dh_close((lis3dh_context) dev); +} + +upm_result_t +upm_lis3dh_get_value(void* dev, float* value, upm_acceleration_u unit) +{ + if (lis3dh_update((lis3dh_context) dev)) { + return UPM_ERROR_OPERATION_FAILED; + } + + // No conversion facility in place yet, so we don't do anything + // with units + + float x, y, z; + + lis3dh_get_accelerometer(dev, &x, &y, &z); + + value[0] = x; + value[1] = y; + value[2] = z; + + return UPM_SUCCESS; +} diff --git a/src/lis3dh/pyupm_lis3dh.i b/src/lis3dh/pyupm_lis3dh.i new file mode 100644 index 000000000..0bebc061f --- /dev/null +++ b/src/lis3dh/pyupm_lis3dh.i @@ -0,0 +1,15 @@ +// Include doxygen-generated documentation +%include "pyupm_doxy2swig.i" +%module pyupm_lis3dh +%include "../upm.i" +%include "../upm_vectortypes.i" + +#ifdef DOXYGEN +%include "lis3dh_doc.i" +#endif + +%include "lis3dh_defs.h" +%include "lis3dh.hpp" +%{ + #include "lis3dh.hpp" +%}