From 09e536b4ff0643df884aaa8a8d394f7f60402e2c Mon Sep 17 00:00:00 2001 From: Assam Boudjelthia Date: Tue, 12 Jun 2018 14:52:29 +0300 Subject: [PATCH] kxtj3: C/C++ driver implementation with examples Kionix tri-axis accelerometer sensor driver * C implementation * C++ wrapper * C/C++ examples Signed-off-by: Assam Boudjelthia Signed-off-by: Noel Eck --- docs/images/kxtj3.png | Bin 0 -> 53505 bytes examples/c++/kxtj3.cxx | 76 +++ examples/c/kxtj3.c | 75 +++ src/kxtj3/CMakeLists.txt | 8 + src/kxtj3/kxtj3.c | 1094 +++++++++++++++++++++++++++++++++++ src/kxtj3/kxtj3.cxx | 316 ++++++++++ src/kxtj3/kxtj3.h | 743 ++++++++++++++++++++++++ src/kxtj3/kxtj3.hpp | 579 ++++++++++++++++++ src/kxtj3/kxtj3.json | 63 ++ src/kxtj3/kxtj3_registers.h | 204 +++++++ 10 files changed, 3158 insertions(+) create mode 100755 docs/images/kxtj3.png create mode 100755 examples/c++/kxtj3.cxx create mode 100755 examples/c/kxtj3.c create mode 100755 src/kxtj3/CMakeLists.txt create mode 100755 src/kxtj3/kxtj3.c create mode 100755 src/kxtj3/kxtj3.cxx create mode 100755 src/kxtj3/kxtj3.h create mode 100755 src/kxtj3/kxtj3.hpp create mode 100755 src/kxtj3/kxtj3.json create mode 100755 src/kxtj3/kxtj3_registers.h diff --git a/docs/images/kxtj3.png b/docs/images/kxtj3.png new file mode 100755 index 0000000000000000000000000000000000000000..7f0fbfbb3d803fce0be84c59eebd2ab5b38f6aed GIT binary patch literal 53505 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�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%0ANb5H=HUltQC@NiP$8=19gb{_0Wg`s5u9+N-3) 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 v^#d|Hob2r#0|2d7)o3&tjYgxF literal 0 HcmV?d00001 diff --git a/examples/c++/kxtj3.cxx b/examples/c++/kxtj3.cxx new file mode 100755 index 00000000..d0607ff2 --- /dev/null +++ b/examples/c++/kxtj3.cxx @@ -0,0 +1,76 @@ +/* +* The MIT License (MIT) +* +* Author: Assam Boudjelthia +* Copyright (c) 2018 Rohm Semiconductor. +* +* 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 "kxtj3.hpp" + +#define SENSOR_ADDR 0x0f +#define I2C_BUS 0 +#define SAMPLE_COUNT 10 + +bool isStopped = false; + +void signal_int_handler(int signo) +{ + if (signo == SIGINT) + isStopped = true; +} + +void print_acceleration_data(upm::KXTJ3 *dev) +{ + float wait_time = dev->GetAccelerationSamplePeriod() * SECOND_IN_MICRO_S; + uint8_t sample_counter = 0; + std::vector xyz; + while (sample_counter < SAMPLE_COUNT && !isStopped) + { + xyz = dev->GetAccelerationVector(); + printf("%.02f | %.02f | %.02f\n", xyz[0], xyz[1], xyz[2]); + usleep(wait_time); + sample_counter++; + } +} + +int main(int argc, char **argv) +{ + signal(SIGINT, signal_int_handler); + + printf("Sensor init\n"); + upm::KXTJ3 *dev = new upm::KXTJ3(I2C_BUS, SENSOR_ADDR); + if (!dev) + { + printf("kxtj3_init() failed.\n"); + return -1; + } + + printf("Setting settings:\nODR: 25 Hz\nResolution: High\nAcceleration range: 16g with 14bits"); + dev->SensorInit(KXTJ3_ODR_25, HIGH_RES, KXTJ3_RANGE_16G_14); + printf("Showing acceleration data:\n"); + print_acceleration_data(dev); + + printf("Closing sensor\n"); + delete dev; + dev = nullptr; + return 0; +} diff --git a/examples/c/kxtj3.c b/examples/c/kxtj3.c new file mode 100755 index 00000000..12cbe7ec --- /dev/null +++ b/examples/c/kxtj3.c @@ -0,0 +1,75 @@ +/* +* The MIT License (MIT) +* +* Author: Assam Boudjelthia +* Copyright (c) 2018 Rohm Semiconductor. +* +* 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 "kxtj3.h" + +#define SENSOR_ADDR 0x0f +#define I2C_BUS 0 +#define SAMPLE_COUNT 10 + +bool isStopped = false; + +void signal_int_handler(int signo) +{ + if (signo == SIGINT) + isStopped = true; +} + +void print_acceleration_data(kxtj3_context dev) +{ + float wait_time = kxtj3_get_acceleration_sampling_period(dev) * SECOND_IN_MICRO_S; + uint8_t sample_counter = 0; + float x, y, z; + while (sample_counter < SAMPLE_COUNT && !isStopped) + { + kxtj3_get_acceleration_data(dev, &x, &y, &z); + printf("%.02f | %.02f | %.02f\n", x, y, z); + usleep(wait_time); + sample_counter++; + } +} + +int main(int argc, char **argv) +{ + signal(SIGINT, signal_int_handler); + + printf("Sensor init\n"); + kxtj3_context dev = kxtj3_init(I2C_BUS, SENSOR_ADDR); + if (!dev) + { + printf("kxtj3_init() failed.\n"); + return -1; + } + + printf("Setting settings:\nODR: 25 Hz\nResolution: High\nAcceleration range: 16g with 14bits"); + kxtj3_sensor_init(dev, KXTJ3_ODR_25, HIGH_RES, KXTJ3_RANGE_16G_14); + printf("Showing acceleration data:\n"); + print_acceleration_data(dev); + + printf("Closing sensor\n"); + kxtj3_close(dev); + return 0; +} diff --git a/src/kxtj3/CMakeLists.txt b/src/kxtj3/CMakeLists.txt new file mode 100755 index 00000000..f770d9c1 --- /dev/null +++ b/src/kxtj3/CMakeLists.txt @@ -0,0 +1,8 @@ +upm_mixed_module_init (NAME kxtj3 + DESCRIPTION "Tri-Axis Accelerometer" + C_HDR kxtj3.h kxtj3_registers.h + C_SRC kxtj3.c + CPP_HDR kxtj3.hpp + CPP_SRC kxtj3.cxx + CPP_WRAPS_C + REQUIRES mraa) diff --git a/src/kxtj3/kxtj3.c b/src/kxtj3/kxtj3.c new file mode 100755 index 00000000..bb5c92e0 --- /dev/null +++ b/src/kxtj3/kxtj3.c @@ -0,0 +1,1094 @@ +/* +* The MIT License (MIT) +* +* Author: Assam Boudjelthia +* Copyright (c) 2018 Rohm Semiconductor. +* +* 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 "kxtj3.h" + +#define SW_RESET_MAX_LOOP_COUNT 10 +#define SW_RESET_READ_WAIT_MICRO_S 50000 + +#define SELF_TEST_SAMPLE_COUNT 10 +#define SELF_TEST_DIFFERENCE_THRESHOLD 0.5f + +#define DATA_BUFFER_LENGTH 6 + +/** + * @brief Acceleration steps in (g) for each range setting. + * Used to calculate acceleration_scale to convert + * raw data to readable acceleration data. + */ +#define RANGE_2G_8BIT_STEP 0.016f +#define RANGE_4G_8BIT_STEP 0.031f +#define RANGE_8G_8BIT_STEP 0.0625f +#define RANGE_16G_8BIT_STEP 0.125f + +#define RANGE_2G_12BIT_STEP 0.001f +#define RANGE_4G_12BIT_STEP 0.002f +#define RANGE_8G_12BIT_STEP 0.0039f +#define RANGE_16G_12BIT_STEP 0.0078f + +#define RANGE_8G_14BIT_STEP 0.00098f +#define RANGE_16G_14BIT_STEP 0.00195f +#define EARTH_GRAVITY 9.81f + +/** + * @brief Map of ODR register values to ODR in Hz + * used to calculate sampling time in seconds + */ +struct odr_map_t +{ + uint8_t odr_reg_bit; + float odr_in_Hz; +}; + +/** + * @brief ODR register values maping with ODR in Hz + */ +const struct odr_map_t odr_map_in_Hz[] = { + {KXTJ3_ODR_0P781, 0.781f}, + {KXTJ3_ODR_1P563, 1.563f}, + {KXTJ3_ODR_3P125, 3.125f}, + {KXTJ3_ODR_6P25, 6.25f}, + {KXTJ3_ODR_12P5, 12.5f}, + {KXTJ3_ODR_25, 25.0f}, + {KXTJ3_ODR_50, 50.0f}, + {KXTJ3_ODR_100, 100.0f}, + {KXTJ3_ODR_200, 200.0f}, + {KXTJ3_ODR_400, 400.0f}, + {KXTJ3_ODR_800, 800.0f}, + {KXTJ3_ODR_1600, 1600.0f}}; +/** + * @brief ODR register values maping with ODR in Hz for + * wake-up function + */ +const struct odr_map_t odr_map_in_Hz_wakeup[] = { + {KXTJ3_ODR_WAKEUP_0P781, 0.781f}, + {KXTJ3_ODR_WAKEUP_1P563, 1.563f}, + {KXTJ3_ODR_WAKEUP_3P125, 3.125f}, + {KXTJ3_ODR_WAKEUP_6P25, 6.25f}, + {KXTJ3_ODR_WAKEUP_12P5, 12.5f}, + {KXTJ3_ODR_WAKEUP_25, 25.0f}, + {KXTJ3_ODR_WAKEUP_50, 50.0f}, + {KXTJ3_ODR_WAKEUP_100, 100.0f}}; + +/** + * @brief Coordinates structure + */ +struct Coordinates +{ + float x, y, z; +}; + +/** +@brief Inits the I2C connections and returns status of initialization + +@param dev The sensor context +@param bus I2C bus number +@param addr I2C addr of the sensor +@return true if initialization successful or false for failure +*/ +static bool kxtj3_check_mraa_i2c_connection(kxtj3_context dev, int bus, uint8_t addr); + +/** +@brief Checks if the sensor WHO_AM_I value is correct + +@param dev The sensor context +@return true if value correct, or false if mismatch +*/ +static bool kxtj3_check_who_am_i(kxtj3_context dev); + +/** +@brief Calculates the ODR sample time from an ODR register value + +@param odr One of KXTJ3_ODR_T values of ODR register configurations +@return the float time value +*/ +static float kxtj3_odr_val_to_sec(KXTJ3_ODR_T odr); + +/** +@brief Calculates the ODR sample time from an ODR register value for wake-up function + +@param odr One of KXTJ3_ODR_WAKEUP_T values of ODR register configurations for wake-up +@return the float time value +*/ +static float kxtj3_odr_val_to_sec_wakeup(KXTJ3_ODR_WAKEUP_T odr); + +/** +@brief Sets the sensor default values for ODR, resolution (with its scale), +G range (both normal and wake-up modes) + +@param dev The sensor context +*/ +static void kxtj3_set_default_values(const kxtj3_context dev); + +/** +@brief Read the value of a provided register + +@param dev The sensor context +@param reg The register address to read from +@param data A pointer to variable for storing the value read +@return A UPM result +*/ +static upm_result_t kxtj3_read_register(const kxtj3_context dev, uint8_t reg, uint8_t *data); + +/** +@brief Read the values starting from a provided register, of specific length + +@param dev The sensor context +@param reg The register address to start reading from +@param data A pointer to variable for storing the value read +@param len The number of bytes to read +@return A UPM result +*/ +static upm_result_t kxtj3_read_registers(const kxtj3_context dev, uint8_t reg, uint8_t *data, int len); + +/** +@brief Writes a value to a provided register + +@param dev The sensor context +@param reg The register address to write to +@param val byte of data to write +@return A UPM result +*/ +static upm_result_t kxtj3_write_register(const kxtj3_context dev, uint8_t reg, uint8_t val); + +/** +@brief Sets a specific bit on in a provided register + +@param dev The sensor context +@param reg register to write into +@param bit_mask The bit to set, as a register mask +@return A UPM result +*/ +static upm_result_t kxtj3_set_bit_on(const kxtj3_context dev, uint8_t reg, uint8_t bit_mask); + +/** +@brief Clear a specific bit (set off) in a provided register + +@param dev The sensor context +@param reg register address to write into +@param bit_mask The bit to set, as a register mask +@return A UPM result +*/ +static upm_result_t kxtj3_set_bit_off(const kxtj3_context dev, uint8_t reg, uint8_t bit_mask); + +/** +@brief Sets a register value or its bits according to a provided mask + +@param dev The sensor context +@param reg The register address to write to +@param val byte data to write +@param bit_mask The bits or register mask +@return A UPM resutl +*/ +static upm_result_t kxtj3_set_bits_with_mask(const kxtj3_context dev, uint8_t reg, uint8_t val, uint8_t bit_mask); + +/** +@brief Checks whether a given G range setting uses 14-bit mode + +@param g_range One of KXTJ3_G_RANGE_T value for available acceleration settings +@return true if range is 14-bit based, false otherwise +*/ +static bool kxtj3_is_14_bit_range(KXTJ3_G_RANGE_T g_range_mode); + +/** +@brief Maps the acceleration_scale (that is used to calculate the acceleration data in g unit) +with the G range and resolution mode. Changes the acceleration_scale value in sensor context. + +@param dev The sensor context +@param g_range The G range setting, one of KXTJ3_G_RANGE_T values +*/ +static void kxtj3_map_g_range_to_resolution(kxtj3_context dev, KXTJ3_G_RANGE_T g_range); +/** +@brief Calculates the average of coordinates for a sample of data (SELF_TEST_SAMPLE_COUNT). +This is used by the self-test functionality. + +@param dev The sensor context +@return Coordinates struct that contains value of x, y and z +*/ +static struct Coordinates kxtj3_get_sample_averaged_data(kxtj3_context dev); + +/** +@brief Check whether the self-test acceleration data difference is whithin the permitted threshold (0.5g) + +@param before The Coordinates struct before the self-test +@param during The Coordinates struct of the self-test +@return true if difference is below thresold, false otherwise +*/ +static bool kxtj3_check_self_test_difference(struct Coordinates before, struct Coordinates during); + +/** +@brief Checks the digital communication register (DCST_RESP) register value with an expected value + +@param dev The sensor context +@param expected_val The expted byte value of the register +@return true if values match, false otherwise. +*/ +static bool kxtj3_check_digital_communication_reg_value(kxtj3_context dev, uint8_t expected_val); + +/** +@brief Gets the count value from a given time (in seconds) for the wake-up function. +Used by the wake-up motion counter and non-activity counter before anothe wake-up functions. + +@param dev The sensor context +@param time_sec Time in seconds to be converted +@return the count value as a uint8_t +*/ +static uint8_t kxtj3_get_wakeup_count_from_time_sec(kxtj3_context dev, float time_sec); + +/** +@brief Gets the count value from a given acceleration threshold (in g) for the wake-up function. +Used by wake-up threshold counter functionality. + +@param dev The sensor context +@param g_threshold acceleration value in g to be converted +@return the count value as a uint16_t (expected range up to 4096) +*/ +static uint16_t kxtj3_get_wakeup_threshold_count_from_g(kxtj3_context dev, float g_threshold); + +// Register Read/Write helper functions +static upm_result_t kxtj3_read_register(const kxtj3_context dev, uint8_t reg, uint8_t *data) +{ + int value = mraa_i2c_read_byte_data(dev->i2c, reg); + if (value == -1) + { + printf("%s: mraa_i2c_read_byte_data() failed.\n", __FUNCTION__, reg); + return UPM_ERROR_OPERATION_FAILED; + } + + *data = (uint8_t)value; + return UPM_SUCCESS; +} + +static upm_result_t kxtj3_read_registers(const kxtj3_context dev, uint8_t reg, uint8_t *data, int len) +{ + if (mraa_i2c_read_bytes_data(dev->i2c, reg, data, len) != (int)len) + return UPM_ERROR_OPERATION_FAILED; + + return UPM_SUCCESS; +} + +static upm_result_t kxtj3_write_register(const kxtj3_context dev, uint8_t reg, uint8_t val) +{ + if (mraa_i2c_write_byte_data(dev->i2c, val, reg) != MRAA_SUCCESS) + { + printf("%s: mraa_i2c_write_byte_data() failed.\n", __FUNCTION__); + return UPM_ERROR_OPERATION_FAILED; + } + + return UPM_SUCCESS; +} + +static upm_result_t kxtj3_set_bit_on(const kxtj3_context dev, uint8_t reg, uint8_t bit_mask) +{ + uint8_t reg_value; + if (kxtj3_read_register(dev, reg, ®_value) != UPM_SUCCESS) + return UPM_ERROR_OPERATION_FAILED; + + reg_value |= bit_mask; + return kxtj3_write_register(dev, reg, reg_value); +} + +static upm_result_t kxtj3_set_bit_off(const kxtj3_context dev, uint8_t reg, uint8_t bit_mask) +{ + uint8_t reg_value; + if (kxtj3_read_register(dev, reg, ®_value) != UPM_SUCCESS) + return UPM_ERROR_OPERATION_FAILED; + + reg_value &= ~bit_mask; + return kxtj3_write_register(dev, reg, reg_value); +} + +static upm_result_t kxtj3_set_bits_with_mask(const kxtj3_context dev, uint8_t reg, uint8_t val, uint8_t bit_mask) +{ + uint8_t reg_val; + if (kxtj3_read_register(dev, reg, ®_val) != UPM_SUCCESS) + return UPM_ERROR_OPERATION_FAILED; + + reg_val &= ~bit_mask; + reg_val |= val; + return kxtj3_write_register(dev, reg, reg_val); +} +// End of register Read/Write helper functions + +static bool kxtj3_check_mraa_i2c_connection(kxtj3_context dev, int bus, uint8_t addr) +{ + if (mraa_init() != MRAA_SUCCESS) + { + printf("%s: mraa_init() failed.\n", __FUNCTION__); + kxtj3_close(dev); + return false; + } + + if (!(dev->i2c = mraa_i2c_init(bus))) + { + printf("%s: mraa_i2c_init() failed.\n", __FUNCTION__, bus); + kxtj3_close(dev); + return false; + } + + if (mraa_i2c_address(dev->i2c, addr)) + { + printf("%s: mraa_i2c_address() failed.\n", __FUNCTION__); + kxtj3_close(dev); + return false; + } + + return true; +} + +static bool kxtj3_check_who_am_i(kxtj3_context dev) +{ + uint8_t who_am_i; + kxtj3_get_who_am_i(dev, &who_am_i); + if (who_am_i != KXTJ3_WHO_AM_I_WIA_ID) + { + printf("%s: Wrong WHO AM I received, expected: 0x%x | got: 0x%x\n", __FUNCTION__, + KXTJ3_WHO_AM_I_WIA_ID, who_am_i); + kxtj3_close(dev); + return false; + } + return true; +} + +static float kxtj3_odr_val_to_sec(KXTJ3_ODR_T odr) +{ + for (size_t i = 0; i < (sizeof(odr_map_in_Hz) / sizeof(struct odr_map_t)); i++) + if (odr == odr_map_in_Hz[i].odr_reg_bit) + return (1 / odr_map_in_Hz[i].odr_in_Hz); + + return -1; +} + +static float kxtj3_odr_val_to_sec_wakeup(KXTJ3_ODR_WAKEUP_T odr) +{ + for (size_t i = 0; i < (sizeof(odr_map_in_Hz_wakeup) / sizeof(struct odr_map_t)); i++) + if (odr == odr_map_in_Hz_wakeup[i].odr_reg_bit) + return (1 / odr_map_in_Hz_wakeup[i].odr_in_Hz); + + return -1; +} + +static void kxtj3_set_default_values(const kxtj3_context dev) +{ + dev->g_range_mode = KXTJ3_RANGE_2G; + dev->acceleration_scale = RANGE_2G_8BIT_STEP; + dev->res_mode = LOW_RES; + dev->odr = KXTJ3_ODR_50; + dev->odr_in_sec = kxtj3_odr_val_to_sec(dev->odr); + dev->odr_wakeup = KXTJ3_ODR_WAKEUP_0P781; + dev->odr_in_sec_wakeup = kxtj3_odr_val_to_sec_wakeup(dev->odr_wakeup); +} + +kxtj3_context kxtj3_init(int bus, uint8_t addr) +{ + kxtj3_context dev = (kxtj3_context)malloc(sizeof(struct _kxtj3_context)); + if (!dev) + return NULL; + + dev->i2c = NULL; + dev->interrupt_pin = NULL; + + if (!kxtj3_check_mraa_i2c_connection(dev, bus, addr)) + return NULL; + + if (!kxtj3_check_who_am_i(dev)) + return NULL; + + kxtj3_set_default_values(dev); + + kxtj3_set_odr_wakeup_function(dev, dev->odr_wakeup); + kxtj3_sensor_init(dev, dev->odr, dev->res_mode, dev->g_range_mode); + + return dev; +} + +upm_result_t kxtj3_sensor_init(const kxtj3_context dev, KXTJ3_ODR_T odr, KXTJ3_RESOLUTION_T res, KXTJ3_G_RANGE_T g_range) +{ + assert(dev != NULL); + if (kxtj3_set_sensor_standby(dev) != UPM_SUCCESS) + return UPM_ERROR_OPERATION_FAILED; + + if (kxtj3_set_odr(dev, odr) != UPM_SUCCESS) + return UPM_ERROR_OPERATION_FAILED; + + uint8_t g_range_with_res = 0; + if (res) + g_range_with_res |= KXTJ3_CTRL_REG1_RES; + + g_range_with_res |= (g_range & KXTJ3_CTRL_REG1_GSEL_MASK); + if (kxtj3_set_bits_with_mask(dev, KXTJ3_CTRL_REG1, g_range_with_res, + KXTJ3_CTRL_REG1_RES | KXTJ3_CTRL_REG1_GSEL_MASK) != UPM_SUCCESS) + return UPM_ERROR_OPERATION_FAILED; + + dev->g_range_mode = g_range; + dev->res_mode = res; + kxtj3_map_g_range_to_resolution(dev, dev->g_range_mode); + + if (kxtj3_set_sensor_active(dev) != UPM_SUCCESS) + return UPM_ERROR_OPERATION_FAILED; + + return UPM_SUCCESS; +} + +upm_result_t kxtj3_get_who_am_i(const kxtj3_context dev, uint8_t *data) +{ + assert(dev != NULL); + return kxtj3_read_register(dev, KXTJ3_WHO_AM_I, data); +} + +void kxtj3_close(kxtj3_context dev) +{ + assert(dev != NULL); + + if (dev->i2c) + { + mraa_i2c_stop(dev->i2c); + } + + if (dev->interrupt_pin) + kxtj3_uninstall_isr(dev); + + free(dev); +} + +upm_result_t kxtj3_set_sensor_active(const kxtj3_context dev) +{ + assert(dev != NULL); + return kxtj3_set_bit_on(dev, KXTJ3_CTRL_REG1, KXTJ3_CTRL_REG1_PC); +} + +upm_result_t kxtj3_set_sensor_standby(const kxtj3_context dev) +{ + assert(dev != NULL); + return kxtj3_set_bit_off(dev, KXTJ3_CTRL_REG1, KXTJ3_CTRL_REG1_PC); +} + +static void kxtj3_map_g_range_to_resolution(kxtj3_context dev, KXTJ3_G_RANGE_T g_range) +{ + if (dev->res_mode == LOW_RES) + switch (g_range) + { + case KXTJ3_RANGE_2G: + dev->acceleration_scale = RANGE_2G_8BIT_STEP; + break; + case KXTJ3_RANGE_4G: + dev->acceleration_scale = RANGE_4G_8BIT_STEP; + break; + case KXTJ3_RANGE_8G: + dev->acceleration_scale = RANGE_8G_8BIT_STEP; + break; + case KXTJ3_RANGE_16G: + case KXTJ3_RANGE_16G_2: + case KXTJ3_RANGE_16G_3: + dev->acceleration_scale = RANGE_16G_8BIT_STEP; + break; + + case KXTJ3_RANGE_8G_14: + kxtj3_set_resolution(dev, HIGH_RES); + dev->acceleration_scale = RANGE_8G_14BIT_STEP; + break; + case KXTJ3_RANGE_16G_14: + kxtj3_set_resolution(dev, HIGH_RES); + dev->acceleration_scale = RANGE_16G_14BIT_STEP; + break; + } + else + switch (g_range) + { + case KXTJ3_RANGE_2G: + dev->acceleration_scale = RANGE_2G_12BIT_STEP; + break; + case KXTJ3_RANGE_4G: + dev->acceleration_scale = RANGE_4G_12BIT_STEP; + break; + case KXTJ3_RANGE_8G: + dev->acceleration_scale = RANGE_8G_12BIT_STEP; + break; + case KXTJ3_RANGE_16G: + case KXTJ3_RANGE_16G_2: + case KXTJ3_RANGE_16G_3: + dev->acceleration_scale = RANGE_16G_12BIT_STEP; + break; + + case KXTJ3_RANGE_8G_14: + dev->acceleration_scale = RANGE_8G_14BIT_STEP; + break; + case KXTJ3_RANGE_16G_14: + dev->acceleration_scale = RANGE_16G_14BIT_STEP; + break; + } +} + +upm_result_t kxtj3_set_g_range(const kxtj3_context dev, KXTJ3_G_RANGE_T g_range) +{ + assert(dev != NULL); + + if (kxtj3_set_bits_with_mask(dev, KXTJ3_CTRL_REG1, g_range, KXTJ3_CTRL_REG1_GSEL_MASK) != UPM_SUCCESS) + return UPM_ERROR_OPERATION_FAILED; + + dev->g_range_mode = g_range; + kxtj3_map_g_range_to_resolution(dev, g_range); + return UPM_SUCCESS; +} + +upm_result_t kxtj3_set_resolution(const kxtj3_context dev, KXTJ3_RESOLUTION_T resolution) +{ + assert(dev != NULL); + + if (resolution == HIGH_RES) + { + if (kxtj3_set_bit_on(dev, KXTJ3_CTRL_REG1, KXTJ3_CTRL_REG1_RES) != UPM_SUCCESS) + return UPM_ERROR_OPERATION_FAILED; + } + else + { + if (kxtj3_set_bit_off(dev, KXTJ3_CTRL_REG1, KXTJ3_CTRL_REG1_RES != UPM_SUCCESS)) + return UPM_ERROR_OPERATION_FAILED; + } + + dev->res_mode = resolution; + kxtj3_map_g_range_to_resolution(dev, dev->g_range_mode); + return UPM_SUCCESS; +} + +upm_result_t kxtj3_set_odr(const kxtj3_context dev, KXTJ3_ODR_T odr) +{ + assert(dev != NULL); + if (kxtj3_set_bits_with_mask(dev, KXTJ3_DATA_CTRL_REG, odr, KXTJ3_DATA_CTRL_REG_OSA_MASK) != UPM_SUCCESS) + return UPM_ERROR_OPERATION_FAILED; + + dev->odr = odr; + dev->odr_in_sec = kxtj3_odr_val_to_sec(odr); + + return UPM_SUCCESS; +} + +upm_result_t kxtj3_set_odr_wakeup_function(const kxtj3_context dev, KXTJ3_ODR_WAKEUP_T odr) +{ + assert(dev != NULL); + if (kxtj3_set_bits_with_mask(dev, KXTJ3_CTRL_REG2, odr, KXTJ3_CTRL_REG2_OWUF_MASK) != UPM_SUCCESS) + return UPM_ERROR_OPERATION_FAILED; + dev->odr_wakeup = odr; + dev->odr_in_sec_wakeup = kxtj3_odr_val_to_sec_wakeup(odr); + + return UPM_SUCCESS; +} + +static bool kxtj3_check_digital_communication_reg_value(kxtj3_context dev, uint8_t expected_val) +{ + uint8_t dcst_reg; + if (kxtj3_read_register(dev, KXTJ3_DCST_RESP, &dcst_reg) != UPM_SUCCESS) + return UPM_ERROR_OPERATION_FAILED; + + if (dcst_reg != expected_val) + return false; + + return true; +} + +upm_result_t kxtj3_self_test_digital_communication(kxtj3_context dev) +{ + assert(dev != NULL); + if (!kxtj3_check_digital_communication_reg_value(dev, KXTJ3_DCST_RESP_DCSTR_BEFORE)) + return UPM_ERROR_OPERATION_FAILED; + + if (kxtj3_set_bit_on(dev, KXTJ3_CTRL_REG2, KXTJ3_CTRL_REG2_DCST) != UPM_SUCCESS) + return UPM_ERROR_OPERATION_FAILED; + + if (!kxtj3_check_digital_communication_reg_value(dev, KXTJ3_DCST_RESP_DCSTR_AFTER)) + return UPM_ERROR_OPERATION_FAILED; + + if (!kxtj3_check_digital_communication_reg_value(dev, KXTJ3_DCST_RESP_DCSTR_BEFORE)) + return UPM_ERROR_OPERATION_FAILED; + + return UPM_SUCCESS; +} + +static struct Coordinates kxtj3_get_sample_averaged_data(kxtj3_context dev) +{ + struct Coordinates coordinates_averaged_sample; + coordinates_averaged_sample.x = 0.0f; + coordinates_averaged_sample.y = 0.0f; + coordinates_averaged_sample.z = 0.0f; + + float wait_time = kxtj3_get_acceleration_sampling_period(dev) * SECOND_IN_MICRO_S; + float x, y, z; + for (size_t i = 0; i < SELF_TEST_SAMPLE_COUNT; i++) + { + kxtj3_get_acceleration_data(dev, &x, &y, &z); + coordinates_averaged_sample.x += fabs((x / EARTH_GRAVITY)); + coordinates_averaged_sample.y += fabs((y / EARTH_GRAVITY)); + coordinates_averaged_sample.z += fabs((z / EARTH_GRAVITY)); + usleep(wait_time); + } + + coordinates_averaged_sample.x /= SELF_TEST_SAMPLE_COUNT; + coordinates_averaged_sample.y /= SELF_TEST_SAMPLE_COUNT; + coordinates_averaged_sample.z /= SELF_TEST_SAMPLE_COUNT; + + return coordinates_averaged_sample; +} + +static bool kxtj3_check_self_test_difference(struct Coordinates before, struct Coordinates during) +{ + struct Coordinates difference; + difference.x = fabs(before.x - during.x); + difference.y = fabs(before.y - during.y); + difference.z = fabs(before.z - during.z); + + if (difference.x > SELF_TEST_DIFFERENCE_THRESHOLD) + { + printf("%s: X-asix FAILED, change on X difference: %.2f\n", __FUNCTION__, difference.x); + return false; + } + + if (difference.y > SELF_TEST_DIFFERENCE_THRESHOLD) + { + printf("%s: Y-asix FAILED, change on Y difference: %.2f\n", __FUNCTION__, difference.y); + return false; + } + + if (difference.z > SELF_TEST_DIFFERENCE_THRESHOLD) + { + printf("%s: Z-asix FAILED, change on Z difference: %.2f\n", __FUNCTION__, difference.z); + return false; + } + + return true; +} + +upm_result_t kxtj3_sensor_self_test(kxtj3_context dev) +{ + assert(dev != NULL); + + struct Coordinates coordinates_before_test, coordinates_during_test; + coordinates_before_test = kxtj3_get_sample_averaged_data(dev); + + uint8_t stpol_val; + kxtj3_read_register(dev, KXTJ3_INT_CTRL_REG1, &stpol_val); + + kxtj3_set_sensor_standby(dev); + kxtj3_set_bit_on(dev, KXTJ3_INT_CTRL_REG1, KXTJ3_INT_CTRL_REG1_STPOL); + kxtj3_write_register(dev, KXTJ3_SELF_TEST, KXTJ3_SELF_TEST_MEMS_TEST_ENABLE); + kxtj3_set_bit_off(dev, KXTJ3_INT_CTRL_REG1, KXTJ3_INT_CTRL_REG1_STPOL); + kxtj3_set_sensor_active(dev); + + coordinates_during_test = kxtj3_get_sample_averaged_data(dev); + kxtj3_write_register(dev, KXTJ3_SELF_TEST, KXTJ3_SELF_TEST_MEMS_TEST_DISABLE); + + if (!kxtj3_check_self_test_difference(coordinates_before_test, coordinates_during_test)) + return UPM_ERROR_OPERATION_FAILED; + + kxtj3_set_sensor_standby(dev); + if (kxtj3_self_test_digital_communication(dev) != UPM_SUCCESS) + return UPM_ERROR_OPERATION_FAILED; + return kxtj3_set_sensor_active(dev); +} + +upm_result_t kxtj3_sensor_software_reset(const kxtj3_context dev) +{ + assert(dev != NULL); + if (kxtj3_set_bit_on(dev, KXTJ3_CTRL_REG2, KXTJ3_CTRL_REG2_SRST) != UPM_SUCCESS) + return UPM_ERROR_OPERATION_FAILED; + + uint8_t ctrl_reg2_data; + kxtj3_read_register(dev, KXTJ3_CTRL_REG2, &ctrl_reg2_data); + + uint8_t srst_counter = 0; + while ((ctrl_reg2_data & KXTJ3_CTRL_REG2_SRST) != 0x00 && srst_counter < SW_RESET_MAX_LOOP_COUNT) + { + usleep(SW_RESET_READ_WAIT_MICRO_S); + kxtj3_read_register(dev, KXTJ3_CTRL_REG2, &ctrl_reg2_data); + srst_counter++; + } + + if (srst_counter == SW_RESET_MAX_LOOP_COUNT) + return UPM_ERROR_OPERATION_FAILED; + + return UPM_SUCCESS; +} + +static bool kxtj3_is_14_bit_range(KXTJ3_G_RANGE_T g_range_mode) +{ + return g_range_mode == KXTJ3_RANGE_8G_14 || g_range_mode == KXTJ3_RANGE_16G_14; +} + +upm_result_t kxtj3_get_acceleration_data_raw(const kxtj3_context dev, float *x, float *y, float *z) +{ + uint8_t buffer[DATA_BUFFER_LENGTH]; + if (kxtj3_read_registers(dev, KXTJ3_XOUT_L, buffer, DATA_BUFFER_LENGTH) != UPM_SUCCESS) + return UPM_ERROR_OPERATION_FAILED; + + if (dev->res_mode == HIGH_RES) + { + uint8_t shift_amount = 4; + if (kxtj3_is_14_bit_range(dev->g_range_mode)) + shift_amount = 2; + + if (x) + *x = (float)((int16_t)((buffer[1] << 8) | buffer[0]) >> shift_amount); + if (y) + *y = (float)((int16_t)((buffer[3] << 8) | buffer[2]) >> shift_amount); + if (z) + *z = (float)((int16_t)((buffer[5] << 8) | buffer[4]) >> shift_amount); + } + else + { + if (x) + *x = (float)(int8_t)buffer[1]; + if (y) + *y = (float)(int8_t)buffer[3]; + if (z) + *z = (float)(int8_t)buffer[5]; + } + + return UPM_SUCCESS; +} + +upm_result_t kxtj3_get_acceleration_data(const kxtj3_context dev, float *x, float *y, float *z) +{ + float x_raw, y_raw, z_raw; + + if (kxtj3_get_acceleration_data_raw(dev, &x_raw, &y_raw, &z_raw) != UPM_SUCCESS) + return UPM_ERROR_OPERATION_FAILED; + + if (x) + *x = (x_raw * dev->acceleration_scale) * EARTH_GRAVITY; + if (y) + *y = (y_raw * dev->acceleration_scale) * EARTH_GRAVITY; + if (z) + *z = (z_raw * dev->acceleration_scale) * EARTH_GRAVITY; + + return UPM_SUCCESS; +} + +float kxtj3_get_acceleration_sampling_period(kxtj3_context dev) +{ + return dev->odr_in_sec; +} + +float kxtj3_get_wakeup_sampling_period(kxtj3_context dev) +{ + return dev->odr_in_sec_wakeup; +} + +upm_result_t kxtj3_enable_data_ready_interrupt(const kxtj3_context dev) +{ + assert(dev != NULL); + return kxtj3_set_bit_on(dev, KXTJ3_CTRL_REG1, KXTJ3_CTRL_REG1_DRDYE); +} + +upm_result_t kxtj3_disable_data_ready_interrupt(const kxtj3_context dev) +{ + assert(dev != NULL); + return kxtj3_set_bit_off(dev, KXTJ3_CTRL_REG1, KXTJ3_CTRL_REG1_DRDYE); +} + +upm_result_t kxtj3_enable_wakeup_interrupt(const kxtj3_context dev) +{ + assert(dev != NULL); + return kxtj3_set_bit_on(dev, KXTJ3_CTRL_REG1, KXTJ3_CTRL_REG1_WUFE); +} + +upm_result_t kxtj3_disable_wakeup_interrupt(const kxtj3_context dev) +{ + assert(dev != NULL); + return kxtj3_set_bit_off(dev, KXTJ3_CTRL_REG1, KXTJ3_CTRL_REG1_WUFE); +} + +upm_result_t kxtj3_enable_interrupt_pin(const kxtj3_context dev, KXTJ3_INTERRUPT_POLARITY_T polarity, + KXTJ3_INTERRUPT_RESPONSE_T response_type) +{ + assert(dev != NULL); + uint8_t int_reg_value; + kxtj3_read_register(dev, KXTJ3_INT_CTRL_REG1, &int_reg_value); + + if (polarity) + polarity = KXTJ3_INT_CTRL_REG1_IEA; + if (response_type) + response_type = KXTJ3_INT_CTRL_REG1_IEL; + + int_reg_value &= ~(KXTJ3_INT_CTRL_REG1_IEA | KXTJ3_INT_CTRL_REG1_IEL); + int_reg_value |= (KXTJ3_INT_CTRL_REG1_IEN | polarity | response_type); + + return kxtj3_write_register(dev, KXTJ3_INT_CTRL_REG1, int_reg_value); +} + +upm_result_t kxtj3_disable_interrupt_pin(const kxtj3_context dev) +{ + assert(dev != NULL); + return kxtj3_set_bit_off(dev, KXTJ3_INT_CTRL_REG1, KXTJ3_INT_CTRL_REG1_IEN); +} + +upm_result_t kxtj3_set_interrupt_polarity(const kxtj3_context dev, KXTJ3_INTERRUPT_POLARITY_T polarity) +{ + assert(dev != NULL); + if (polarity == ACTIVE_HIGH) + return kxtj3_set_bit_on(dev, KXTJ3_INT_CTRL_REG1, KXTJ3_INT_CTRL_REG1_IEA); + + return kxtj3_set_bit_off(dev, KXTJ3_INT_CTRL_REG1, KXTJ3_INT_CTRL_REG1_IEA); +} + +upm_result_t kxtj3_set_interrupt_response(const kxtj3_context dev, KXTJ3_INTERRUPT_RESPONSE_T response_type) +{ + assert(dev != NULL); + if (response_type == LATCH_UNTIL_CLEARED) + return kxtj3_set_bit_on(dev, KXTJ3_INT_CTRL_REG1, KXTJ3_INT_CTRL_REG1_IEL); + + return kxtj3_set_bit_off(dev, KXTJ3_INT_CTRL_REG1, KXTJ3_INT_CTRL_REG1_IEL); +} + +bool kxtj3_get_interrupt_status(const kxtj3_context dev) +{ + assert(dev != NULL); + uint8_t status_reg_value; + kxtj3_read_register(dev, KXTJ3_STATUS_REG, &status_reg_value); + if (!(status_reg_value & KXTJ3_STATUS_REG_INT)) + return false; + + return true; +} + +upm_result_t kxtj3_read_interrupt_source1_reg(const kxtj3_context dev, uint8_t *reg_value) +{ + assert(dev != NULL); + return kxtj3_read_register(dev, KXTJ3_INT_SOURCE1, reg_value); +} + +KXTJ3_INTERRUPT_SOURCE_T kxtj3_get_interrupt_source(const kxtj3_context dev) +{ + assert(dev != NULL); + if (kxtj3_get_interrupt_status(dev)) + { + uint8_t int_source_reg; + kxtj3_read_interrupt_source1_reg(dev, &int_source_reg); + + int_source_reg &= (KXTJ3_INT_SOURCE1_DRDY | KXTJ3_INT_SOURCE1_WUFS); + switch (int_source_reg) + { + case KXTJ3_INT_SOURCE1_DRDY: + return KXTJ3_DATA_READY_INTERRUPT; + case KXTJ3_INT_SOURCE1_WUFS: + return KXTJ3_WAKEUP_INTERRUPT; + case KXTJ3_INT_SOURCE1_DRDY | KXTJ3_INT_SOURCE1_WUFS: + return KXTJ3_DATA_READY_AND_WAKEUP_INT; + } + } + return NO_INTERRUPT; +} + +upm_result_t kxtj3_install_isr(const kxtj3_context dev, mraa_gpio_edge_t edge, int pin, void (*isr)(void *), void *isr_args) +{ + assert(dev != NULL); + mraa_gpio_context isr_gpio = NULL; + if (!(isr_gpio = mraa_gpio_init(pin))) + { + printf("%s: mraa_gpio_init() failed.\n", __FUNCTION__); + return UPM_ERROR_OPERATION_FAILED; + } + + mraa_gpio_dir(isr_gpio, MRAA_GPIO_IN); + + if (mraa_gpio_isr(isr_gpio, edge, isr, isr_args) != MRAA_SUCCESS) + { + mraa_gpio_close(isr_gpio); + printf("%s: mraa_gpio_isr() failed.\n", __FUNCTION__); + return UPM_ERROR_OPERATION_FAILED; + } + + dev->interrupt_pin = isr_gpio; + return UPM_SUCCESS; +} + +void kxtj3_uninstall_isr(const kxtj3_context dev) +{ + assert(dev != NULL); + mraa_gpio_isr_exit(dev->interrupt_pin); + mraa_gpio_close(dev->interrupt_pin); + dev->interrupt_pin = NULL; +} + +upm_result_t kxtj3_clear_interrupt_information(kxtj3_context dev) +{ + assert(dev != NULL); + uint8_t int_rel_value; + return kxtj3_read_register(dev, KXTJ3_INT_REL, &int_rel_value); +} + +upm_result_t kxtj3_enable_wakeup_single_axis_direction(kxtj3_context dev, KXTJ3_WAKEUP_SOURCE_T axis) +{ + assert(dev != NULL); + return kxtj3_set_bit_on(dev, KXTJ3_INT_CTRL_REG2, axis); +} + +upm_result_t kxtj3_disable_wakeup_single_axis_direction(kxtj3_context dev, KXTJ3_WAKEUP_SOURCE_T axis) +{ + assert(dev != NULL); + return kxtj3_set_bit_off(dev, KXTJ3_INT_CTRL_REG2, axis); +} + +kxtj3_wakeup_axes kxtj3_get_wakeup_axis_and_direction(kxtj3_context dev) +{ + assert(dev != NULL); + + uint8_t int_source2_value; + kxtj3_read_register(dev, KXTJ3_INT_SOURCE2, &int_source2_value); + + kxtj3_wakeup_axes wakeup_axis; + wakeup_axis.X_NEGATIVE = false; + wakeup_axis.X_POSITIVE = false; + wakeup_axis.Y_POSITIVE = false; + wakeup_axis.Y_NEGATIVE = false; + wakeup_axis.Z_POSITIVE = false; + wakeup_axis.Z_NEGATIVE = false; + + if (int_source2_value & KXTJ3_INT_SOURCE2_XPWU) + wakeup_axis.X_POSITIVE = true; + else if (int_source2_value & KXTJ3_INT_SOURCE2_XNWU) + wakeup_axis.X_NEGATIVE = true; + if (int_source2_value & KXTJ3_INT_SOURCE2_YPWU) + wakeup_axis.Y_POSITIVE = true; + else if (int_source2_value & KXTJ3_INT_SOURCE2_YNWU) + wakeup_axis.Y_NEGATIVE = true; + if (int_source2_value & KXTJ3_INT_SOURCE2_ZPWU) + wakeup_axis.Z_POSITIVE = true; + else if (int_source2_value & KXTJ3_INT_SOURCE2_ZNWU) + wakeup_axis.Z_NEGATIVE = true; + + return wakeup_axis; +} + +upm_result_t kxtj3_enable_wakeup_latch(kxtj3_context dev) +{ + assert(dev != NULL); + return kxtj3_set_bit_off(dev, KXTJ3_INT_CTRL_REG2, KXTJ3_INT_CTRL_REG2_ULMODE); +} + +upm_result_t kxtj3_disable_wakeup_latch(kxtj3_context dev) +{ + assert(dev != NULL); + return kxtj3_set_bit_on(dev, KXTJ3_INT_CTRL_REG2, KXTJ3_INT_CTRL_REG2_ULMODE); +} + +upm_result_t kxtj3_set_wakeup_motion_counter(kxtj3_context dev, uint8_t count) +{ + assert(dev != NULL); + if (count == 0) + return UPM_ERROR_OPERATION_FAILED; + return kxtj3_write_register(dev, KXTJ3_WAKEUP_COUNTER, count); +} + +static uint8_t kxtj3_get_wakeup_count_from_time_sec(kxtj3_context dev, float time_sec) +{ + return time_sec / dev->odr_in_sec_wakeup; +} + +upm_result_t kxtj3_set_wakeup_motion_time(kxtj3_context dev, float desired_time) +{ + assert(dev != NULL); + uint8_t count = kxtj3_get_wakeup_count_from_time_sec(dev, desired_time); + return kxtj3_set_wakeup_motion_counter(dev, count); +} + +upm_result_t kxtj3_get_wakeup_motion_time(kxtj3_context dev, float *out_time) +{ + assert(dev != NULL); + uint8_t motion_count; + if (kxtj3_read_register(dev, KXTJ3_WAKEUP_COUNTER, &motion_count) != UPM_SUCCESS) + return UPM_ERROR_OPERATION_FAILED; + + *out_time = (float)motion_count * dev->odr_in_sec_wakeup; + + return UPM_SUCCESS; +} + +upm_result_t kxtj3_set_wakeup_non_activity_counter(kxtj3_context dev, uint8_t count) +{ + assert(dev != NULL); + if (count == 0) + return UPM_ERROR_OPERATION_FAILED; + return kxtj3_write_register(dev, KXTJ3_NA_COUNTER, count); +} + +upm_result_t kxtj3_set_wakeup_non_activity_time(kxtj3_context dev, float desired_time) +{ + assert(dev != NULL); + uint8_t count = kxtj3_get_wakeup_count_from_time_sec(dev, desired_time); + return kxtj3_set_wakeup_non_activity_counter(dev, count); +} + +upm_result_t kxtj3_get_wakeup_non_activity_time(kxtj3_context dev, float *out_time) +{ + assert(dev != NULL); + uint8_t non_activity_reg_count; + if (kxtj3_read_register(dev, KXTJ3_NA_COUNTER, &non_activity_reg_count) != UPM_SUCCESS) + return UPM_ERROR_OPERATION_FAILED; + + *out_time = (float)non_activity_reg_count * dev->odr_in_sec_wakeup; + + return UPM_SUCCESS; +} + +upm_result_t kxtj3_set_wakeup_threshold_counter(kxtj3_context dev, uint16_t count) +{ + assert(dev != NULL); + if (count == 0) + return UPM_ERROR_OPERATION_FAILED; + if (kxtj3_write_register(dev, KXTJ3_WAKEUP_THRESHOLD_H, (uint8_t)(count >> 4)) != UPM_SUCCESS) + return UPM_ERROR_OPERATION_FAILED; + + if (kxtj3_write_register(dev, KXTJ3_WAKEUP_THRESHOLD_L, (uint8_t)(count << 4)) != UPM_SUCCESS) + return UPM_ERROR_OPERATION_FAILED; + + return UPM_SUCCESS; +} + +static uint16_t kxtj3_get_wakeup_threshold_count_from_g(kxtj3_context dev, float g_threshold) +{ + return g_threshold * 256; +} + +upm_result_t kxtj3_set_wakeup_threshold_g_value(kxtj3_context dev, float g_threshold) +{ + assert(dev != NULL); + uint16_t count = kxtj3_get_wakeup_threshold_count_from_g(dev, g_threshold); + return kxtj3_set_wakeup_threshold_counter(dev, count); +} + +upm_result_t kxtj3_get_wakeup_threshold(kxtj3_context dev, float *out_threshold) +{ + assert(dev != NULL); + uint8_t reg_value_h, reg_value_l; + if (kxtj3_read_register(dev, KXTJ3_WAKEUP_THRESHOLD_H, ®_value_h) != UPM_SUCCESS) + return UPM_ERROR_OPERATION_FAILED; + + if (kxtj3_read_register(dev, KXTJ3_WAKEUP_THRESHOLD_L, ®_value_l) != UPM_SUCCESS) + return UPM_ERROR_OPERATION_FAILED; + + *out_threshold = (float)((uint16_t)((reg_value_h << 8) | reg_value_l) >> 4) / 256; + + return UPM_SUCCESS; +} diff --git a/src/kxtj3/kxtj3.cxx b/src/kxtj3/kxtj3.cxx new file mode 100755 index 00000000..3b78858b --- /dev/null +++ b/src/kxtj3/kxtj3.cxx @@ -0,0 +1,316 @@ +/* +* The MIT License (MIT) +* +* Author: Assam Boudjelthia +* Copyright (c) 2018 Rohm Semiconductor. +* +* 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 "kxtj3.hpp" + +using namespace upm; + +KXTJ3::KXTJ3(int bus, uint8_t addr) : m_kxtj3(kxtj3_init(bus, addr)) +{ + if (!m_kxtj3) + throw std::runtime_error(std::string(__FUNCTION__) + "kxtj3_init() failed"); +} + +KXTJ3::~KXTJ3() +{ + kxtj3_close(m_kxtj3); +} + +void KXTJ3::SensorInit(KXTJ3_ODR_T odr, KXTJ3_RESOLUTION_T resolution, KXTJ3_G_RANGE_T g_range) +{ + if (kxtj3_sensor_init(m_kxtj3, odr, resolution, g_range) != UPM_SUCCESS) + throw std::runtime_error(std::string(__FUNCTION__) + "kxtj3_sensor_init() failed"); +} + +uint8_t KXTJ3::GetWhoAmI() +{ + uint8_t who_am_i; + if (kxtj3_get_who_am_i(m_kxtj3, &who_am_i) != UPM_SUCCESS) + throw std::runtime_error(std::string(__FUNCTION__) + "kxtj3_get_who_am_i() failed"); + + return who_am_i; +} + +void KXTJ3::SensorActive() +{ + if (kxtj3_set_sensor_active(m_kxtj3) != UPM_SUCCESS) + throw std::runtime_error(std::string(__FUNCTION__) + "kxtj3_set_sensor_active() failed"); +} + +void KXTJ3::SensorStandby() +{ + if (kxtj3_set_sensor_standby(m_kxtj3) != UPM_SUCCESS) + throw std::runtime_error(std::string(__FUNCTION__) + "kxtj3_set_sensor_standby() failed"); +} + +void KXTJ3::SetGRange(KXTJ3_G_RANGE_T g_range) +{ + if (kxtj3_set_g_range(m_kxtj3, g_range) != UPM_SUCCESS) + throw std::runtime_error(std::string(__FUNCTION__) + "kxtj3_set_g_range() failed"); +} + +void KXTJ3::SetResolution(KXTJ3_RESOLUTION_T resolution) +{ + if (kxtj3_set_resolution(m_kxtj3, resolution) != UPM_SUCCESS) + throw std::runtime_error(std::string(__FUNCTION__) + "kxtj3_set_resolution() failed"); +} + +void KXTJ3::SetOdr(KXTJ3_ODR_T odr) +{ + if (kxtj3_set_odr(m_kxtj3, odr) != UPM_SUCCESS) + throw std::runtime_error(std::string(__FUNCTION__) + "kxtj3_set_odr() failed"); +} + +void KXTJ3::SetOdrForWakeup(KXTJ3_ODR_WAKEUP_T odr) +{ + if (kxtj3_set_odr_wakeup_function(m_kxtj3, odr) != UPM_SUCCESS) + throw std::runtime_error(std::string(__FUNCTION__) + "kxtj3_set_odr_wakeup_function() failed"); +} + +void KXTJ3::SelfTestDigitalCommunication() +{ + if (kxtj3_self_test_digital_communication(m_kxtj3) != UPM_SUCCESS) + throw std::runtime_error(std::string(__FUNCTION__) + "kxtj3_self_test_digital_communication() failed"); +} + +void KXTJ3::SensorSelfTest() +{ + if (kxtj3_sensor_self_test(m_kxtj3) != UPM_SUCCESS) + throw std::runtime_error(std::string(__FUNCTION__) + "kxtj3_sensor_self_test() failed"); +} + +void KXTJ3::SensorSoftwareReset() +{ + if (kxtj3_sensor_software_reset(m_kxtj3) != UPM_SUCCESS) + throw std::runtime_error(std::string(__FUNCTION__) + "kxtj3_sensor_software_reset() failed"); +} + +void KXTJ3::GetAccelerationRaw(float *x, float *y, float *z) +{ + if (kxtj3_get_acceleration_data_raw(m_kxtj3, x, y, z) != UPM_SUCCESS) + throw std::runtime_error(std::string(__FUNCTION__) + "kxtj3_get_acceleration_data_raw() failed"); +} + +void KXTJ3::GetAcceleration(float *x, float *y, float *z) +{ + if (kxtj3_get_acceleration_data(m_kxtj3, x, y, z) != UPM_SUCCESS) + throw std::runtime_error(std::string(__FUNCTION__) + "kxtj3_get_acceleration_data() failed"); +} + +std::vector KXTJ3::GetAccelerationRawVector() +{ + std::vector xyz(3); + GetAccelerationRaw(&xyz[0], &xyz[1], &xyz[2]); + + return xyz; +} + +std::vector KXTJ3::GetAccelerationVector() +{ + std::vector xyz(3); + GetAcceleration(&xyz[0], &xyz[1], &xyz[2]); + + return xyz; +} + +float KXTJ3::GetAccelerationSamplePeriod() +{ + return kxtj3_get_acceleration_sampling_period(m_kxtj3); +} + +float KXTJ3::GetWakeUpSamplePeriod() +{ + return kxtj3_get_wakeup_sampling_period(m_kxtj3); +} + +void KXTJ3::EnableDataReadyInterrupt() +{ + if (kxtj3_enable_data_ready_interrupt(m_kxtj3) != UPM_SUCCESS) + throw std::runtime_error(std::string(__FUNCTION__) + "kxtj3_enable_data_ready_interrupt() failed"); +} + +void KXTJ3::DisableDataReadyInterrupt() +{ + if (kxtj3_disable_data_ready_interrupt(m_kxtj3) != UPM_SUCCESS) + throw std::runtime_error(std::string(__FUNCTION__) + "kxtj3_disable_data_ready_interrupt() failed"); +} + +void KXTJ3::EnableWakeUpInterrupt() +{ + if (kxtj3_enable_wakeup_interrupt(m_kxtj3) != UPM_SUCCESS) + throw std::runtime_error(std::string(__FUNCTION__) + "kxtj3_enable_wakeup_interrupt() failed"); +} + +void KXTJ3::DisableWakeUpInterrupt() +{ + if (kxtj3_disable_wakeup_interrupt(m_kxtj3) != UPM_SUCCESS) + throw std::runtime_error(std::string(__FUNCTION__) + "kxtj3_disable_wakeup_interrupt() failed"); +} + +void KXTJ3::EnableInterruptPin(KXTJ3_INTERRUPT_POLARITY_T polarity, KXTJ3_INTERRUPT_RESPONSE_T response_type) +{ + if (kxtj3_enable_interrupt_pin(m_kxtj3, polarity, response_type) != UPM_SUCCESS) + throw std::runtime_error(std::string(__FUNCTION__) + "kxtj3_enable_interrupt_pin() failed"); +} + +void KXTJ3::DisableInterruptPin() +{ + if (kxtj3_disable_interrupt_pin(m_kxtj3) != UPM_SUCCESS) + throw std::runtime_error(std::string(__FUNCTION__) + "kxtj3_disable_interrupt_pin() failed"); +} + +void KXTJ3::SetInterruptPolarity(KXTJ3_INTERRUPT_POLARITY_T polarity) +{ + if (kxtj3_set_interrupt_polarity(m_kxtj3, polarity) != UPM_SUCCESS) + throw std::runtime_error(std::string(__FUNCTION__) + "kxtj3_set_interrupt_polarity() failed"); +} + +void KXTJ3::SetInerruptResponse(KXTJ3_INTERRUPT_RESPONSE_T response_type) +{ + if (kxtj3_set_interrupt_response(m_kxtj3, response_type) != UPM_SUCCESS) + throw std::runtime_error(std::string(__FUNCTION__) + "kxtj3_set_interrupt_response() failed"); +} + +bool KXTJ3::GetInterruptStatus() +{ + return kxtj3_get_interrupt_status(m_kxtj3); +} + +uint8_t KXTJ3::ReadInterruptSource1() +{ + uint8_t reg_value; + if (kxtj3_read_interrupt_source1_reg(m_kxtj3, ®_value) != UPM_SUCCESS) + throw std::runtime_error(std::string(__FUNCTION__) + "kxtj3_read_interrupt_source1_reg() failed"); + + return reg_value; +} + +KXTJ3_INTERRUPT_SOURCE_T KXTJ3::GetInterruptSource() +{ + return kxtj3_get_interrupt_source(m_kxtj3); +} + +void KXTJ3::InstallIsr(mraa_gpio_edge_t edge, int pin, void (*isr)(void *), void *isr_args) +{ + if (kxtj3_install_isr(m_kxtj3, edge, pin, isr, isr_args) != UPM_SUCCESS) + throw std::runtime_error(std::string(__FUNCTION__) + "kxtj3_install_isr() failed"); +} + +void KXTJ3::UninstallIsr() +{ + kxtj3_uninstall_isr(m_kxtj3); +} + +void KXTJ3::ClearInterrupt() +{ + if (kxtj3_clear_interrupt_information(m_kxtj3) != UPM_SUCCESS) + throw std::runtime_error(std::string(__FUNCTION__) + "kxtj3_clear_interrupt_information() failed"); +} + +void KXTJ3::EnableWakeUpSingleAxisDirection(KXTJ3_WAKEUP_SOURCE_T axis) +{ + if (kxtj3_enable_wakeup_single_axis_direction(m_kxtj3, axis) != UPM_SUCCESS) + throw std::runtime_error(std::string(__FUNCTION__) + "kxtj3_enable_wakeup_single_axis_direction() failed"); +} + +kxtj3_wakeup_axes KXTJ3::GetWakeUpAxisDirection() +{ + return kxtj3_get_wakeup_axis_and_direction(m_kxtj3); +} + +void KXTJ3::EnableWakeUpLatch() +{ + if (kxtj3_enable_wakeup_latch(m_kxtj3) != UPM_SUCCESS) + throw std::runtime_error(std::string(__FUNCTION__) + "kxtj3_enable_wakeup_latch() failed"); +} + +void KXTJ3::DisableWakeUpLatch() +{ + if (kxtj3_disable_wakeup_latch(m_kxtj3) != UPM_SUCCESS) + throw std::runtime_error(std::string(__FUNCTION__) + "kxtj3_disable_wakeup_latch() failed"); +} + +void KXTJ3::SetWakeUpMotionCounter(uint8_t count) +{ + if (kxtj3_set_wakeup_motion_counter(m_kxtj3, count) != UPM_SUCCESS) + throw std::runtime_error(std::string(__FUNCTION__) + "kxtj3_set_wakeup_motion_counter() failed"); +} + +void KXTJ3::SetWakeUpMotionTime(float desired_time) +{ + if (kxtj3_set_wakeup_motion_time(m_kxtj3, desired_time) != UPM_SUCCESS) + throw std::runtime_error(std::string(__FUNCTION__) + "kxtj3_set_wakeup_motion_time() failed"); +} + +float KXTJ3::GetWakeUpMotionTime() +{ + float out_time; + if (kxtj3_get_wakeup_motion_time(m_kxtj3, &out_time) != UPM_SUCCESS) + throw std::runtime_error(std::string(__FUNCTION__) + "kxtj3_get_wakeup_motion_time() failed"); + + return out_time; +} + +void KXTJ3::SetWakeUpNonActivityCounter(uint8_t count) +{ + if (kxtj3_set_wakeup_non_activity_counter(m_kxtj3, count) != UPM_SUCCESS) + throw std::runtime_error(std::string(__FUNCTION__) + "kxtj3_set_wakeup_non_activity_counter() failed"); +} + +void KXTJ3::SetWakeUpNonActivityTime(float desired_time) +{ + if (kxtj3_set_wakeup_non_activity_time(m_kxtj3, desired_time) != UPM_SUCCESS) + throw std::runtime_error(std::string(__FUNCTION__) + "kxtj3_set_wakeup_non_activity_time() failed"); +} + +float KXTJ3::GetWakeUpNonActivityTime() +{ + float out_time; + if (kxtj3_get_wakeup_non_activity_time(m_kxtj3, &out_time) != UPM_SUCCESS) + throw std::runtime_error(std::string(__FUNCTION__) + "kxtj3_get_wakeup_non_activity_time() failed"); + + return out_time; +} + +void KXTJ3::SetWakeUpThresholdCounter(uint16_t count) +{ + if (kxtj3_set_wakeup_threshold_counter(m_kxtj3, count) != UPM_SUCCESS) + throw std::runtime_error(std::string(__FUNCTION__) + "kxtj3_set_wakeup_threshold_counter() failed"); +} + +void KXTJ3::SetWakeUpThresholdGRange(float g_threshold) +{ + if (kxtj3_set_wakeup_threshold_g_value(m_kxtj3, g_threshold) != UPM_SUCCESS) + throw std::runtime_error(std::string(__FUNCTION__) + "kxtj3_set_wakeup_threshold_g_value() failed"); +} + +float KXTJ3::GetWakeUpThresholdGRange() +{ + float out_threshold; + if (kxtj3_get_wakeup_threshold(m_kxtj3, &out_threshold) != UPM_SUCCESS) + throw std::runtime_error(std::string(__FUNCTION__) + "kxtj3_get_wakeup_threshold() failed"); + + return out_threshold; +} \ No newline at end of file diff --git a/src/kxtj3/kxtj3.h b/src/kxtj3/kxtj3.h new file mode 100755 index 00000000..4124aebc --- /dev/null +++ b/src/kxtj3/kxtj3.h @@ -0,0 +1,743 @@ +/* +* The MIT License (MIT) +* +* Author: Assam Boudjelthia +* Copyright (c) 2018 Rohm Semiconductor. +* +* 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. +*/ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include + +#include +#include +#include + +#include "kxtj3_registers.h" + + /** + * @brief C API for the kxtj3 driver + * @defgroup kxtj3 libupm-kxtj3 + * @ingroup Kionix i2c acceleromter + * + * @include kxtj3.c + */ + + /** + * @library kxtj3 + * @sensor kxtj3 + * @comname tri-axis accelerometer + * @type acceleromter + * @man Kionix + * @con i2c + * + * @brief C API for the kxtj3 driver + * + * @image html kxtj3.png + */ + +#define SECOND_IN_MICRO_S 1000000 + + /** + * @brief Resolution types + */ + typedef enum + { + LOW_RES, + HIGH_RES + } KXTJ3_RESOLUTION_T; + + /** + * @brief Interrupt polarity types + */ + typedef enum + { + ACTIVE_LOW, + ACTIVE_HIGH + + } KXTJ3_INTERRUPT_POLARITY_T; + + /** + * @brief Interrupt response types + */ + typedef enum + { + LATCH_UNTIL_CLEARED, + TRANSMIT_ONE_PULSE + } KXTJ3_INTERRUPT_RESPONSE_T; + + /** + * @brief Interrupt sources + */ + typedef enum + { + NO_INTERRUPT, + KXTJ3_DATA_READY_INTERRUPT = KXTJ3_INT_SOURCE1_DRDY, + KXTJ3_WAKEUP_INTERRUPT = KXTJ3_INT_SOURCE1_WUFS, + KXTJ3_DATA_READY_AND_WAKEUP_INT = KXTJ3_INT_SOURCE1_DRDY | KXTJ3_INT_SOURCE1_WUFS + } KXTJ3_INTERRUPT_SOURCE_T; + + /** + * @brief Acceleration ranges + */ + typedef enum + { + KXTJ3_RANGE_2G = KXTJ3_CTRL_REG1_GSEL_2G, + KXTJ3_RANGE_4G = KXTJ3_CTRL_REG1_GSEL_4G, + KXTJ3_RANGE_8G = KXTJ3_CTRL_REG1_GSEL_8G, + KXTJ3_RANGE_8G_14 = KXTJ3_CTRL_REG1_GSEL_8G_14, + KXTJ3_RANGE_16G = KXTJ3_CTRL_REG1_GSEL_16G, + KXTJ3_RANGE_16G_2 = KXTJ3_CTRL_REG1_GSEL_16G2, + KXTJ3_RANGE_16G_3 = KXTJ3_CTRL_REG1_GSEL_16G3, + KXTJ3_RANGE_16G_14 = KXTJ3_CTRL_REG1_GSEL_16G_14 + } KXTJ3_G_RANGE_T; + + /** + * @brief Output Data Rates for normal mode + */ + typedef enum + { + KXTJ3_ODR_0P781 = KXTJ3_DATA_CTRL_REG_OSA_0P781, + KXTJ3_ODR_1P563 = KXTJ3_DATA_CTRL_REG_OSA_1P563, + KXTJ3_ODR_3P125 = KXTJ3_DATA_CTRL_REG_OSA_3P125, + KXTJ3_ODR_6P25 = KXTJ3_DATA_CTRL_REG_OSA_6P25, + KXTJ3_ODR_12P5 = KXTJ3_DATA_CTRL_REG_OSA_12P5, + KXTJ3_ODR_25 = KXTJ3_DATA_CTRL_REG_OSA_25, + KXTJ3_ODR_50 = KXTJ3_DATA_CTRL_REG_OSA_50, + KXTJ3_ODR_100 = KXTJ3_DATA_CTRL_REG_OSA_100, + KXTJ3_ODR_200 = KXTJ3_DATA_CTRL_REG_OSA_200, + KXTJ3_ODR_400 = KXTJ3_DATA_CTRL_REG_OSA_400, + KXTJ3_ODR_800 = KXTJ3_DATA_CTRL_REG_OSA_800, + KXTJ3_ODR_1600 = KXTJ3_DATA_CTRL_REG_OSA_1600 + } KXTJ3_ODR_T; + + /** + * @brief Output Data Rates for wake-up function + */ + typedef enum + { + KXTJ3_ODR_WAKEUP_0P781 = KXTJ3_CTRL_REG2_OWUF_0P781, + KXTJ3_ODR_WAKEUP_1P563 = KXTJ3_CTRL_REG2_OWUF_1P563, + KXTJ3_ODR_WAKEUP_3P125 = KXTJ3_CTRL_REG2_OWUF_3P125, + KXTJ3_ODR_WAKEUP_6P25 = KXTJ3_CTRL_REG2_OWUF_6P25, + KXTJ3_ODR_WAKEUP_12P5 = KXTJ3_CTRL_REG2_OWUF_12P5, + KXTJ3_ODR_WAKEUP_25 = KXTJ3_CTRL_REG2_OWUF_25, + KXTJ3_ODR_WAKEUP_50 = KXTJ3_CTRL_REG2_OWUF_50, + KXTJ3_ODR_WAKEUP_100 = KXTJ3_CTRL_REG2_OWUF_100 + } KXTJ3_ODR_WAKEUP_T; + + /** + * @brief Wake-up source axis and direction + */ + typedef enum + { + X_NEGATIVE = KXTJ3_INT_CTRL_REG2_XNWU, + X_POSITIVE = KXTJ3_INT_CTRL_REG2_XPWU, + Y_NEGATIVE = KXTJ3_INT_CTRL_REG2_YNWU, + Y_POSITIVE = KXTJ3_INT_CTRL_REG2_YPWU, + Z_NEGATIVE = KXTJ3_INT_CTRL_REG2_ZNWU, + Z_POSITIVE = KXTJ3_INT_CTRL_REG2_ZPWU + } KXTJ3_WAKEUP_SOURCE_T; + + /** + * @brief Use it to get axis and direction for wake-up function + */ + typedef struct + { + bool X_NEGATIVE, + X_POSITIVE, + Y_NEGATIVE, + Y_POSITIVE, + Z_NEGATIVE, + Z_POSITIVE; + } kxtj3_wakeup_axes; + + /** + * @brief Sensor context + */ + typedef struct _kxtj3_context + { + mraa_i2c_context i2c; + KXTJ3_RESOLUTION_T res_mode; + KXTJ3_G_RANGE_T g_range_mode; + float acceleration_scale; + KXTJ3_ODR_T odr; + float odr_in_sec; + KXTJ3_ODR_WAKEUP_T odr_wakeup; + float odr_in_sec_wakeup; + mraa_gpio_context interrupt_pin; + } * kxtj3_context; + + /** +@brief KXTJ3 initialization. Sets sensor default values and put it to active state. + +@param bus I2C bus to use +@param addr I2C address of the sensor +@return The sensor context, or NULL if an error occurs +*/ + kxtj3_context kxtj3_init(int bus, uint8_t addr); + + /** +@brief Intilializes the sensor with the given resolution and acceleration range + +This gets called during the kxtj3_init(), so it will not need to be called unless the sensor is reset + +Sensor is automatically set into standby mode during the initialization +Sensor is set to active mode after initialization + +Be cautious not to set resolution to LOW_RES along with g_range as 14-bits modes + +@param dev The sensor context +@param odr Output Data Rate, One of the KXTJ3_ODR_T values +@param res Resolution mode, One of the KXTJ3_RESOLUTION_T values. LOW_RES valid only for ODR <= 200 Hz. +@param g_range Acceleration range, One of the KXTJ3_G_RANGE_T values +@return UPM result +*/ + upm_result_t kxtj3_sensor_init(const kxtj3_context dev, KXTJ3_ODR_T odr, KXTJ3_RESOLUTION_T res, KXTJ3_G_RANGE_T g_range); + + /** +@brief Gets "who am I" value of the sensor + +@param dev The sensor context +@param data Pointer to a uint8_t variable to store the value +@return UPM result +*/ + upm_result_t kxtj3_get_who_am_i(const kxtj3_context dev, uint8_t *data); + + /** +@brief KXTJ3 destructor +Closes the I2C context, and removes interrupts +Frees memory of the kxtj3_context + +@param dev The sensor context +*/ + void kxtj3_close(kxtj3_context dev); + + /** +@brief Sets the sensor to active mode + +@param dev The sensor context +@return UPM result +*/ + upm_result_t kxtj3_set_sensor_active(const kxtj3_context dev); + + /** +@brief Sets the sensor to the standby mode + +@param dev The sensor context +@return UPM result +*/ + upm_result_t kxtj3_set_sensor_standby(const kxtj3_context dev); + + /** +@brief Sets the acceleration range of the sensor + +Sensor needs to be in standby mode when setting the acceleration range value + +Be cautious not to set g_range to 14-bits modes with the resolution being on LOW_RES. +If the acceleration range is not compatible with resolution mode, the resolution is set automatically +to the compatible value. + +@param dev The sensor context +@param g_range One of the KXTJ3_G_RANGE_T values +@return UPM result +*/ + upm_result_t kxtj3_set_g_range(const kxtj3_context dev, KXTJ3_G_RANGE_T g_range); + + /** +@brief Sets the resolution of the sensor. High resolution (14 bits and 12 bits) or low resolution (8 bits). + +LOW_RES valid only for ODR <= 200 Hz + +Be cautious not to set resolution to LOW_RES with the G_RANG being on 14-bits modes. +If the resolution mode is not compatible with acceleration range, the resolution is set automatically +to the compatible value. + +Sensor needs to be in standby mode when setting the sensor resolution + +@param dev The sensor context +@param resolution One of the KXTJ3_RESOLUTION_T values +@return UPM result +*/ + upm_result_t kxtj3_set_resolution(const kxtj3_context dev, KXTJ3_RESOLUTION_T resolution); + + /** +@brief Sets the ODR of the sensor + +Sensor needs to be in standby mode when setting the ODR + +@param dev The sensor context +@param odr One of the KXTJ3_ODR_T values +@return UPM result +*/ + upm_result_t kxtj3_set_odr(const kxtj3_context dev, KXTJ3_ODR_T odr); + + /** +@brief Sets the ODR of the wakeup function of the sensor + +Sensor needs to be in standby mode when setting the ODR + +@param dev The sensor context +@param odr One of the KXTJ3_ODR_WAKEUP_T values +@return UPM result +*/ + upm_result_t kxtj3_set_odr_wakeup_function(const kxtj3_context dev, KXTJ3_ODR_WAKEUP_T odr); + + /** +@brief Performs a self-test for digital communication of the sensor. The test sets DCST bit in +CTRL_REG2, then checks the DCST_RESP register for a 0xAA, after the register is read, its +value is 0x55 and DCST bit is cleared. + +This function is called by kxtj3_sensor_self_test also + +Sensor must be in standby mode before performing this action + +@param dev The sensor context +@return UPM result +*/ + upm_result_t kxtj3_self_test_digital_communication(kxtj3_context dev); + + /** +@brief Performs a self-test of the sensor. The test applies an electrostatic force to the sensor, +simulating input acceleration. The test compares samples from all axis before and after +applying the electrostatic force to the sensor. If the amount of acceleration increases according +to the values defined in TABLE 1 of the datasheet (0.5 g), the test passes. + +The function prints out the values before and during test and the average difference for each axis + +See the datasheet for more information + +@param dev The sensor context +@return UPM result +*/ + upm_result_t kxtj3_sensor_self_test(const kxtj3_context dev); + + /** +@brief Performs a sensor software reset. The software reset clears the RAM of the sensor and resets all registers +to pre-defined values. + +You should call kxtj3_sensor_init() after the software reset + +See the datasheet for more details + +@param dev The sensor context +@return UPM result +*/ + upm_result_t kxtj3_sensor_software_reset(const kxtj3_context dev); + + /** +@brief Gets raw accelerometer data from the sensor + +@param dev The sensor context +@param x Pointer to a floating point variable to store the x-axis value. Set to NULL if not wanted. +@param y Pointer to a floating point variable to store the y-axis value. Set to NULL if not wanted. +@param z Pointer to a floating point variable to store the z-axis value. Set to NULL if not wanted. +@return UPM result +*/ + upm_result_t kxtj3_get_acceleration_data_raw(const kxtj3_context dev, float *x, float *y, float *z); + + /** +@brief Gets converted (m/s^2) accelerometer data from the sensor + +@param dev The sensor context +@param x Pointer to a floating point variable to store the x-axis value. Set to NULL if not wanted. +@param y Pointer to a floating point variable to store the y-axis value. Set to NULL if not wanted. +@param z Pointer to a floating point variable to store the z-axis value. Set to NULL if not wanted. +*/ + upm_result_t kxtj3_get_acceleration_data(const kxtj3_context dev, float *x, float *y, float *z); + + /** +@brief Gets the duration of one sample period (in seconds) for getting the acceleration data depending on +the sampling rate of the sensor + +@param dev The sensor context +@return Floating point value of the sampling period +*/ + float kxtj3_get_acceleration_sampling_period(kxtj3_context dev); + + /** +Gets the duration of one sample period (in seconds) for the wakeup function depending on +the sampling rate of the sensor wakeup function + +@param dev The sensor context +@return Floating point value of the sampling period +*/ + float kxtj3_get_wakeup_sampling_period(kxtj3_context dev); + + /** +@brief Enables the data ready interrupt. Availability of new acceleration data is +reflected as an interrupt. + +Sensor must be in standby mode before performing this action + +@param dev The sensor context +@return UPM result + */ + upm_result_t kxtj3_enable_data_ready_interrupt(const kxtj3_context dev); + + /** +@brief Disables the data ready interrupt + +Sensor must be in standby mode before performing this action + +@param dev The sensor context +@return UPM result +*/ + upm_result_t kxtj3_disable_data_ready_interrupt(const kxtj3_context dev); + + /** +@brief Enables the wakeup function (motion detection) + +Sensor must be in standby mode before performing this action + +@param dev The sensor context +@return UPM result + */ + upm_result_t kxtj3_enable_wakeup_interrupt(const kxtj3_context dev); + + /** +@brief Disables the wakeup function (motion detection) + +Sensor must be in standby mode before performing this action + +@param dev The sensor context +@return UPM result + */ + upm_result_t kxtj3_disable_wakeup_interrupt(const kxtj3_context dev); + + /** +@brief Enables interrupts on the interrupt pin, sets polarity and response types + +Polarity ACTIVE_HIGH or ACTIVE_LOW + +Response either latch until cleared by reading INT_REL register, or transmit one pulse +Pulse width of 0.03-0.05ms + +For Wakeup function, the response type is always latched unless set wakeup latch off + +Sensor needs to be in standby mode when enabling the interrupt + +See datasheet for more details + +@param dev The sensor context +@param polarity Select the polarity of the interrupt. One of the KXTJ3_INTERRUPT_POLARITY_T values. +@param response_type Select the response type of the interrupt. One of the KXTJ3_INTERRUPT_RESPONSE_T values. +@return UPM result +*/ + upm_result_t kxtj3_enable_interrupt_pin(const kxtj3_context dev, KXTJ3_INTERRUPT_POLARITY_T polarity, KXTJ3_INTERRUPT_RESPONSE_T response_type); + + /** +@brief Disables interrupts on the interrupt pin of the sensor + +Sensor needs to be in standby mode when disabling the interrupt pin + +@param dev The sensor context +@return UPM result +*/ + upm_result_t kxtj3_disable_interrupt_pin(const kxtj3_context dev); + + /** +@brief Sets the polarity of the interrupt pin + +Polarity ACTIVE_HIGH or ACTIVE_LOW + +Sensor must be in standby mode before performing this action + +@param dev The sensor context +@param polarity Select the polarity of the interrupt. One of the KXTJ3_INTERRUPT_POLARITY_T values. +@return UPM result +*/ + upm_result_t kxtj3_set_interrupt_polarity(const kxtj3_context dev, KXTJ3_INTERRUPT_POLARITY_T polarity); + + /** +@brief Sets the response type of the interrupt pin + +Response either latch until cleared by reading INT_REL register, or transmit one pulse +Pulse width of 0.03-0.05ms + +For Wakeup function, the response type is always latched unless set wakeup latch off + +Sensor must be in standby mode before performing this action + +See datasheet for more details + +@param dev The sensor context +@param response_type Select the response type of the interrupt. One of the KXTJ3_INTERRUPT_RESPONSE_T values. +@return UPM result +*/ + upm_result_t kxtj3_set_interrupt_response(const kxtj3_context dev, KXTJ3_INTERRUPT_RESPONSE_T response_type); + + /** +@brief Gets the status of the interrupts. (Has an interrupt occured). + +See datasheet for more details + +@param dev The sensor context +@return Return true if an interrupt event has occured (DRDY or WUFS is 1), returns false if no interrupts have occured +*/ + bool kxtj3_get_interrupt_status(const kxtj3_context dev); + + /** +@brief Gets the source of the interrupt +The value stored is one or more of the KXTJ3_INTERRUPT_SOURCE_T values + +See datasheet for more details + +@param dev The sensor context +@param reg_value Pointer to a uint8_t variable to store the value +@return UPM result +*/ + upm_result_t kxtj3_read_interrupt_source1_reg(const kxtj3_context dev, uint8_t *reg_value); + + /** +@brief Gets the source of the interrupt + +See datasheet for more details + +@param dev The sensor context +@return One of the KXTJ3_INTERRUPT_SOURCE_T values/types +*/ + KXTJ3_INTERRUPT_SOURCE_T kxtj3_get_interrupt_source(const kxtj3_context dev); + + /** +@brief Installs an interrupt handler to be executed when an interrupt is detected on the interrupt pin + +@param dev The sensor context +@param edge One of the mraa_gpio_edge_t values, Interrupt trigger edge +@param pin The GPIO pin to use as the interrupt pin +@param isr Pointer to the function to be called, when the interrupt occurs +@param isr_args The arguments to be passed to the function +@return UPM result +*/ + upm_result_t kxtj3_install_isr(const kxtj3_context dev, mraa_gpio_edge_t edge, int pin, void (*isr)(void *), void *isr_args); + + /** +@brief Uninstalls a previously installed interrupt handler for interrupt pin + +@param dev The sensor context +@return UPM result +*/ + void kxtj3_uninstall_isr(const kxtj3_context dev); + + /** +@brief Clears latching interrupts information of Wakeup (with axis and direction information) and Data Ready + +See datasheet for more details + +@param dev The sensor context +@return UPM result +*/ + upm_result_t kxtj3_clear_interrupt_information(kxtj3_context dev); + + /** +@brief Enables wakeup interrupt for the given axis (axis with direction) + +Sensor must be in standby mode before performing this action + +@param dev The sensor context +@param axis The axis to enable, takes one of KXTJ3_WAKEUP_SOURCE_T value +@return UPM result + */ + upm_result_t kxtj3_enable_wakeup_single_axis_direction(kxtj3_context dev, KXTJ3_WAKEUP_SOURCE_T axis); + + /** +@brief Disables wakeup interrupt for the given axis (axis with direction) + +Sensor must be in standby mode before performing this action + +@param dev The sensor context +@param axis The axis to enable, takes one of KXTJ3_WAKEUP_SOURCE_T value +@return UPM result + */ + upm_result_t kxtj3_disable_wakeup_single_axis_direction(kxtj3_context dev, KXTJ3_WAKEUP_SOURCE_T axis); + + /** +@brief Gets the source axis and direction of motion detection of the wakeup function interrupt + +See datasheet for more details + +@param dev The sensor context +@return A kxtj3_wakeup_axes struct with values of true/false for a wakeup for each axis and its direction +*/ + kxtj3_wakeup_axes kxtj3_get_wakeup_axis_and_direction(kxtj3_context dev); + + /** +@brief Enables the Unlached mode motion interrupt (ULMODE). This mode is always by default enabled. + +When this bit is set, the wakeup interrupt has to be cleared manually +(cannot use interrupt response with pulse) + +Sensor must be in standby mode before performing this action + +@param dev The sensor context +@return UPM result +*/ + upm_result_t kxtj3_enable_wakeup_latch(kxtj3_context dev); + + /** +@brief Disables the Unlached mode motion interrupt (ULMODE). This mode is always by default enabled +(cannot use interrupt response with pulse). + +The wakeup threshold is advised to not be very low to avoid interrupt being triggered in +an almost continuous manner + +Sensor must be in standby mode before performing this action + +When this bit is cleared, and the interrupt response type is set to Pulse, then upon a wakeup event +the wakeup interrupt signal will pulse and return low, but only once. Then, the interrupt +output will not reset until data is read or interrupt cleared. + +@param dev The sensor context +@return UPM result +*/ + upm_result_t kxtj3_disable_wakeup_latch(kxtj3_context dev); + + /** +@brief Sets the timer counter of motion before sending a wakeup interrupt + +The count is limited to values from 1 to 255 + +Every count is calculated as (1 / Wakeup_ODR_FREQUENCY) where Wakeup_ODR_FREQUENCY +is the current odr_in_Hz value from odr_map_in_Hz_wakeup value + +Sensor must be in standby mode before performing this action + +See datasheet for more details + +@param dev The sensor context +@param count The timer count from 1 to 255 +@return UPM result + */ + upm_result_t kxtj3_set_wakeup_motion_counter(kxtj3_context dev, uint8_t count); + + /** +@brief Sets the timer of motion before sending a wakeup interrupt + +the desired time should be such that (0 < desired_time * wakeup_odr_frequency <= 255) + +Sensor must be in standby mode before performing this action + +See datasheet for more details + +@param dev The sensor context +@param desired_time The desired time in seconds +@return UPM result + */ + upm_result_t kxtj3_set_wakeup_motion_time(kxtj3_context dev, float desired_time); + + /** +@brief Gets the current count value of the timer of motion before sending a wakeup interrupt + +@param dev The sensor context +@param out_time Pointer to a float variable to store the time value in seconds +@return UPM result + */ + upm_result_t kxtj3_get_wakeup_motion_time(kxtj3_context dev, float *out_time); + + /** +@brief Sets the timer counter of non-activity before sending another wakeup interrupt + +The count is limited to values from 1 to 255 + +Every count is calculated as (1 / Wakeup_ODR_FREQUENCY) where Wakeup_ODR_FREQUENCY +is the current odr_in_Hz value from odr_map_in_Hz_wakeup value + +Sensor must be in standby mode before performing this action + +See datasheet for more details + +@param dev The sensor context +@param count The timer count from 1 to 255 +@return UPM result + */ + upm_result_t kxtj3_set_wakeup_non_activity_counter(kxtj3_context dev, uint8_t count); + + /** +@brief Sets the timer of non-activity before sending another wakeup interrupt + +the desired time should be such that (0 < desired_time * wakeup_odr_frequency <= 255) + +Sensor must be in standby mode before performing this action + +See datasheet for more details + +@param dev The sensor context +@param desired_time The desired time in seconds +@return UPM result + */ + upm_result_t kxtj3_set_wakeup_non_activity_time(kxtj3_context dev, float desired_time); + + /** +@brief Gets the current count value of the timer of non-activity before sending another wakeup interrupt + +@param dev The sensor context +@param out_time Pointer to a float variable to store the time value in seconds +@return UPM result + */ + upm_result_t kxtj3_get_wakeup_non_activity_time(kxtj3_context dev, float *out_time); + + /** +@brief Sets the threshold counter for acceleration difference before sending a wakeup interrupt + +The count is limited to values from 1 to 4096, that it is 16g threshold with (3.9mg/count) +It is advised to not set the threshold to a very low value which may cause bad behaviour +in the wakeup interrupt + +Sensor must be in standby mode before performing this action + +See datasheet for more details + +@param dev The sensor context +@param count The timer count from 1 to 4096, that it is 16g threshold with (3.9mg/count) +@return UPM result + */ + upm_result_t kxtj3_set_wakeup_threshold_counter(kxtj3_context dev, uint16_t count); + + /** +@brief Sets the threshold g value for acceleration difference before sending a wakeup interrupt + +The count is limited to values up to 16g, with steps of 3.9mg. It is advised to not set +the threshold to a very low value which may cause bad behaviour in the wakeup interrupt + +Sensor must be in standby mode before performing this action + +See datasheet for more details + +@param dev The sensor context +@param g_threshold The acceleration threshold (in g) in g, from 3.9mg to 16g, steps of 3.9mg/count +@return UPM result + */ + upm_result_t kxtj3_set_wakeup_threshold_g_value(kxtj3_context dev, float g_threshold); + + /** +@brief Gets the current threshold difference value before sending wakeup interrupt + +@param dev The sensor context +@param out_threshold Pointer to a float variable to store the threshold value in g +@return UPM result + */ + upm_result_t kxtj3_get_wakeup_threshold(kxtj3_context dev, float *out_threshold); + +#ifdef __cplusplus +} +#endif diff --git a/src/kxtj3/kxtj3.hpp b/src/kxtj3/kxtj3.hpp new file mode 100755 index 00000000..85ca275d --- /dev/null +++ b/src/kxtj3/kxtj3.hpp @@ -0,0 +1,579 @@ +/* +* The MIT License (MIT) +* +* Author: Assam Boudjelthia +* Copyright (c) 2018 Rohm Semiconductor. +* +* 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 "kxtj3.h" + +/** + * @brief C API for the kxtj3 driver + * @defgroup kxtj3 libupm-kxtj3 + * @ingroup Kionix i2c acceleromter + * + * @include kxtj3.cxx + */ + +/** + * @library kxtj3 + * @sensor kxtj3 + * @comname tri-axis accelerometer + * @type acceleromter + * @man Kionix + * @con i2c + * + * @brief C++ API for the kxtj3 driver + * + * @image html kxtj3.png + */ + +namespace upm +{ +class KXTJ3 +{ +public: + /** + * @brief KXTJ3 constructor + * + * If no errors occur, the device is initialized with default values and set to active state + * + * @param bus I2C bus + * @param addr I2C address of the sensor + * @throws std::runtime_error on initialization failure + */ + KXTJ3(int bus, uint8_t addr); + + /** + * @brief KXTJ3 destructor + * + * Closes the I2C context, and removes interrupts + * Frees memory of the kxtj3_context + * + * @throws std::runtime_error on initialization failure + */ + ~KXTJ3(); + + /** + * @brief Initializes the sensor with given sampling rate, resolution and acceleration range. + * This gets called in the constructor, so it will not need to be called unless the device is reset. + * + * Sensor is set to standby mode during the initialization and back to active after initialization. + * + * @param odr One of the KXTJ3_ODR_T values + * @param resolution One of the KXTJ3_RESOLUTION_T values + * @param g_range One of the KXTJ3_G_RANGE_T values + * @throws std::runtime_error on failure + */ + void SensorInit(KXTJ3_ODR_T odr, KXTJ3_RESOLUTION_T resolution, KXTJ3_G_RANGE_T g_range); + + /** + * @brief Gets "who am I" value of the sensor + * + * @return Who am I value of the sensor + * @throws std::runtime_error on failure + */ + uint8_t GetWhoAmI(); + + /** + * @brief Sets the sensor to active mode + * + * @throws std::runtime_error on failure + */ + void SensorActive(); + + /** + * @brief Sets the sensor to standby mode + + * @throws std::runtime_error on failure + */ + void SensorStandby(); + + /** + * @brief Sets the acceleration range of the sensor + * + * Sensor needs to be in standby mode when setting the acceleration range value + * + * Be cautious not to set g_range to 14-bits modes with the resolution being on LOW_RES + * + * @param g_range One of the KXTJ3_G_RANGE_T values + * @throws std::runtime_error on failure + */ + void SetGRange(KXTJ3_G_RANGE_T g_range); + + /** + * @brief Sets the resolution of the sensor. High resolution (14 bits and 12 bits) or low resolution (8 bits). + * + * LOW_RES valid only for ODR <= 200 Hz + * + * Be cautious not to set resolution to LOW_RES with the G_RANG being on 14-bits modes + * + * Sensor needs to be in standby mode when setting the sensor resolution + * + * @param resolution One of the KXTJ3_RESOLUTION_T values + * @throws std::runtime_error on failure + */ + void SetResolution(KXTJ3_RESOLUTION_T resolution); + + /** + * @brief Sets the ODR of the sensor + * + * Sensor needs to be in standby mode when setting the ODR + * + * @param odr One of the KXTJ3_ODR_T values + * @throws std::runtime_error on failure + */ + void SetOdr(KXTJ3_ODR_T odr); + + /** + * @brief Sets the ODR of the wakeup function of the sensor + * + * Sensor needs to be in standby mode when setting the ODR + * + * @param odr One of the KXTJ3_ODR_WAKEUP_T values + * @throws std::runtime_error on failure + */ + void SetOdrForWakeup(KXTJ3_ODR_WAKEUP_T odr); + + /** + * @brief Performs a self-test for digital communication of the sensor. The test sets DCST bit in + * CTRL_REG2, then checks the DCST_RESP register for a 0xAA, after the register is read, its + * value is 0x55 and DCST bit is cleared. + * + * This method is called by SensorSelfTest also + * + * Sensor must be in standby mode before performing this action + * + * @throws std::runtime_error on failure + */ + void SelfTestDigitalCommunication(); + + /** + * @brief Performs a self-test of the sensor. The test applies an electrostatic force to the sensor, + * simulating input acceleration. The test compares samples from all axis before and after + * applying the electrostatic force to the sensor. If the amount of acceleration increases according + * to the values defined in TABLE 1 of the datasheet (0.5 g), the test passes. + * + * The method prints out the values before and during test and the average difference for each axis + * + * See the datasheet for more information + * + * @throws std::runtime_error on failure + */ + void SensorSelfTest(); + + /** + * @brief Performs a sensor software reset. The software reset clears the RAM of the sensor and resets all registers + * to pre-defined values. + * + * You should call kxtj3_sensor_init() after the software reset + * + * See the datasheet for more details + * + * @throws std::runtime_error on failure + */ + void SensorSoftwareReset(); + + /** + * @brief Gets raw accelerometer data from the sensor + * + * @param x Pointer to a floating point variable to store the x-axis value. Set to NULL if not wanted. + * @param y Pointer to a floating point variable to store the y-axis value. Set to NULL if not wanted. + * @param z Pointer to a floating point variable to store the z-axis value. Set to NULL if not wanted. + * @throws std::runtime_error on failure + */ + void GetAccelerationRaw(float *x, float *y, float *z); + + /** + * @brief Gets converted (m/s^2) accelerometer data from the sensor + * + * @param x Pointer to a floating point variable to store the x-axis value. Set to NULL if not wanted. + * @param y Pointer to a floating point variable to store the y-axis value. Set to NULL if not wanted. + * @param z Pointer to a floating point variable to store the z-axis value. Set to NULL if not wanted. + * @throws std::runtime_error on failure + */ + void GetAcceleration(float *x, float *y, float *z); + + /** + * Gets raw acceleration data from the sensor. + * + * @return Acceleration vector [x, y, z] + * @throws std::runtime_error on failure. + */ + std::vector GetAccelerationRawVector(); + + /** + * Gets acceleration data in (m/s^2) from the sensor. + * + * @return Acceleration vector [x, y, z] + * @throws std::runtime_error on failure. + */ + std::vector GetAccelerationVector(); + + /** + * @brief Gets the duration of one sample period (in seconds) for getting the acceleration data depending on + * the sampling rate of the sensor + * + * @return Floating point value of the sampling period + * @throws std::runtime_error on failure + */ + float GetAccelerationSamplePeriod(); + + /** + * @brief Gets the duration of one sample period (in seconds) for the wakeup function depending on + * the sampling rate of the sensor wakeup function + * + * @return Floating point value of the sampling period + * @throws std::runtime_error on failure + */ + float GetWakeUpSamplePeriod(); + + /** + * @brief Enables the data ready interrupt. Availability of new acceleration data is + * reflected as an interrupt. + * + * Sensor must be in standby mode before performing this action + * + * @throws std::runtime_error on failure + */ + void EnableDataReadyInterrupt(); + + /** + * @brief Disables the data ready interrupt + * + * Sensor must be in standby mode before performing this action + * + * @throws std::runtime_error on failure + */ + void DisableDataReadyInterrupt(); + + /** + * @brief Enables the wakeup function (motion detection) + * + * Sensor must be in standby mode before performing this action + * + * @throws std::runtime_error on failure + */ + void EnableWakeUpInterrupt(); + + /** + * @brief Disables the wakeup function (motion detection) + * + * Sensor must be in standby mode before performing this action + * + * @throws std::runtime_error on failure + */ + void DisableWakeUpInterrupt(); + + /** + * @brief Enables interrupts on the interrupt pin, sets polarity and response types. + * Polarity ACTIVE_HIGH or ACTIVE_LOW. + * Response either latch until cleared by reading INT_REL register, or transmit one pulse + * Pulse width of 0.03-0.05ms. + * + * For Wakeup function, the response type is always latched unless set wakeup latch off + * Sensor needs to be in standby mode when enabling the interrupt + * + * See datasheet for more details + * + * @param polarity Select the polarity of the interrupt. One of the KXTJ3_INTERRUPT_POLARITY_T values. + * @param response_type Select the response type of the interrupt. One of the KXTJ3_INTERRUPT_RESPONSE_T values. + * @throws std::runtime_error on failure + */ + void EnableInterruptPin(KXTJ3_INTERRUPT_POLARITY_T polarity, KXTJ3_INTERRUPT_RESPONSE_T response_type); + + /** + * @brief Disables interrupts on the interrupt pin of the sensor + * + * Sensor needs to be in standby mode when disabling the interrupt pin + * + * @throws std::runtime_error on failure + */ + void DisableInterruptPin(); + + /** + * @brief Sets the polarity of the interrupt pin + * + * Polarity ACTIVE_HIGH or ACTIVE_LOW + * + * Sensor must be in standby mode before performing this action + * + * @param polarity Select the polarity of the interrupt. One of the KXTJ3_INTERRUPT_POLARITY_T values. + * @throws std::runtime_error on failure + */ + void SetInterruptPolarity(KXTJ3_INTERRUPT_POLARITY_T polarity); + + /** + * @brief Sets the response type of the interrupt pin + * + * Response either latch until cleared by reading INT_REL register, or transmit one pulse + * Pulse width of 0.03-0.05ms + * + * For Wakeup function, the response type is always latched unless set wakeup latch off + * + * Sensor must be in standby mode before performing this action + * + * See datasheet for more details + * + * @param response_type Select the response type of the interrupt. One of the KXTJ3_INTERRUPT_RESPONSE_T values + * @throws std::runtime_error on failure + */ + void SetInerruptResponse(KXTJ3_INTERRUPT_RESPONSE_T response_type); + + /** + * @brief Gets the status of the interrupts. (Has an interrupt occured) + * + * See datasheet for more details + * + * @return Return true if an interrupt event has occured (DRDY or WUFS is 1), + * returns false if no interrupts have occured + */ + bool GetInterruptStatus(); + + /** + * @brief Gets the source of the interrupt + * The value stored is one or more of the KXTJ3_INTERRUPT_SOURCE_T values + * + * See datasheet for more details + * + * @return Value of the interrupt source register + * @throws std::runtime_error on failure + */ + uint8_t ReadInterruptSource1(); + + /** + * @brief Gets the source of the interrupt + * + * See datasheet for more details + * + * @return One of the KXTJ3_INTERRUPT_SOURCE_T values/types + */ + KXTJ3_INTERRUPT_SOURCE_T GetInterruptSource(); + + /** + * @brief Installs an interrupt handler to be executed when an interrupt is detected on the interrupt pin + + * @param edge One of the mraa_gpio_edge_t values. Interrupt trigger edge. + * @param pin The GPIO pin to use as the interrupt pin + * @param isr Pointer to the method to be called, when the interrupt occurs + * @param isr_args The arguments to be passed to the method + * @return std::runtime_error on failure + */ + void InstallIsr(mraa_gpio_edge_t edge, int pin, void (*isr)(void *), void *isr_args); + + /** + * @brief Uninstalls a previously installed interrupt handler for interrupt pin + * + * @throws std::runtime_error on failure + */ + void UninstallIsr(); + + /** + * @brief Clears latching interrupts information of Wakeup (with axis and direction information) and Data Ready + * + * See datasheet for more details + * + * @throws std::runtime_error on failure + */ + void ClearInterrupt(); + + /** + * @brief Enables wakeup interrupt for the given axis (axis with direction) + * + * Sensor must be in standby mode before performing this action + * + * @param axis The axis to enable, takes one of KXTJ3_WAKEUP_SOURCE_T value + * @throws std::runtime_error on failure + */ + void EnableWakeUpSingleAxisDirection(KXTJ3_WAKEUP_SOURCE_T axis); + + /** + * @brief Disables wakeup interrupt for the given axis (axis with direction) + * + * Sensor must be in standby mode before performing this action + * + * @param axis The axis to enable, takes one of KXTJ3_WAKEUP_SOURCE_T value + * @throws std::runtime_error on failure + */ + void DisableWakeUpSingleAxisDirection(KXTJ3_WAKEUP_SOURCE_T axis); + + /** + * @brief Gets the source axis and direction of motion detection of the wakeup function interrupt + * + * See datasheet for more details + * + * @return A kxtj3_wakeup_axes struct with values of true/false + * for a wakeup for each axis and its direction + */ + kxtj3_wakeup_axes GetWakeUpAxisDirection(); + + /** + * @brief Enables the Unlached mode motion interrupt (ULMODE). This mode is always by default enabled. + * + * When this bit is set, the wakeup interrupt has to be cleared manually + * (cannot use interrupt response with pulse) + * + * Sensor must be in standby mode before performing this action + * + * @throws std::runtime_error on failure + */ + void EnableWakeUpLatch(); + + /** + * @brief Disables the Unlached mode motion interrupt (ULMODE). This mode is always by default enabled. + * (cannot use interrupt response with pulse) + * + * The wakeup threshold is advised to not be very low to avoid interrupt being triggered in + * an almost continuous manner + * + * Sensor must be in standby mode before performing this action + * + * When this bit is cleared, and the interrupt response type is set to Pulse, then upon a wakeup event + * the wakeup interrupt signal will pulse and return low, but only once. Then, the interrupt + * output will not reset until data is read or interrupt cleared. + + * @throws std::runtime_error on failure + */ + void DisableWakeUpLatch(); + + /** + * @brief Sets the timer counter of motion before sending a wakeup interrupt + * + * The count is limited to values from 1 to 255 + * + * Every count is calculated as (1 / Wakeup_ODR_FREQUENCY) where Wakeup_ODR_FREQUENCY + * is the current odr_in_Hz value from odr_map_in_Hz_wakeup value + * + * Sensor must be in standby mode before performing this action + * + * See datasheet for more details + * + * @param count The timer count from 1 to 255 + * @throws std::runtime_error on failure + */ + void SetWakeUpMotionCounter(uint8_t count); + + /** + * @brief Sets the timer of motion before sending a wakeup interrupt + * + * the desired time should be such that (0 < desired_time * wakeup_odr_frequency <= 255) + * + * Sensor must be in standby mode before performing this action + * + * See datasheet for more details + * + * @param desired_time The desired time in seconds + * @throws std::runtime_error on failure + */ + void SetWakeUpMotionTime(float desired_time); + + /** + * @brief Get the current count value of the timer of motion before sending a wakeup interrupt + * + * @return Time value in seconds + * @throws std::runtime_error on failure + */ + float GetWakeUpMotionTime(); + + /** + * @brief Sets the timer counter of non-activity before sending another wakeup interrupt + * + * The count is limited to values from 1 to 255 + * + * Every count is calculated as (1 / Wakeup_ODR_FREQUENCY) where Wakeup_ODR_FREQUENCY + * is the current odr_in_Hz value from odr_map_in_Hz_wakeup value + * + * Sensor must be in standby mode before performing this action + * + * See datasheet for more details + * + * @param count The timer count from 1 to 255 + * @throws std::runtime_error on failure + */ + void SetWakeUpNonActivityCounter(uint8_t count); + + /** + * @brief Sets the timer of non-activity before sending another wakeup interrupt + * + * the desired time should be such that (0 < desired_time * wakeup_odr_frequency <= 255) + * + * Sensor must be in standby mode before performing this action + * + * See datasheet for more details + * + * @param desired_time The desired time in seconds + * @throws std::runtime_error on failure + */ + void SetWakeUpNonActivityTime(float desired_time); + + /** + * @brief Get the current count value of the timer of non-activity before sending another wakeup interrupt + * + * @return Time value in seconds + * @throws std::runtime_error on failure + */ + float GetWakeUpNonActivityTime(); + + /** + * @brief Sets the threshold counter for acceleration difference before sending a wakeup interrupt + * + * The count is limited to values from 1 to 4096, that it is 16g threshold with (3.9mg/count) + * It is advised to not set the threshold to a very low value which may cause bad behaviour + * in the wakeup interrupt + * + * Sensor must be in standby mode before performing this action + * + * See datasheet for more details + * + * @param count The timer count from 1 to 4096, that it is 16g threshold with (3.9mg/count) + * @throws std::runtime_error on failure + */ + void SetWakeUpThresholdCounter(uint16_t count); + + /** + * @brief Sets the threshold g value for acceleration difference before sending a wakeup interrupt + * + * The count is limited to values up to 16g, with steps of 3.9mg. It is advised to not set + * the threshold to a very low value which may cause bad behaviour in the wakeup interrupt + * + * Sensor must be in standby mode before performing this action + * + * See datasheet for more details + * + * @param g_threshold The acceleration threshold (in g) in g, from 3.9mg to 16g, steps of 3.9mg/count. + * @throws std::runtime_error on failure + */ + void SetWakeUpThresholdGRange(float g_threshold); + + /** + * @brief Get the current threshold difference value before sending wakeup interrupt + * + * @return Threshold value in g + * @throws std::runtime_error on failure + */ + float GetWakeUpThresholdGRange(); + +private: + kxtj3_context m_kxtj3; +}; +} // namespace upm diff --git a/src/kxtj3/kxtj3.json b/src/kxtj3/kxtj3.json new file mode 100755 index 00000000..8ea6169c --- /dev/null +++ b/src/kxtj3/kxtj3.json @@ -0,0 +1,63 @@ +{ + "Library": "KXTJ3", + "Description": "Kionix KXTJ3 tri-axis accelerometer sensor library", + "Sensor Class": { + "KXTJ3": { + "Name": "Kionix KXTJ3 tri-axis accelerometer sensor", + "Description": "This is the UPM Module for the Kionix KXTJ3 accelerometer sensor. The Kionix KXTJ3 sensor is a multifunctional sensor that provides multiple functionalities. The sensor provides an extended g-range up to +/- 16g, higher resolution embedded wake-up function down to 3.9mg, and a flexible interrupt circuitry. The KXTJ3 sense element offers lower noise performance, exceptional shock resiliency, stable performance over temperature, and virtually eliminates offset and sensitivity shifts from reflow.", + "Categories": [ + "acceleration" + ], + "Connections": [ + "i2c" + ], + "Project Type": [ + "prototyping", + "commercial" + ], + "Manufacturers": [ + "Kionix" + ], + "Image": "kxtj3.png", + "Examples": { + "C++": [ + "kxtj3.cxx" + ], + "C": [ + "kxtj3.c" + ] + }, + "Specifications": { + "Supply Voltage (VDD)": { + "unit": "V", + "min": 1.71, + "typical": 2.5, + "max": 3.6 + }, + "I/O Pads Supply Voltage (IO_VDD)": { + "unit": "V", + "min": 1.7, + "max": 3.6 + }, + "Supply Current": { + "unit": "uA", + "min": 0.9, + "max": 155 + }, + "Operating Temperature": { + "unit": "°C", + "min": -40, + "max": 85 + } + }, + "Urls": { + "Product Pages": [ + "http://www.kionix.com/product/KXTJ3-1057" + ], + "Datasheets": [ + "http://kionixfs.kionix.com/en/datasheet/KXTJ3-1057-Specifications-Rev-3.0.pdf" + ] + } + } + } +} \ No newline at end of file diff --git a/src/kxtj3/kxtj3_registers.h b/src/kxtj3/kxtj3_registers.h new file mode 100755 index 00000000..ea2d4440 --- /dev/null +++ b/src/kxtj3/kxtj3_registers.h @@ -0,0 +1,204 @@ +/* +The MIT License (MIT) +Copyright (c) 2017 Kionix Inc. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef __KXTJ3_REGISTERS_H__ +#define __KXTJ3_REGISTERS_H__ +/* registers */ +// output register x +#define KXTJ3_XOUT_L 0x06 +#define KXTJ3_XOUT_H 0x07 +// output register y +#define KXTJ3_YOUT_L 0x08 +#define KXTJ3_YOUT_H 0x09 +// output register z +#define KXTJ3_ZOUT_L 0x0A +#define KXTJ3_ZOUT_H 0x0B +// This register can be used to verify proper integrated circuit functionality +#define KXTJ3_DCST_RESP 0x0C +// This register can be used for supplier recognition, as it can be factory written to a known byte value. +#define KXTJ3_WHO_AM_I 0x0F +// This register reports which function caused an interrupt. +#define KXTJ3_INT_SOURCE1 0x16 +// This register reports the axis and direction of detected motion +#define KXTJ3_INT_SOURCE2 0x17 +// This register reports the status of the interrupt +#define KXTJ3_STATUS_REG 0x18 +#define KXTJ3_INT_REL 0x1A +// Read/write control register that controls the main feature set +#define KXTJ3_CTRL_REG1 0x1B +// Read/write control register that provides more feature set control +#define KXTJ3_CTRL_REG2 0x1D +// This register controls the settings for the physical interrupt pin +#define KXTJ3_INT_CTRL_REG1 0x1E +// This register controls which axis and direction of detected motion can cause an interrupt +#define KXTJ3_INT_CTRL_REG2 0x1F +// Read/write control register that configures the acceleration outputs +#define KXTJ3_DATA_CTRL_REG 0x21 +#define KXTJ3_WAKEUP_COUNTER 0x29 +#define KXTJ3_NA_COUNTER 0x2A +// When 0xCA is written to this register, the MEMS self-test function is enabled +#define KXTJ3_SELF_TEST 0x3A +#define KXTJ3_WAKEUP_THRESHOLD_H 0x6A +#define KXTJ3_WAKEUP_THRESHOLD_L 0x6B +// This register can be used for supplier recognition, as it can be factory written to a known byte value. +#define KXCJC_WHO_AM_I 0x0F +/* registers bits */ +// before set +#define KXTJ3_DCST_RESP_DCSTR_BEFORE (0x55 << 0) +// after set +#define KXTJ3_DCST_RESP_DCSTR_AFTER (0xAA << 0) +// WHO_AM_I -value for KXTJ3 +#define KXTJ3_WHO_AM_I_WIA_ID (0x35 << 0) +// indicates that new acceleration data +#define KXTJ3_INT_SOURCE1_DRDY (0x01 << 4) +// Wake up +#define KXTJ3_INT_SOURCE1_WUFS (0x01 << 1) +// x- +#define KXTJ3_INT_SOURCE2_XNWU (0x01 << 5) +// x+ +#define KXTJ3_INT_SOURCE2_XPWU (0x01 << 4) +// y- +#define KXTJ3_INT_SOURCE2_YNWU (0x01 << 3) +// y+ +#define KXTJ3_INT_SOURCE2_YPWU (0x01 << 2) +// z- +#define KXTJ3_INT_SOURCE2_ZNWU (0x01 << 1) +// z+ +#define KXTJ3_INT_SOURCE2_ZPWU (0x01 << 0) +// reports the combined (OR) interrupt information of DRDY and WUFS in the interrupt source register +#define KXTJ3_STATUS_REG_INT (0x01 << 4) +// controls the operating mode of the KXTJ3 +#define KXTJ3_CTRL_REG1_PC (0x01 << 7) +// determines the performance mode of the KXTJ3 +#define KXTJ3_CTRL_REG1_RES (0x01 << 6) +// enables the reporting of the availability of new acceleration data as an interrupt +#define KXTJ3_CTRL_REG1_DRDYE (0x01 << 5) +// 2g range +#define KXTJ3_CTRL_REG1_GSEL_2G (0x00 << 2) +// 16g range +#define KXTJ3_CTRL_REG1_GSEL_16G (0x01 << 2) +// 4g range +#define KXTJ3_CTRL_REG1_GSEL_4G (0x02 << 2) +// 16g range +#define KXTJ3_CTRL_REG1_GSEL_16G2 (0x03 << 2) +// 8g range +#define KXTJ3_CTRL_REG1_GSEL_8G (0x04 << 2) +// 16g range +#define KXTJ3_CTRL_REG1_GSEL_16G3 (0x05 << 2) +// 8g range with 14b resolution +#define KXTJ3_CTRL_REG1_GSEL_8G_14 (0x06 << 2) +// 16g range with 14b resolution +#define KXTJ3_CTRL_REG1_GSEL_16G_14 (0x07 << 2) +// enables 14-bit mode if GSEL = '11' +#define KXTJ3_CTRL_REG1_EN16G (0x01 << 2) +// enables the Wake Up (motion detect) function. +#define KXTJ3_CTRL_REG1_WUFE (0x01 << 1) +// initiates software reset +#define KXTJ3_CTRL_REG2_SRST (0x01 << 7) +// initiates the digital communication self-test function. +#define KXTJ3_CTRL_REG2_DCST (0x01 << 4) +// 0.78Hz +#define KXTJ3_CTRL_REG2_OWUF_0P781 (0x00 << 0) +// 1.563Hz +#define KXTJ3_CTRL_REG2_OWUF_1P563 (0x01 << 0) +// 3.125Hz +#define KXTJ3_CTRL_REG2_OWUF_3P125 (0x02 << 0) +// 6.25Hz +#define KXTJ3_CTRL_REG2_OWUF_6P25 (0x03 << 0) +// 12.5Hz +#define KXTJ3_CTRL_REG2_OWUF_12P5 (0x04 << 0) +// 25Hz +#define KXTJ3_CTRL_REG2_OWUF_25 (0x05 << 0) +// 50Hz +#define KXTJ3_CTRL_REG2_OWUF_50 (0x06 << 0) +// 100Hz +#define KXTJ3_CTRL_REG2_OWUF_100 (0x07 << 0) +// enables/disables the physical interrupt pin +#define KXTJ3_INT_CTRL_REG1_IEN (0x01 << 5) +// sets the polarity of the physical interrupt pin +#define KXTJ3_INT_CTRL_REG1_IEA (0x01 << 4) +// sets the response of the physical interrupt pin +#define KXTJ3_INT_CTRL_REG1_IEL (0x01 << 3) +// selftest polarity +#define KXTJ3_INT_CTRL_REG1_STPOL (0x01 << 1) +// Unlatched mode motion interrupt; 0=disabled,1=enabled +#define KXTJ3_INT_CTRL_REG2_ULMODE (0x01 << 7) +// x- +#define KXTJ3_INT_CTRL_REG2_XNWU (0x01 << 5) +// x+ +#define KXTJ3_INT_CTRL_REG2_XPWU (0x01 << 4) +// y- +#define KXTJ3_INT_CTRL_REG2_YNWU (0x01 << 3) +// y+ +#define KXTJ3_INT_CTRL_REG2_YPWU (0x01 << 2) +// z- +#define KXTJ3_INT_CTRL_REG2_ZNWU (0x01 << 1) +// z+ +#define KXTJ3_INT_CTRL_REG2_ZPWU (0x01 << 0) +// 12.5Hz +#define KXTJ3_DATA_CTRL_REG_OSA_12P5 (0x00 << 0) +// 25Hz +#define KXTJ3_DATA_CTRL_REG_OSA_25 (0x01 << 0) +// 50Hz +#define KXTJ3_DATA_CTRL_REG_OSA_50 (0x02 << 0) +// 100Hz +#define KXTJ3_DATA_CTRL_REG_OSA_100 (0x03 << 0) +// 200Hz +#define KXTJ3_DATA_CTRL_REG_OSA_200 (0x04 << 0) +// 400Hz +#define KXTJ3_DATA_CTRL_REG_OSA_400 (0x05 << 0) +// 800Hz +#define KXTJ3_DATA_CTRL_REG_OSA_800 (0x06 << 0) +// 1600Hz +#define KXTJ3_DATA_CTRL_REG_OSA_1600 (0x07 << 0) +// 0.78Hz +#define KXTJ3_DATA_CTRL_REG_OSA_0P781 (0x08 << 0) +// 1.563Hz +#define KXTJ3_DATA_CTRL_REG_OSA_1P563 (0x09 << 0) +// 3.125Hz +#define KXTJ3_DATA_CTRL_REG_OSA_3P125 (0x0A << 0) +// 6.25Hz +#define KXTJ3_DATA_CTRL_REG_OSA_6P25 (0x0B << 0) +// charge on +#define KXTJ3_SELF_TEST_MEMS_TEST_ENABLE (0xCA << 0) +// charge off +#define KXTJ3_SELF_TEST_MEMS_TEST_DISABLE (0x00 << 0) +// WHO_AM_I -value for KXCJC +#define KXCJC_WHO_AM_I_WIA_ID (0x36 << 0) +/*registers bit masks */ + +#define KXTJ3_DCST_RESP_DCSTR_MASK 0xFF + +#define KXTJ3_WHO_AM_I_WIA_MASK 0xFF +// selects the acceleration range of the accelerometer outputs +#define KXTJ3_CTRL_REG1_GSEL_MASK 0x1C +// sets the Output Data Rate for the Wake Up function +#define KXTJ3_CTRL_REG2_OWUF_MASK 0x07 +// sets the output data rate (ODR) +#define KXTJ3_DATA_CTRL_REG_OSA_MASK 0x0F + +#define KXTJ3_SELF_TEST_MEMS_TEST_MASK 0xFF + +#define KXCJC_WHO_AM_I_WIA_MASK 0xFF +#endif