From c8d0aee8735164e4a34dfb4ec87f198a793864a1 Mon Sep 17 00:00:00 2001 From: Jon Trulson Date: Fri, 24 Apr 2015 18:53:54 -0600 Subject: [PATCH] h3lis331dl: Initial implementation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The driver implements support for the Grove 3-Axis Digital Accelerometer(±400g), using the h3lis331dl chip. Signed-off-by: Jon Trulson Signed-off-by: Mihai Tudor Panu --- examples/c++/CMakeLists.txt | 3 + examples/c++/h3lis331dl.cxx | 80 ++++ examples/javascript/h3lis331dl.js | 91 +++++ examples/python/h3lis331dl.py | 76 ++++ src/h3lis331dl/CMakeLists.txt | 5 + src/h3lis331dl/h3lis331dl.cxx | 569 +++++++++++++++++++++++++++ src/h3lis331dl/h3lis331dl.h | 613 ++++++++++++++++++++++++++++++ src/h3lis331dl/jsupm_h3lis331dl.i | 13 + src/h3lis331dl/pyupm_h3lis331dl.i | 18 + 9 files changed, 1468 insertions(+) create mode 100644 examples/c++/h3lis331dl.cxx create mode 100644 examples/javascript/h3lis331dl.js create mode 100644 examples/python/h3lis331dl.py create mode 100644 src/h3lis331dl/CMakeLists.txt create mode 100644 src/h3lis331dl/h3lis331dl.cxx create mode 100644 src/h3lis331dl/h3lis331dl.h create mode 100644 src/h3lis331dl/jsupm_h3lis331dl.i create mode 100644 src/h3lis331dl/pyupm_h3lis331dl.i diff --git a/examples/c++/CMakeLists.txt b/examples/c++/CMakeLists.txt index 408bb38c..1072f149 100644 --- a/examples/c++/CMakeLists.txt +++ b/examples/c++/CMakeLists.txt @@ -119,6 +119,7 @@ add_executable (si114x-example si114x.cxx) add_executable (maxsonarez-example maxsonarez.cxx) add_executable (hm11-example hm11.cxx) add_executable (ht9170-example ht9170.cxx) +add_executable (h3lis331dl-example h3lis331dl.cxx) include_directories (${PROJECT_SOURCE_DIR}/src/hmc5883l) include_directories (${PROJECT_SOURCE_DIR}/src/grove) @@ -216,6 +217,7 @@ include_directories (${PROJECT_SOURCE_DIR}/src/si114x) include_directories (${PROJECT_SOURCE_DIR}/src/maxsonarez) include_directories (${PROJECT_SOURCE_DIR}/src/hm11) include_directories (${PROJECT_SOURCE_DIR}/src/ht9170) +include_directories (${PROJECT_SOURCE_DIR}/src/h3lis331dl) target_link_libraries (hmc5883l-example hmc5883l ${CMAKE_THREAD_LIBS_INIT}) target_link_libraries (groveled-example grove ${CMAKE_THREAD_LIBS_INIT}) @@ -336,3 +338,4 @@ target_link_libraries (si114x-example si114x ${CMAKE_THREAD_LIBS_INIT}) target_link_libraries (maxsonarez-example maxsonarez ${CMAKE_THREAD_LIBS_INIT}) target_link_libraries (hm11-example hm11 ${CMAKE_THREAD_LIBS_INIT}) target_link_libraries (ht9170-example ht9170 ${CMAKE_THREAD_LIBS_INIT}) +target_link_libraries (h3lis331dl-example h3lis331dl ${CMAKE_THREAD_LIBS_INIT}) diff --git a/examples/c++/h3lis331dl.cxx b/examples/c++/h3lis331dl.cxx new file mode 100644 index 00000000..d4a04ca0 --- /dev/null +++ b/examples/c++/h3lis331dl.cxx @@ -0,0 +1,80 @@ +/* + * Author: Jon Trulson + * Copyright (c) 2015 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 +#include "h3lis331dl.h" + +using namespace std; +using namespace upm; + +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 H3LIS331DL on I2C bus 0 + + upm::H3LIS331DL *accel = new upm::H3LIS331DL(H3LIS331DL_I2C_BUS, + H3LIS331DL_DEFAULT_I2C_ADDR); + + // Initialize the device with default values + accel->init(); + + while (shouldRun) + { + int x, y, z; + float ax, ay, az; + + accel->update(); + + accel->getRawXYZ(&x, &y, &z); + accel->getAcceleration(&ax, &ay, &az); + + cout << "Raw: X = " << x << " Y = " << y << " Z = " << z << endl; + + cout << "Acceleration: AX = " << ax << " AY = " << ay << " AZ = " << az + << endl; + + cout << endl; + + usleep(500000); + } + +//! [Interesting] + + cout << "Exiting..." << endl; + + delete accel; + return 0; +} diff --git a/examples/javascript/h3lis331dl.js b/examples/javascript/h3lis331dl.js new file mode 100644 index 00000000..ae38cd5e --- /dev/null +++ b/examples/javascript/h3lis331dl.js @@ -0,0 +1,91 @@ +/*jslint node:true, vars:true, bitwise:true, unparam:true */ +/*jshint unused:true */ + +/* +* Author: Jon Trulson +* Copyright (c) 2015 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 digitalAccelerometer = require('jsupm_h3lis331dl'); + +// Instantiate an H3LIS331DL on I2C bus 0 +var myDigitalAccelerometer = new digitalAccelerometer.H3LIS331DL( + digitalAccelerometer.H3LIS331DL_I2C_BUS, + digitalAccelerometer.H3LIS331DL_DEFAULT_I2C_ADDR); + +// Initialize the device with default values +myDigitalAccelerometer.init(); + +var x, y, z; +x = digitalAccelerometer.new_intp(); +y = digitalAccelerometer.new_intp(); +z = digitalAccelerometer.new_intp(); + +var ax, ay, az; +ax = digitalAccelerometer.new_floatp(); +ay = digitalAccelerometer.new_floatp(); +az = digitalAccelerometer.new_floatp(); + +var outputStr; + +var myInterval = setInterval(function() +{ + myDigitalAccelerometer.update(); + myDigitalAccelerometer.getRawXYZ(x, y, z); + outputStr = "Raw: X = " + digitalAccelerometer.intp_value(x) + + " Y = " + digitalAccelerometer.intp_value(y) + + " Z = " + digitalAccelerometer.intp_value(z); + console.log(outputStr); + + myDigitalAccelerometer.getAcceleration(ax, ay, az); + outputStr = "Acceleration: AX = " + + roundNum(digitalAccelerometer.floatp_value(ax), 6) + + " AY = " + roundNum(digitalAccelerometer.floatp_value(ay), 6) + + " AZ = " + roundNum(digitalAccelerometer.floatp_value(az), 6); + console.log(outputStr); +}, 500); + +// round off output to match C example, which has 6 decimal places +function roundNum(num, decimalPlaces) +{ + var extraNum = (1 / (Math.pow(10, decimalPlaces) * 1000)); + return (Math.round((num + extraNum) + * (Math.pow(10, decimalPlaces))) / Math.pow(10, decimalPlaces)); +} + +// When exiting: clear interval and print message +process.on('SIGINT', function() +{ + clearInterval(myInterval); + + // clean up memory + digitalAccelerometer.delete_intp(x); + digitalAccelerometer.delete_intp(y); + digitalAccelerometer.delete_intp(z); + + digitalAccelerometer.delete_floatp(ax); + digitalAccelerometer.delete_floatp(ay); + digitalAccelerometer.delete_floatp(az); + + console.log("Exiting..."); + process.exit(0); +}); diff --git a/examples/python/h3lis331dl.py b/examples/python/h3lis331dl.py new file mode 100644 index 00000000..60142f51 --- /dev/null +++ b/examples/python/h3lis331dl.py @@ -0,0 +1,76 @@ +#!/usr/bin/python +# Author: Jon Trulson +# Copyright (c) 2015 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 time, sys, signal, atexit +import pyupm_h3lis331dl as upmH3LIS331DL + +# Instantiate an H3LIS331DL on I2C bus 0 +myDigitalAccelerometer = upmH3LIS331DL.H3LIS331DL( + upmH3LIS331DL.H3LIS331DL_I2C_BUS, + upmH3LIS331DL.H3LIS331DL_DEFAULT_I2C_ADDR); + + +## 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, including functions from myDigitalAccelerometer +def exitHandler(): + print "Exiting" + sys.exit(0) + +# Register exit handlers +atexit.register(exitHandler) +signal.signal(signal.SIGINT, SIGINTHandler) + + +# Initialize the device with default values +myDigitalAccelerometer.init() + +x = upmH3LIS331DL.new_intp() +y = upmH3LIS331DL.new_intp() +z = upmH3LIS331DL.new_intp() + +ax = upmH3LIS331DL.new_floatp() +ay = upmH3LIS331DL.new_floatp() +az = upmH3LIS331DL.new_floatp() + +while (1): + myDigitalAccelerometer.update() + myDigitalAccelerometer.getRawXYZ(x, y, z) + outputStr = ("Raw: X = {0}" + " Y = {1}" + " Z = {2}").format(upmH3LIS331DL.intp_value(x), + upmH3LIS331DL.intp_value(y), + upmH3LIS331DL.intp_value(z)) + print outputStr + + myDigitalAccelerometer.getAcceleration(ax, ay, az) + outputStr = ("Acceleration: AX = {0}" + " AY = {1}" + " AZ = {2}").format(upmH3LIS331DL.floatp_value(ax), + upmH3LIS331DL.floatp_value(ay), + upmH3LIS331DL.floatp_value(az)) + print outputStr + time.sleep(.5) diff --git a/src/h3lis331dl/CMakeLists.txt b/src/h3lis331dl/CMakeLists.txt new file mode 100644 index 00000000..cec9f55e --- /dev/null +++ b/src/h3lis331dl/CMakeLists.txt @@ -0,0 +1,5 @@ +set (libname "h3lis331dl") +set (libdescription "upm h3lis331dl I2c Accelerometer (400g)") +set (module_src ${libname}.cxx) +set (module_h ${libname}.h) +upm_module_init() diff --git a/src/h3lis331dl/h3lis331dl.cxx b/src/h3lis331dl/h3lis331dl.cxx new file mode 100644 index 00000000..457c5d56 --- /dev/null +++ b/src/h3lis331dl/h3lis331dl.cxx @@ -0,0 +1,569 @@ +/* + * Author: Jon Trulson + * Copyright (c) 2015 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 +#include + +#include "h3lis331dl.h" + +using namespace upm; +using namespace std; + + +H3LIS331DL::H3LIS331DL(int bus, uint8_t address): + m_i2c(bus) +{ + m_addr = address; + + mraa_result_t rv; + if ( (rv = m_i2c.address(m_addr)) != MRAA_SUCCESS) + { + cerr << "H3LIS331DL: Could not initialize i2c address. " << endl; + mraa_result_print(rv); + return; + } + + m_rawX = m_rawY = m_rawZ = 0; + setAdjustmentOffsets(0, 0, 0); +} + +H3LIS331DL::~H3LIS331DL() +{ +} + +bool H3LIS331DL::init(DR_BITS_T odr, PM_BITS_T pm, FS_BITS_T fs) +{ + if (!setDataRate(odr)) + return false; + if (!setPowerMode(pm)) + return false; + if (!setFullScale(fs)) + return false; + + // now enable X, Y, and Z axes + if (enableAxis(REG1_XEN | REG1_YEN | REG1_ZEN)) + return false; + + return true; +} + +uint8_t H3LIS331DL::getChipID() +{ + return m_i2c.readReg(REG_WHOAMI); +} + +bool H3LIS331DL::setDataRate(DR_BITS_T odr) +{ + uint8_t reg1 = m_i2c.readReg(REG_REG1); + + reg1 &= ~(REG1_DR0 | REG1_DR1); + reg1 |= (odr << REG1_DR_SHIFT); + + if (m_i2c.writeReg(REG_REG1, reg1)) + { + cerr << __FUNCTION__ << "@" << __LINE__ << ": writeReg failed" << endl; + return false; + } + + return true; +} + +bool H3LIS331DL::setPowerMode(PM_BITS_T pm) +{ + uint8_t reg1 = m_i2c.readReg(REG_REG1); + + reg1 &= ~(REG1_PM0 | REG1_PM1 | REG1_PM2); + reg1 |= (pm << REG1_PM_SHIFT); + + if (m_i2c.writeReg(REG_REG1, reg1)) + { + cerr << __FUNCTION__ << "@" << __LINE__ << ": writeReg failed" << endl; + return false; + } + + return true; +} + +bool H3LIS331DL::enableAxis(uint8_t axisEnable) +{ + uint8_t reg1 = m_i2c.readReg(REG_REG1); + + reg1 &= ~(REG1_XEN | REG1_YEN | REG1_ZEN); + reg1 |= (axisEnable & (REG1_XEN | REG1_YEN | REG1_ZEN)); + + if (m_i2c.writeReg(REG_REG1, reg1)) + { + cerr << __FUNCTION__ << "@" << __LINE__ << ": writeReg failed" << endl; + return false; + } + + return true; +} + +bool H3LIS331DL::setFullScale(FS_BITS_T fs) +{ + uint8_t reg4 = m_i2c.readReg(REG_REG4); + + reg4 &= ~(REG4_FS0 | REG4_FS1); + reg4 |= (fs << REG4_FS_SHIFT); + + if (m_i2c.writeReg(REG_REG4, reg4)) + { + cerr << __FUNCTION__ << "@" << __LINE__ << ": writeReg failed" << endl; + return false; + } + + return true; +} + +bool H3LIS331DL::setHPCF(HPCF_BITS_T val) +{ + uint8_t reg = m_i2c.readReg(REG_REG2); + + reg &= ~(REG2_HPCF0 | REG2_HPCF1); + reg |= (val << REG2_HPCF_SHIFT); + + if (m_i2c.writeReg(REG_REG2, reg)) + { + cerr << __FUNCTION__ << "@" << __LINE__ << ": writeReg failed" << endl; + return false; + } + + return true; +} + +bool H3LIS331DL::setHPM(HPM_BITS_T val) +{ + uint8_t reg = m_i2c.readReg(REG_REG2); + + reg &= ~(REG2_HPM0 | REG2_HPM1); + reg |= (val << REG2_HPM_SHIFT); + + if (m_i2c.writeReg(REG_REG2, reg)) + { + cerr << __FUNCTION__ << "@" << __LINE__ << ": writeReg failed" << endl; + return false; + } + + return true; +} + +bool H3LIS331DL::boot() +{ + uint8_t reg = m_i2c.readReg(REG_REG2); + + reg |= REG2_BOOT; + + if (m_i2c.writeReg(REG_REG2, reg)) + { + cerr << __FUNCTION__ << "@" << __LINE__ << ": writeReg failed" << endl; + return false; + } + + // wait for the boot bit to clear + do { + reg = m_i2c.readReg(REG_REG2); + usleep(200000); + } while (reg & REG2_BOOT); + + return true; +} + +bool H3LIS331DL::enableHPF1(bool enable) +{ + uint8_t reg = m_i2c.readReg(REG_REG2); + + if (enable) + reg |= REG2_HPEN1; + else + reg &= ~REG2_HPEN1; + + if (m_i2c.writeReg(REG_REG2, reg)) + { + cerr << __FUNCTION__ << "@" << __LINE__ << ": writeReg failed" << endl; + return false; + } + + return true; +} + +bool H3LIS331DL::enableHPF2(bool enable) +{ + uint8_t reg = m_i2c.readReg(REG_REG2); + + if (enable) + reg |= REG2_HPEN2; + else + reg &= ~REG2_HPEN2; + + if (m_i2c.writeReg(REG_REG2, reg)) + { + cerr << __FUNCTION__ << "@" << __LINE__ << ": writeReg failed" << endl; + return false; + } + + return true; +} + +bool H3LIS331DL::enableFDS(bool enable) +{ + uint8_t reg = m_i2c.readReg(REG_REG2); + + if (enable) + reg |= REG2_FDS; + else + reg &= ~REG2_FDS; + + if (m_i2c.writeReg(REG_REG2, reg)) + { + cerr << __FUNCTION__ << "@" << __LINE__ << ": writeReg failed" << endl; + return false; + } + + return true; +} + +bool H3LIS331DL::setInterruptActiveLow(bool enable) +{ + uint8_t reg = m_i2c.readReg(REG_REG3); + + if (enable) + reg |= REG3_IHL; + else + reg &= ~REG3_IHL; + + if (m_i2c.writeReg(REG_REG3, reg)) + { + cerr << __FUNCTION__ << "@" << __LINE__ << ": writeReg failed" << endl; + return false; + } + + return true; +} + +bool H3LIS331DL::setInterruptOpenDrain(bool enable) +{ + uint8_t reg = m_i2c.readReg(REG_REG3); + + if (enable) + reg |= REG3_PP_OD; + else + reg &= ~REG3_PP_OD; + + if (m_i2c.writeReg(REG_REG3, reg)) + { + cerr << __FUNCTION__ << "@" << __LINE__ << ": writeReg failed" << endl; + return false; + } + + return true; +} + +bool H3LIS331DL::setInterrupt1Latch(bool enable) +{ + uint8_t reg = m_i2c.readReg(REG_REG3); + + if (enable) + reg |= REG3_LIR1; + else + reg &= ~REG3_LIR1; + + if (m_i2c.writeReg(REG_REG3, reg)) + { + cerr << __FUNCTION__ << "@" << __LINE__ << ": writeReg failed" << endl; + return false; + } + + return true; +} + +bool H3LIS331DL::setInterrupt2Latch(bool enable) +{ + uint8_t reg = m_i2c.readReg(REG_REG3); + + if (enable) + reg |= REG3_LIR2; + else + reg &= ~REG3_LIR2; + + if (m_i2c.writeReg(REG_REG3, reg)) + { + cerr << __FUNCTION__ << "@" << __LINE__ << ": writeReg failed" << endl; + return false; + } + + return true; +} + +bool H3LIS331DL::setInterrupt1PadConfig(I_CFG_BITS_T val) +{ + uint8_t reg = m_i2c.readReg(REG_REG3); + + reg &= ~(REG3_I1_CFG0 | REG3_I1_CFG1); + reg |= (val << REG3_I1_CFG_SHIFT); + + if (m_i2c.writeReg(REG_REG3, reg)) + { + cerr << __FUNCTION__ << "@" << __LINE__ << ": writeReg failed" << endl; + return false; + } + + return true; +} + +bool H3LIS331DL::setInterrupt2PadConfig(I_CFG_BITS_T val) +{ + uint8_t reg = m_i2c.readReg(REG_REG3); + + reg &= ~(REG3_I2_CFG0 | REG3_I2_CFG1); + reg |= (val << REG3_I2_CFG_SHIFT); + + if (m_i2c.writeReg(REG_REG3, reg)) + { + cerr << __FUNCTION__ << "@" << __LINE__ << ": writeReg failed" << endl; + return false; + } + + return true; +} + + +bool H3LIS331DL::enableBDU(bool enable) +{ + uint8_t reg = m_i2c.readReg(REG_REG4); + + if (enable) + reg |= REG4_BDU; + else + reg &= ~REG4_BDU; + + if (m_i2c.writeReg(REG_REG4, reg)) + { + cerr << __FUNCTION__ << "@" << __LINE__ << ": writeReg failed" << endl; + return false; + } + + return true; +} + +bool H3LIS331DL::enableBLE(bool enable) +{ + uint8_t reg = m_i2c.readReg(REG_REG4); + + if (enable) + reg |= REG4_BLE; + else + reg &= ~REG4_BLE; + + if (m_i2c.writeReg(REG_REG4, reg)) + { + cerr << __FUNCTION__ << "@" << __LINE__ << ": writeReg failed" << endl; + return false; + } + + return true; +} + +bool H3LIS331DL::enableSleepToWake(bool enable) +{ + uint8_t reg = m_i2c.readReg(REG_REG5); + + if (enable) + reg |= (REG5_TURNON0 | REG5_TURNON1); + else + reg &= ~(REG5_TURNON0 | REG5_TURNON1); + + if (m_i2c.writeReg(REG_REG5, reg)) + { + cerr << __FUNCTION__ << "@" << __LINE__ << ": writeReg failed" << endl; + return false; + } + + return true; +} + +uint8_t H3LIS331DL::getStatus() +{ + return m_i2c.readReg(REG_STATUS); +} + +bool H3LIS331DL::setInterrupt1Config(uint8_t val) +{ + uint8_t reg = m_i2c.readReg(REG_INT1_CFG); + + // mask off reserved bit + reg = (val & ~0x40); + + if (m_i2c.writeReg(REG_INT1_CFG, reg)) + { + cerr << __FUNCTION__ << "@" << __LINE__ << ": writeReg failed" << endl; + return false; + } + + return true; +} + +bool H3LIS331DL::setInterrupt1Source(uint8_t val) +{ + uint8_t reg = m_i2c.readReg(REG_INT1_SRC); + + // mask off reserved bit + reg = (val & ~0x80); + + if (m_i2c.writeReg(REG_INT1_SRC, reg)) + { + cerr << __FUNCTION__ << "@" << __LINE__ << ": writeReg failed" << endl; + return false; + } + + return true; +} + +bool H3LIS331DL::setInterrupt1Threshold(uint8_t val) +{ + if (m_i2c.writeReg(REG_INT1_THS, val)) + { + cerr << __FUNCTION__ << "@" << __LINE__ << ": writeReg failed" << endl; + return false; + } + + return true; +} + +bool H3LIS331DL::setInterrupt1Duration(uint8_t val) +{ + if (m_i2c.writeReg(REG_INT1_DUR, val)) + { + cerr << __FUNCTION__ << "@" << __LINE__ << ": writeReg failed" << endl; + return false; + } + + return true; +} + +bool H3LIS331DL::setInterrupt2Config(uint8_t val) +{ + uint8_t reg = m_i2c.readReg(REG_INT2_CFG); + + // mask off reserved bit + reg = (val & ~0x40); + + if (m_i2c.writeReg(REG_INT2_CFG, reg)) + { + cerr << __FUNCTION__ << "@" << __LINE__ << ": writeReg failed" << endl; + return false; + } + + return true; +} + +bool H3LIS331DL::setInterrupt2Source(uint8_t val) +{ + uint8_t reg = m_i2c.readReg(REG_INT2_SRC); + + // mask off reserved bit + reg = (val & ~0x80); + + if (m_i2c.writeReg(REG_INT2_SRC, reg)) + { + cerr << __FUNCTION__ << "@" << __LINE__ << ": writeReg failed" << endl; + return false; + } + + return true; +} + +bool H3LIS331DL::setInterrupt2Threshold(uint8_t val) +{ + if (m_i2c.writeReg(REG_INT2_THS, val)) + { + cerr << __FUNCTION__ << "@" << __LINE__ << ": writeReg failed" << endl; + return false; + } + + return true; +} + +bool H3LIS331DL::setInterrupt2Duration(uint8_t val) +{ + if (m_i2c.writeReg(REG_INT2_DUR, val)) + { + cerr << __FUNCTION__ << "@" << __LINE__ << ": writeReg failed" << endl; + return false; + } + + return true; +} + +void H3LIS331DL::update() +{ + uint8_t low, high; + + // X + low = m_i2c.readReg(REG_OUT_X_L); + high = m_i2c.readReg(REG_OUT_X_H); + m_rawX = ((high << 8) | low); + + // Y + low = m_i2c.readReg(REG_OUT_Y_L); + high = m_i2c.readReg(REG_OUT_Y_H); + m_rawY = ((high << 8) | low); + + // Z + low = m_i2c.readReg(REG_OUT_Z_L); + high = m_i2c.readReg(REG_OUT_Z_H); + m_rawZ = ((high << 8) | low); +} + +void H3LIS331DL::setAdjustmentOffsets(int adjX, int adjY, int adjZ) +{ + m_adjX = adjX; + m_adjY = adjY; + m_adjZ = adjZ; +} + +void H3LIS331DL::getAcceleration(float *aX, float *aY, float *aZ) +{ + const float gains = 0.003; // Seeed magic number? + + *aX = float(m_rawX - m_adjX) * gains; + *aY = float(m_rawY - m_adjY) * gains; + *aZ = float(m_rawZ - m_adjZ) * gains; +} + +void H3LIS331DL::getRawXYZ(int *x, int *y, int*z) +{ + *x = m_rawX; + *y = m_rawY; + *z = m_rawZ; +} + +void H3LIS331DL::getXYZ(int *x, int *y, int*z) +{ + *x = (m_rawX - m_adjX); + *y = (m_rawY - m_adjY); + *z = (m_rawZ - m_adjZ); +} diff --git a/src/h3lis331dl/h3lis331dl.h b/src/h3lis331dl/h3lis331dl.h new file mode 100644 index 00000000..a0ed7415 --- /dev/null +++ b/src/h3lis331dl/h3lis331dl.h @@ -0,0 +1,613 @@ +/* + * Author: Jon Trulson + * Copyright (c) 2015 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. + */ +#pragma once + +#include +#include + +#define H3LIS331DL_I2C_BUS 0 +#define H3LIS331DL_DEFAULT_I2C_ADDR 0x18 + +namespace upm { + + /** + * @brief Grove 3-axis I2C Accelerometer (400G) + * @defgroup h3lis331dl libupm-h3lis331dl + * @ingroup seeed i2c accelerometer + */ + + /** + * @library h3lis331dl + * @sensor h3lis331dl + * @comname Grove 3-axis I2C Accelerometer (400G) + * @type accelerometer + * @man seeed + * @web http://www.seeedstudio.com/depot/Grove-3Axis-Digital-Accelerometer400g-p-1897.html + * @con i2c + * + * @brief C++ API for the H3LIS331DL based Grove 3-axis I2C Accelerometer + * (400G) + * + * @image html h3lis331dl.jpg + * @snippet h3lis331dl.cxx Interesting + */ + class H3LIS331DL { + public: + + /** + * H3LIS331DL registers + */ + typedef enum { + // Reserved bytes must not be written into as they contain + // factory calibration data. Changing those values may lead to + // improper functioning of the device. + + // 0x00-0x0E reserved + + REG_WHOAMI = 0x0f, + + // 0x10-0x1f reserved + + REG_REG1 = 0x20, + REG_REG2 = 0x21, + REG_REG3 = 0x22, + REG_REG4 = 0x23, + REG_REG5 = 0x24, + + REG_HP_FILTER_RESET = 0x25, + REG_REFERENCE = 0x26, + + REG_STATUS = 0x27, + + REG_OUT_X_L = 0x28, + REG_OUT_X_H = 0x29, + REG_OUT_Y_L = 0x2a, + REG_OUT_Y_H = 0x2b, + REG_OUT_Z_L = 0x2c, + REG_OUT_Z_H = 0x2d, + + // 0x2e, 0x2f reserved + + REG_INT1_CFG = 0x30, + REG_INT1_SRC = 0x31, + REG_INT1_THS = 0x32, + REG_INT1_DUR = 0x33, + + REG_INT2_CFG = 0x34, + REG_INT2_SRC = 0x35, + REG_INT2_THS = 0x36, + REG_INT2_DUR = 0x37, + + // 0x38-0x3f reserved + } H3LIS331DL_REG_T; + + /** + * REG1 bits + */ + typedef enum { + REG1_XEN = 0x01, // X axis enable + REG1_YEN = 0x02, + REG1_ZEN = 0x04, + + REG1_DR0 = 0x08, // data rate + REG1_DR1 = 0x10, + REG1_DR_SHIFT = 3, // DR shift + + REG1_PM0 = 0x20, // power mode + REG1_PM1 = 0x40, + REG1_PM2 = 0x80, + REG1_PM_SHIFT = 5 + } REG1_BITS_T; + + /** + * REG1 DR (output rate) bits + */ + typedef enum { + DR_50_37 = 0x0, // 50Hz output with 37Hz LPF cutoff + DR_100_74 = 0x1, + DR_400_292 = 0x2, + DR_1000_780 = 0x3 + } DR_BITS_T; + + /** + * REG1 PM (Power mode) bits + */ + typedef enum { + PM_POWERDWN = 0x0, + PM_NORMAL = 0x1, + PM_LP05 = 0x2, // .5 updates/sec + PM_LP1 = 0x3, // 1 update/sec + PM_LP2 = 0x4, + PM_LP5 = 0x5, + PM_LP10 = 0x6 + } PM_BITS_T; + + /** + * REG2 bits + */ + typedef enum { + REG2_HPCF0 = 0x01, + REG2_HPCF1 = 0x02, + REG2_HPCF_SHIFT = 0, + + REG2_HPEN1 = 0x04, + REG2_HPEN2 = 0x08, + REG2_FDS = 0x10, + + REG2_HPM0 = 0x20, + REG2_HPM1 = 0x40, + REG2_HPM_SHIFT = 5, + + REG2_BOOT = 0x80 + } REG2_BITS_T; + + /** + * REG2 HPCF (High Pass Cutoff Frequency) bits + */ + typedef enum { + HPCF_8 = 0x0, + HPCF_16 = 0x1, + HPCF_32 = 0x2, + HPCF_64 = 0x3, + } HPCF_BITS_T; + + /** + * REG2 HPM (High Pass Filter Mode) bits + */ + typedef enum { + HPM_NORMAL0 = 0x0, + HPM_REF = 0x1, + HPM_NORMAL1 = 0x2 + } HPM_BITS_T; + + /** + * REG3 bits + */ + typedef enum { + REG3_I1_CFG0 = 0x01, + REG3_I1_CFG1 = 0x02, + REG3_I1_CFG_SHIFT = 0, + + REG3_LIR1 = 0x04, + + REG3_I2_CFG0 = 0x08, + REG3_I2_CFG1 = 0x10, + REG3_I2_CFG_SHIFT = 3, + + REG3_LIR2 = 0x20, + REG3_PP_OD = 0x40, + REG3_IHL = 0x80 + } REG3_BITS_T; + + /** + * REG3 I1/I2 PAD control bits + */ + typedef enum { + I_SRC = 0x0, // INT source + I_OR = 0x1, // INT1 OR INT2 source + I_DR = 0x2, // Data Ready + I_BOOTING = 0x3 // Boot is running + } I_CFG_BITS_T; + + /** + * REG4 bits + */ + typedef enum { + REG4_SIM = 0x01, // SPI 4 or 3 wire + + // bits 01,02,04 reserved + + REG4_FS0 = 0x10, + REG4_FS1 = 0x20, + REG4_FS_SHIFT = 4, + + REG4_BLE = 0x40, // big/little endian + REG4_BDU = 0x80 // Block data update + } REG4_BITS_T; + + /** + * REG4 FS (Full Scale) bits + */ + typedef enum { + FS_100 = 0x0, // 100g scale + FS_200 = 0x1, // 200g scale + FS_400 = 0x3 // 400g scale + } FS_BITS_T; + + /** + * REG5 TURNON (sleep to wake) bits + */ + typedef enum { + REG5_TURNON0 = 0x01, // turnon mode for sleep-to-wake + REG5_TURNON1 = 0x02 + + // bits 04-80 reserved + } REG5_BITS_T; + + /** + * STATUS bits + */ + typedef enum { + STATUS_XDA = 0x01, // X data available + STATUS_YDA = 0x02, + STATUS_ZDA = 0x04, + STATUS_ZYXDA = 0x08, // X, Y, and Z data available + STATUS_XOR = 0x10, // X overrun + STATUS_YOR = 0x20, + STATUS_ZOR = 0x40, + STATUS_ZYXOR = 0x80 // X, Y, and Z data overrun + } STATUS_BITS_T; + + /** + * INT1/INT2 CFG bits + */ + typedef enum { + INT_CFG_XLIE = 0x01, // enable intr on low X event + INT_CFG_XHIE = 0x02, // enable intr on high X event + INT_CFG_YLIE = 0x04, + INT_CFG_YHIE = 0x08, + INT_CFG_ZLIE = 0x10, + INT_CFG_ZHIE = 0x20, + // 0x40 reserved + INT_CFG_AOI = 0x80 // AND or OR combination or intrs + } INT_CFG_BITS_T; + + /** + * INT1/INT2 SRC bits + */ + typedef enum { + INT_SRC_XL = 0x01, // X low intr event + INT_SRC_XH = 0x02, // X high intr event + INT_SRC_YL = 0x04, + INT_SRC_YH = 0x08, + INT_SRC_ZL = 0x10, + INT_SRC_ZH = 0x20, + INT_SRC_IA = 0x40 // Interrupt generated (active) + // 0x80 reserved + } INT_SRC_BITS_T; + + /** + * h3lis331dl constructor + * + * @param bus i2c bus to use + * @param address the address for this device + */ + H3LIS331DL(int bus, uint8_t address = H3LIS331DL_DEFAULT_I2C_ADDR); + + /** + * H3LIS331DL Destructor + */ + ~H3LIS331DL(); + + /** + * set up initial values and start operation + * + * @param odr the data rate: one of the DR_BITS_T values + * @param pm the power mode: one of the PM_BITS_T values + * @param fs the FullScale: one of the FS_BITS_T values + * @return true if successful + */ + bool init(DR_BITS_T odr=DR_50_37, PM_BITS_T pm=PM_NORMAL, + FS_BITS_T fs=FS_100); + + /** + * read and return the Chip ID (WHO_AM_I register) + * + * @return true if successful + */ + uint8_t getChipID(); + + /** + * set the output data rate + * + * @param one of the DR_BITS_T values + * @return true if successful + */ + bool setDataRate(DR_BITS_T odr); + + /** + * set the power mode + * + * @param one of the PM_BITS_T values + * @return true if successful + */ + bool setPowerMode(PM_BITS_T pm); + + /** + * enable one or more of the 3 axes. The arguement is a bitmsk + * composed of REG1_XEN, REG1_YEN and/or REG1_ZEN corresponding + * the axes you want enabled. + * + * @param axisEnable bitmask of axes to enable + * (REG1_XEN | REG1_YEN | REG1_ZEN) + * @return true if successful + */ + bool enableAxis(uint8_t axisEnable); + + /** + * set the scaling factor to 100, 200, or 400G's + * + * @param fs one of the FS_BITS_T values + * @return true if successful + */ + bool setFullScale(FS_BITS_T fs); + + /** + * set high pass cutoff filter + * + * @param val one of the HPCF_BITS_T values + * @return true if successful + */ + bool setHPCF(HPCF_BITS_T val); + + /** + * set high pass filter mode + * + * @param val one of the HPM_BITS_T values + * @return true if successful + */ + bool setHPM(HPM_BITS_T val); + + /** + * boot the device. Booting the device causes internal flash + * calibration values to be reloaded into the visible registers, + * in the event they have been corrupted. This function will + * return when boot is complete. + * + * @return true if successful + */ + bool boot(); + + /** + * enable high pass filter for interrupt 1 source + * + * @param enable true to enable the filter, false otherwise + * @return true if successful + */ + bool enableHPF1(bool enable); + + /** + * enable high pass filter for interrupt 2 source + * + * @param enable true to enable the filter, false otherwise + * @return true if successful + */ + bool enableHPF2(bool enable); + + /** + * enable filtered data selection + * + * @param enable true to enable, false otherwise + * @return true if successful + */ + bool enableFDS(bool enable); + + /** + * set interrupts to be active low instead of high + * + * @param enable true to enable, false otherwise + * @return true if successful + */ + bool setInterruptActiveLow(bool enable); + + /** + * set interrupt output mode to open drain rather than push/pull + * + * @param enable true to enable, false otherwise + * @return true if successful + */ + bool setInterruptOpenDrain(bool enable); + + /** + * set interrupt 1 latch enable + * + * @param enable true to enable, false otherwise + * @return true if successful + */ + bool setInterrupt1Latch(bool enable); + + /** + * set interrupt 2 latch enable + * + * @param enable true to enable, false otherwise + * @return true if successful + */ + bool setInterrupt2Latch(bool enable); + + /** + * set the interrupt 1 pad configuration + * + * @param val one fo the I_CFG_BITS_T values + * @return true if successful + */ + bool setInterrupt1PadConfig(I_CFG_BITS_T val); + + /** + * set the interrupt 2 pad configuration + * + * @param val one fo the I_CFG_BITS_T values + * @return true if successful + */ + bool setInterrupt2PadConfig(I_CFG_BITS_T val); + + /** + * enable block data update. When enabled, low/high output + * registers are not update until both low and high values have + * been read. + * + * @param enable true to enable, false otherwise + * @return true if successful + */ + bool enableBDU(bool enable); + + /** + * enable big endian output for 16b reads + * + * @param enable true to enable, false otherwise + * @return true if successful + */ + bool enableBLE(bool enable); + + /** + * enable sleep to wake functionality. + * + * @param enable true to enable, false otherwise + * @return true if successful + */ + bool enableSleepToWake(bool enable); + + /** + * return the contents of the REG_STATUS register + * + * @return the contents of the REG_STATUS register + */ + uint8_t getStatus(); + + /** + * setup the interrupt 1 config register + * + * @param val a bitmask of desired INT_CFG_BITS_T bits + * @return true if successful + */ + bool setInterrupt1Config(uint8_t val); + + /** + * setup the interrupt 2 config register + * + * @param val a bitmask of desired INT_CFG_BITS_T bits + * @return true if successful + */ + bool setInterrupt2Config(uint8_t val); + + /** + * setup the interrupt 1 source register + * + * @param val a bitmask of desired INT_SRC_BITS_T bits + * @return true if successful + */ + bool setInterrupt1Source(uint8_t val); + + /** + * setup the interrupt 2 source register + * + * @param val a bitmask of desired INT_SRC_BITS_T bits + * @return true if successful + */ + bool setInterrupt2Source(uint8_t val); + + /** + * setup the interrupt 1 threshold register + * + * @param val the threshhold to set + * @return true if successful + */ + bool setInterrupt1Threshold(uint8_t val); + + /** + * setup the interrupt 2 threshold register + * + * @param val the threshhold to set + * @return true if successful + */ + bool setInterrupt2Threshold(uint8_t val); + + /** + * setup the interrupt 1 duration register + * + * @param val the duration to set + * @return true if successful + */ + bool setInterrupt1Duration(uint8_t val); + + /** + * setup the interrupt 2 duration register + * + * @param val the duration to set + * @return true if successful + */ + bool setInterrupt2Duration(uint8_t val); + + /** + * read the sensor and store current values internally + */ + void update(); + + /** + * set adjustment offsets for each of the axes. This can be used + * for calibration. The values supplied here will be subtracted + * from the axis data read from the device. + * + * @param adjX the amount by which to correct the X axis measurement + * @param adjY the amount by which to correct the Y axis measurement + * @param adjZ the amount by which to correct the Z axis measurement + */ + void setAdjustmentOffsets(int adjX, int adjY, int adjZ); + + /** + * get the acceleration values for each of the axes + * + * @param aX the returned X acceleration + * @param aY the returned Y acceleration + * @param aZ the returned Z acceleration + */ + void getAcceleration(float *aX, float *aY, float *aZ); + + /** + * get the raw axis values + * + * @param x the returned raw X value + * @param y the returned raw Y value + * @param z the returned raw Z value + */ + void getRawXYZ(int *x, int *y, int *z); + + /** + * get the adjusted axis values + * + * @param x the returned X value + * @param y the returned Y value + * @param z the returned Z value + */ + void getXYZ(int *x, int *y, int *z); + + /** + * provide public access to the class's MRAA i2C context for + * direct user access + * + * @return a reference to the class i2c context + */ + mraa::I2c& i2cContext() { return m_i2c; }; + + + protected: + int16_t m_rawX, m_rawY, m_rawZ; + int16_t m_adjX, m_adjY, m_adjZ; + mraa::I2c m_i2c; + + private: + uint8_t m_addr; + }; +} + + diff --git a/src/h3lis331dl/jsupm_h3lis331dl.i b/src/h3lis331dl/jsupm_h3lis331dl.i new file mode 100644 index 00000000..f459bf3a --- /dev/null +++ b/src/h3lis331dl/jsupm_h3lis331dl.i @@ -0,0 +1,13 @@ +%module jsupm_h3lis331dl +%include "../upm.i" +%include "cpointer.i" + +/* Send "int *" and "float *" to JavaScript as intp and floatp */ +%pointer_functions(int, intp); +%pointer_functions(float, floatp); + +%{ + #include "h3lis331dl.h" +%} + +%include "h3lis331dl.h" diff --git a/src/h3lis331dl/pyupm_h3lis331dl.i b/src/h3lis331dl/pyupm_h3lis331dl.i new file mode 100644 index 00000000..a207b155 --- /dev/null +++ b/src/h3lis331dl/pyupm_h3lis331dl.i @@ -0,0 +1,18 @@ +%module pyupm_h3lis331dl +%include "../upm.i" +%include "cpointer.i" + +/* Send "int *" and "float *" to python as intp and floatp */ +%pointer_functions(int, intp); +%pointer_functions(float, floatp); + +%feature("autodoc", "3"); + +#ifdef DOXYGEN +%include "h3lis331dl_doc.i" +#endif + +%include "h3lis331dl.h" +%{ + #include "h3lis331dl.h" +%}