diff --git a/drivers/sensors/Kconfig b/drivers/sensors/Kconfig index 969f5e1010..b5fc76cb41 100644 --- a/drivers/sensors/Kconfig +++ b/drivers/sensors/Kconfig @@ -75,12 +75,41 @@ config SENSOR_KXTJ9_I2C_BUS_SPEED endif # SENSOR_KXTJ9 +config LIS2DH + bool "STMicro LIS2DH device support" + default n + select I2C + ---help--- + Enable driver support for the STMicro LIS2DH accelerometer + +if LIS2DH + +config DEBUG_LIS2DH + bool "Debug support for the LIS2DH" + default n + ---help--- + Enables debug features for the LIS2DH + +config LIS2DH_NPOLLWAITERS + int "Number of waiters to poll" + default 2 + ---help--- + Maximum number of threads that can be waiting on poll() + +config LIS2DH_DRIVER_SELFTEST + bool "Enable selftest in LIS2DH driver" + default n + ---help--- + Enable selftest in LIS2DH driver + +endif # LIS2DH + config LIS3DSH - bool "STMicro LIS3DSH 3-Axis acclerometer support" + bool "STMicro LIS3DSH 3-Axis accelerometer support" default n select SPI ---help--- - Enable driver support for the STMicro LIS3DSH 3-Axis acclerometer. + Enable driver support for the STMicro LIS3DSH 3-Axis accelerometer. config LIS331DL bool "STMicro LIS331DL device support" diff --git a/drivers/sensors/Make.defs b/drivers/sensors/Make.defs index b676c4aa8e..7a5d5a0bba 100644 --- a/drivers/sensors/Make.defs +++ b/drivers/sensors/Make.defs @@ -53,6 +53,10 @@ ifeq ($(CONFIG_SENSOR_KXTJ9),y) CSRCS += kxtj9.c endif +ifeq ($(CONFIG_LIS2DH),y) + CSRCS += lis2dh.c +endif + ifeq ($(CONFIG_LIS3DSH),y) CSRCS += lis3dsh.c endif diff --git a/drivers/sensors/lis2dh.c b/drivers/sensors/lis2dh.c new file mode 100644 index 0000000000..003baa390a --- /dev/null +++ b/drivers/sensors/lis2dh.c @@ -0,0 +1,2033 @@ +/**************************************************************************** + * drivers/sensors/lis2dh.c + * LIS2DH accelerometer driver + * + * Copyright (C) 2014-2017 Haltian Ltd. All rights reserved. + * Authors: Timo Voutilainen + * Jussi Kivilinna + * Juha Niskanen + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#ifdef CONFIG_DEBUG_LIS2DH +# define lis2dh_dbg(x, ...) _info(x, ##__VA_ARGS__) +#else +# define lis2dh_dbg(x, ...) sninfo(x, ##__VA_ARGS__) +#endif + +#ifdef CONFIG_LIS2DH_DRIVER_SELFTEST +# define LSB_AT_10BIT_RESOLUTION 4 +# define LSB_AT_12BIT_RESOLUTION 1 +# define SELFTEST_BUF_SIZE 5 +# define SELFTEST_MAX_READ_ATTEMPTS 200 +# define SELFTEST_ABS_DIFF_MIN_10BIT 17 +# define SELFTEST_ABS_DIFF_MAX_10_BIT 360 +# define SELFTEST_ABS_DIFF_MIN_12BIT (LSB_AT_10BIT_RESOLUTION * SELFTEST_ABS_DIFF_MIN_10BIT) +# define SELFTEST_ABS_DIFF_MAX_12BIT (LSB_AT_10BIT_RESOLUTION * SELFTEST_ABS_DIFF_MAX_10_BIT) +# define SELFTEST_0 0 +# define SELFTEST_1 1 +#endif + +/* Miscellaneous macros */ + +#define LIS2DH_I2C_RETRIES 10 +#define LIS2DH_COUNT_INTS + +/**************************************************************************** + * Private Data Types + ****************************************************************************/ + +enum interrupts +{ + LIS2DH_INT1 = 1, + LIS2DH_INT2 = 2 +}; + +struct lis2dh_dev_s +{ + FAR struct i2c_master_s *i2c; /* I2C interface */ + uint8_t addr; /* I2C address */ + FAR struct lis2dh_config_s *config; /* Platform specific configuration */ + struct lis2dh_setup *setup; /* User defined device operation mode setup */ + struct lis2dh_vector_s vector_data; /* Latest read data read from lis2dh */ + int scale; /* Full scale in milliG */ + sem_t devsem; /* Manages exclusive access to this structure */ + bool fifo_used; /* LIS2DH configured to use FIFO */ + bool fifo_stopped;/* FIFO got full and has stopped. */ +#ifdef LIS2DH_COUNT_INTS + volatile int16_t int_pending; /* Interrupt received but data not read, yet */ +#else + volatile bool int_pending; /* Interrupt received but data not read, yet */ +#endif +#ifndef CONFIG_DISABLE_POLL + struct pollfd *fds[CONFIG_LIS2DH_NPOLLWAITERS]; +#endif +}; + +/**************************************************************************** + * Private Function prototypes + ****************************************************************************/ + +static int lis2dh_open(FAR struct file *filep); +static int lis2dh_close(FAR struct file *filep); +static ssize_t lis2dh_read(FAR struct file *, FAR char *, size_t); +static ssize_t lis2dh_write(FAR struct file *filep, + FAR const char *buffer, size_t buflen); +static int lis2dh_ioctl(FAR struct file *filep, int cmd, + unsigned long arg); +static int lis2dh_access(FAR struct lis2dh_dev_s *dev, + uint8_t subaddr, FAR uint8_t *buf, int length); +static int lis2dh_get_reading(FAR struct lis2dh_dev_s *dev, + FAR struct lis2dh_vector_s *res, bool force_read); +static int lis2dh_powerdown(FAR struct lis2dh_dev_s dev); +static int lis2dh_reboot(FAR struct lis2dh_dev_s *dev); +static int lis2dh_poll(FAR struct file *filep, + FAR struct pollfd *fds, bool setup); +static void lis2dh_notify(FAR struct lis2dh_dev_s *priv); +static int lis2dh_int_handler(int irq, FAR void *context, + FAR void *arg); +static int lis2dh_setup(FAR struct lis2dh_dev_s *dev, + FAR struct lis2dh_setup *new_setup); +static inline int16_t lis2dh_raw_to_mg(uint8_t raw_hibyte, + uint8_t raw_lobyte, int scale); +static int lis2dh_read_temp(FAR struct lis2dh_dev_s *dev, + FAR int16_t *temper); +static int lis2dh_clear_interrupts(FAR struct lis2dh_dev_s *priv, + uint8_t interrupts); +static unsigned int lis2dh_get_fifo_readings(FAR struct lis2dh_dev_s *priv, + FAR struct lis2dh_result *res, unsigned int readcount, + FAR int *perr); +#ifdef CONFIG_LIS2DH_DRIVER_SELFTEST +static int lis2dh_handle_selftest(FAR struct lis2dh_dev_s *priv); +static int16_t lis2dh_raw_convert_to_12bit(uint8_t raw_hibyte, + uint8_t raw_lobyte); +static FAR const struct lis2dh_vector_s * + lis2dh_get_raw_readings(FAR struct lis2dh_dev_s *dev, + FAR int *err); +#endif + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct file_operations g_lis2dhops = +{ + lis2dh_open, /* open */ + lis2dh_close, /* close */ + lis2dh_read, /* read */ + lis2dh_write, /* write */ + NULL, /* seek */ + lis2dh_ioctl /* ioctl */ +#ifndef CONFIG_DISABLE_POLL + , lis2dh_poll /* poll */ +#endif +#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS + , NULL /* unlink */ +#endif +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static int lis2dh_who_am_i(FAR struct lis2dh_dev_s *dev, uint8_t *id) +{ + int ret; + + ret = lis2dh_access(dev, ST_LIS2DH_WHOAMI_REG, id, 1); + if (ret < 0) + { + lis2dh_dbg("Cannot read who am i value\n"); + return -EIO; + } + + return ret; +} + +/**************************************************************************** + * Name: lis2dh_open + * + * Description: + * This function is called whenever the LIS2DH device is opened. + * + ****************************************************************************/ + +static int lis2dh_open(FAR struct file *filep) +{ + FAR struct inode *inode = filep->f_inode; + FAR struct lis2dh_dev_s *priv = inode->i_private; + uint8_t regval; + int ret = OK; + + /* Probe device */ + + if (lis2dh_access(priv, ST_LIS2DH_WHOAMI_REG, ®val, 1) > 0) + { + /* Check chip identification, in the future several more compatible parts + * may be added here. + */ + + if (regval == ST_LIS2DH_WHOAMI_VALUE) + { + priv->config->irq_enable(priv->config, true); + /* Normal exit point */ + ret = lis2dh_clear_interrupts(priv, LIS2DH_INT1 | LIS2DH_INT2); + return ret; + } + + /* Otherwise, we mark an invalid device found at given address */ + + ret = -ENODEV; + } + else + { + /* No response at given address is marked as */ + + ret = -EFAULT; + } + + /* Error exit */ + + return ret; +} + +/**************************************************************************** + * Name: lis2dh_close + * + * Description: + * This routine is called when the LIS2DH device is closed. + * + ****************************************************************************/ + +static int lis2dh_close(FAR struct file *filep) +{ + FAR struct inode *inode = filep->f_inode; + FAR struct lis2dh_dev_s *priv = inode->i_private; + + priv->config->irq_enable(priv->config, false); + return lis2dh_powerdown(priv); +} + +/**************************************************************************** + * Name: lis2dh_fifo_start + * + * Description: + * This function restarts FIFO reading. + * + ****************************************************************************/ + +static int lis2dh_fifo_start(FAR struct lis2dh_dev_s *priv) +{ + uint8_t buf; + int err = OK; + + buf = 0x00 | priv->setup->trigger_selection | + priv->setup->fifo_trigger_threshold; + if (lis2dh_access(priv, ST_LIS2DH_FIFO_CTRL_REG, &buf, -1) != 1) + { + lis2dh_dbg("lis2dh: Failed to write FIFO control register\n"); + err = -EIO; + } + else + { + buf = priv->setup->fifo_mode | priv->setup->trigger_selection | + priv->setup->fifo_trigger_threshold; + if (lis2dh_access(priv, ST_LIS2DH_FIFO_CTRL_REG, &buf, -1) != 1) + { + lis2dh_dbg("lis2dh: Failed to write FIFO control register\n"); + err = -EIO; + } + else + { + priv->fifo_stopped = false; + + lis2dh_dbg("lis2dh: FIFO restarted\n"); + } + } + + return err; +} + +/**************************************************************************** + * Name: lis2dh_read + * + * Description: + * This routine is called when the LIS2DH device is read. + * + ****************************************************************************/ + +static ssize_t lis2dh_read(FAR struct file *filep, FAR char *buffer, + size_t buflen) +{ + FAR struct inode *inode = filep->f_inode; + FAR struct lis2dh_dev_s *priv = inode->i_private; + FAR struct lis2dh_result *ptr; + int readcount = (buflen - sizeof(struct lis2dh_res_header)) / sizeof(struct lis2dh_vector_s); + uint8_t buf; + uint8_t int1_src = 0; + uint8_t int2_src = 0; + irqstate_t flags; + int err; + + if (buflen < sizeof(struct lis2dh_result) || + (buflen - sizeof(struct lis2dh_res_header)) % sizeof(struct lis2dh_vector_s) != 0) + { + lis2dh_dbg("lis2dh: Illegal amount of bytes to read: %d\n", buflen); + return -EINVAL; + } + + flags = enter_critical_section(); +#ifdef LIS2DH_COUNT_INTS + if (priv->int_pending > 0) + { + priv->int_pending--; + } + + DEBUGASSERT(priv->int_pending >= 0 && priv->int_pending < 10); +#else + priv->int_pending = false; +#endif + leave_critical_section(flags); + + /* Set pointer to first measurement data */ + + ptr = (FAR struct lis2dh_result *)buffer; + + err = sem_wait(&priv->devsem); + if (err < 0) + { + return -EINTR; + } + + ptr->header.meas_count = 0; + + if (!priv->fifo_used) + { + /* FIFO not used, read only one sample. */ + + if (readcount > 0) + { + err = lis2dh_get_reading(priv, &ptr->measurements[0], true); + if (err < 0) + { + lis2dh_dbg("lis2dh: Failed to read xyz\n"); + } + else + { + ptr->header.meas_count = 1; + } + } + } + else /* FIFO modes */ + { + uint8_t fifo_mode = priv->setup->fifo_mode & ST_LIS2DH_FIFOCR_MODE_MASK; + bool fifo_empty = false; + uint8_t fifo_num_samples; + + ptr->header.meas_count = 0; + + do + { + /* Check if FIFO needs to be restarted after being read empty. + * We need to read SRC_REG before reading measurement, as reading + * sample from FIFO clears OVRN_FIFO flag. + */ + + if (lis2dh_access(priv, ST_LIS2DH_FIFO_SRC_REG, &buf, 1) != 1) + { + lis2dh_dbg("lis2dh: Failed to read FIFO source register\n"); + return -EIO; + } + + if (fifo_mode != LIS2DH_STREAM_MODE) + { + /* FIFO is full and has stopped. */ + + priv->fifo_stopped |= !!(buf & ST_LIS2DH_FIFOSR_OVRN_FIFO); + } + + if (buf & ST_LIS2DH_FIFOSR_OVRN_FIFO) + { + lis2dh_dbg("lis2dh: FIFO overrun\n"); + } + + if (buf & ST_LIS2DH_FIFOSR_EMPTY) + { + lis2dh_dbg("lis2dh: FIFO empty\n"); + + fifo_empty = true; + + if (fifo_mode != LIS2DH_STREAM_MODE) + { + priv->fifo_stopped = true; + } + + /* FIFO is empty, skip reading. */ + + break; + } + + /* How many samples available in FIFO? */ + + fifo_num_samples = (buf & ST_LIS2DH_FIFOSR_NUM_SAMP_MASK) + 1; + + if (fifo_num_samples > readcount) + { + fifo_num_samples = readcount; + } + + ptr->header.meas_count += + lis2dh_get_fifo_readings(priv, ptr, fifo_num_samples, &err); + } + while (!fifo_empty && ptr->header.meas_count < readcount); + + if (!fifo_empty && fifo_mode != LIS2DH_TRIGGER_MODE) + { + /* FIFO was not read empty, more data available. */ + + flags = enter_critical_section(); + +#ifdef LIS2DH_COUNT_INTS + priv->int_pending++; +#else + priv->int_pending = true; +#endif + +#ifndef CONFIG_DISABLE_POLL + lis2dh_notify(priv); +#endif + + leave_critical_section(flags); + } + else if (fifo_mode != LIS2DH_STREAM_MODE && priv->fifo_stopped) + { + /* FIFO is empty and has stopped by overrun event. Reset FIFO for + * further reading. + */ + + err = lis2dh_fifo_start(priv); + } + } + + /* Make sure interrupt will get cleared (by reading this register) in case of + * latched configuration. + */ + + buf = 0; + if (lis2dh_access(priv, ST_LIS2DH_INT1_SRC_REG, &buf, 1) != 1) + { + lis2dh_dbg("lis2dh: Failed to read INT1_SRC_REG\n"); + err = -EIO; + } + if (buf & ST_LIS2DH_INT_SR_ACTIVE) + { + /* Interrupt has happened */ + + int1_src = buf; + ptr->header.int1_occurred = true; + } + else + { + ptr->header.int1_occurred = false; + } + + /* Make sure interrupt will get cleared (by reading this register) in case of + * latched configuration. + */ + + buf = 0; + if (lis2dh_access(priv, ST_LIS2DH_INT2_SRC_REG, &buf, 1) != 1) + { + lis2dh_dbg("lis2dh: Failed to read INT2_SRC_REG\n"); + err = -EIO; + } + if (buf & ST_LIS2DH_INT_SR_ACTIVE) + { + /* Interrupt has happened */ + + int2_src = buf; + ptr->header.int2_occurred = true; + } + else + { + ptr->header.int2_occurred = false; + } + ptr->header.int1_source = int1_src; + ptr->header.int2_source = int2_src; + + sem_post(&priv->devsem); + + /* 'err' was just for debugging, we do return partial reads here. */ + + return sizeof(ptr->header) + + ptr->header.meas_count * sizeof(struct lis2dh_vector_s); +} + +/**************************************************************************** + * Name: lis2dh_write + * Description: + * This routine is called when the LIS2DH device is written to. + ****************************************************************************/ + +static ssize_t lis2dh_write(FAR struct file *filep, FAR const char *buffer, + size_t buflen) +{ + DEBUGASSERT(filep != NULL && buffer != NULL && buflen > 0); + + return -ENOSYS; +} + +/**************************************************************************** + * Name: lis2dh_ioctl + * + * Description: + * This routine is called when ioctl function call + * for the LIS2DH device is done. + * + ****************************************************************************/ + +static int lis2dh_ioctl(FAR struct file *filep, int cmd, unsigned long arg) +{ + FAR struct inode *inode = filep->f_inode; + FAR struct lis2dh_dev_s *priv = inode->i_private; + int ret; + uint8_t buf; + + DEBUGASSERT(filep != NULL); + + ret = sem_wait(&priv->devsem); + if (ret < 0) + { + return -EINTR; + } + + ret = OK; + switch (cmd) + { + case SNIOC_WRITESETUP: + { + /* Write to the configuration registers. Arg: uint8_t value */ + + ret = lis2dh_setup(priv, (struct lis2dh_setup *)arg); + lis2dh_dbg("lis2dh: conf: %02x ret: %d\n", *(uint8_t*)arg, ret); + + /* Make sure interrupt will get cleared (by reading this register) in + * case of latched configuration. + */ + + lis2dh_clear_interrupts(priv, LIS2DH_INT1 | LIS2DH_INT2); + } + break; + + case SNIOC_WRITE_INT1THRESHOLD: + { + buf = (uint8_t)arg; + + if (lis2dh_access(priv, ST_LIS2DH_INT1_THS_REG, &buf, -1) != 1) + { + lis2dh_dbg("lis2dh: Failed to write INT1_THS_REG\n"); + ret = -EIO; + } + + lis2dh_clear_interrupts(priv, LIS2DH_INT2); + } + break; + + case SNIOC_WRITE_INT2THRESHOLD: + { + buf = (uint8_t)arg; + + if (lis2dh_access(priv, ST_LIS2DH_INT2_THS_REG, &buf, -1) != 1) + { + lis2dh_dbg("lis2dh: Failed to write INT2_THS_REG\n"); + ret = -EIO; + } + + lis2dh_clear_interrupts(priv, LIS2DH_INT2); + } + break; + + case SNIOC_RESET_HPFILTER: + { + /* Read reference register to reset/recalib DC offset for HP filter */ + + if (lis2dh_access(priv, ST_LIS2DH_REFERENCE_REG, &buf, 1) != 1) + { + lis2dh_dbg("lis2dh: Failed to write reference register\n"); + ret = -EIO; + } + + lis2dh_clear_interrupts(priv, LIS2DH_INT2); + } + break; + + case SNIOC_START_SELFTEST: +#ifdef CONFIG_LIS2DH_DRIVER_SELFTEST + { + priv->config->irq_enable(priv->config, false); + lis2dh_clear_interrupts(priv, LIS2DH_INT1 | LIS2DH_INT2); + ret = lis2dh_handle_selftest(priv); + priv->config->irq_enable(priv->config, true); + } +#else + { + ret = -EINVAL; + } +#endif + break; + + case SNIOC_READ_TEMP: + { + ret = lis2dh_read_temp(priv, (int16_t *)arg); + } + break; + + case SNIOC_WHO_AM_I: + { + ret = lis2dh_who_am_i(priv, (uint8_t *)arg); + } + break; + + default: + { + lis2dh_dbg("lis2dh: Unrecognized cmd: %d\n", cmd); + ret = -ENOTTY; + } + break; + } + + sem_post(&priv->devsem); + return ret; +} + +/**************************************************************************** + * Name: lis2dh_poll + * + * Description: + * This routine is called during LIS2DH device poll + * + ****************************************************************************/ + +#ifndef CONFIG_DISABLE_POLL +static int lis2dh_poll(FAR struct file *filep, FAR struct pollfd *fds, + bool setup) +{ + FAR struct inode *inode; + FAR struct lis2dh_dev_s *priv; + int ret = OK; + int i; + + DEBUGASSERT(filep && fds); + inode = filep->f_inode; + + DEBUGASSERT(inode && inode->i_private); + priv = (FAR struct lis2dh_dev_s *)inode->i_private; + + ret = sem_wait(&priv->devsem); + if (ret < 0) + { + return -EINTR; + } + + if (setup) + { + /* Ignore waits that do not include POLLIN */ + + if ((fds->events & POLLIN) == 0) + { + ret = -EDEADLK; + goto out; + } + + /* This is a request to set up the poll. Find an available + * slot for the poll structure reference + */ + + for (i = 0; i < CONFIG_LIS2DH_NPOLLWAITERS; i++) + { + /* Find an available slot */ + + if (!priv->fds[i]) + { + /* Bind the poll structure and this slot */ + + priv->fds[i] = fds; + fds->priv = &priv->fds[i]; + break; + } + } + + if (i >= CONFIG_LIS2DH_NPOLLWAITERS) + { + fds->priv = NULL; + ret = -EBUSY; + goto out; + } + + if (priv->int_pending) + { + lis2dh_notify(priv); + } + } + else if (fds->priv) + { + /* This is a request to tear down the poll. */ + + struct pollfd **slot = (struct pollfd **)fds->priv; + DEBUGASSERT(slot != NULL); + + /* Remove all memory of the poll setup */ + + *slot = NULL; + fds->priv = NULL; + } + +out: + sem_post(&priv->devsem); + return ret; +} + +static void lis2dh_notify(FAR struct lis2dh_dev_s *priv) +{ + DEBUGASSERT(priv != NULL); + + int i; + + /* If there are threads waiting on poll() for LIS2DH data to become available, + * then wake them up now. NOTE: we wake up all waiting threads because we + * do not know that they are going to do. If they all try to read the data, + * then some make end up blocking after all. + */ + + for (i = 0; i < CONFIG_LIS2DH_NPOLLWAITERS; i++) + { + struct pollfd *fds = priv->fds[i]; + if (fds) + { + fds->revents |= POLLIN; + lis2dh_dbg("lis2dh: Report events: %02x\n", fds->revents); + sem_post(fds->sem); + } + } +} +#endif /* !CONFIG_DISABLE_POLL */ + +/**************************************************************************** + * Name: lis2dh_callback + * + * Description: + * lis2dh interrupt handler + * + ****************************************************************************/ + +static int lis2dh_int_handler(int irq, FAR void *context, FAR void *arg) +{ + FAR struct lis2dh_dev_s *priv = (FAR struct lis2dh_dev_s *)arg; + irqstate_t flags; + + DEBUGASSERT(priv != NULL); + + flags = enter_critical_section(); + +#ifdef LIS2DH_COUNT_INTS + priv->int_pending++; +#else + priv->int_pending = true; +#endif + +#ifndef CONFIG_DISABLE_POLL + lis2dh_notify(priv); +#endif + + leave_critical_section(flags); + + return OK; +} + +#ifdef CONFIG_LIS2DH_DRIVER_SELFTEST +/**************************************************************************** + * Name: lis2dh_clear_registers + * + * Description: + * Clear lis2dh registers + * + * Input Parameters: + * priv - pointer to LIS2DH Private Structure + * + * Returned Value: + * Returns OK in case of success, otherwise ERROR + * + ****************************************************************************/ + +static int lis2dh_clear_registers(FAR struct lis2dh_dev_s *priv) +{ + uint8_t i, buf = 0; + + DEBUGASSERT(priv); + + for (i = ST_LIS2DH_TEMP_CFG_REG; i <= ST_LIS2DH_ACT_DUR_REG; i++) + { + /* Skip read only registers */ + + if ((i <= 0x1e) || (i >= 0x27 && i <= 0x2d) || (i == 0x2f) || (i == 0x31)) + { + continue; + } + + if (lis2dh_access(priv, i, &buf, -1) != 1) + { + lis2dh_dbg("lis2dh: Failed to clear register 0x%02x\n", i); + return ERROR; + } + } + + return OK; +} + +/**************************************************************************** + * Name: lis2dh_write_register + * + * Description: + * Clear lis2dh registers + * + * Input Parameters: + * priv - pointer to LIS2DH Private Structure + * reg - target register + * value - value to write + * + * Returned Value: + * Returns OK in case of success, otherwise ERROR + * + ****************************************************************************/ + +static int lis2dh_write_register(FAR struct lis2dh_dev_s *priv, uint8_t reg, + uint8_t value) +{ + DEBUGASSERT(priv); + + if (lis2dh_access(priv, reg, &value, -1) != 1) + { + lis2dh_dbg("lis2dh: Failed to write %d to register 0x%02x\n", + value, reg); + return ERROR; + } + return OK; +} + +/**************************************************************************** + * Name: lis2dh_read_register + * + * Description: + * read lis2dh register + * + * Input Parameters: + * priv - pointer to LIS2DH Private Structure + * reg - register to read + * + * Returned Value: + * Returns positive register value in case of success, otherwise ERROR ( < 0) + ****************************************************************************/ + +static int lis2dh_read_register(FAR struct lis2dh_dev_s *priv, uint8_t reg) +{ + uint8_t buf; + + DEBUGASSERT(priv); + + if (lis2dh_access(priv, reg, &buf, sizeof(buf)) == sizeof(buf)) + { + return buf; + } + + return ERROR; +} + +/**************************************************************************** + * Name: lis2dh_handle_selftest + * + * Description: + * Handle selftest. Note, that after running selftest lis2dh is left in + * shutdown mode without valid setup. Therefore SNIOC_WRITESETUP must be + * sent again to proceed with normal operations. + * + ****************************************************************************/ + +static int lis2dh_handle_selftest(FAR struct lis2dh_dev_s *priv) +{ + const struct lis2dh_vector_s *results; + uint8_t i; + uint8_t j; + uint8_t buf; + int16_t avg_x_no_st = 0; + int16_t avg_y_no_st = 0; + int16_t avg_z_no_st = 0; + int16_t avg_x_with_st = 0; + int16_t avg_y_with_st = 0; + int16_t avg_z_with_st = 0; + int16_t abs_st_x_value; + int16_t abs_st_y_value; + int16_t abs_st_z_value; + int ret = OK; + int err = OK; + + DEBUGASSERT(priv); + + lis2dh_powerdown(priv); + + if (lis2dh_clear_registers(priv) != OK) + { + ret = -EIO; + goto out; + } + + /* Set the control register (23h) to ±2g FS, normal mode with BDU (Block + * Data Update) and HR (High Resolution) bits enabled. + */ + + if (lis2dh_write_register(priv, ST_LIS2DH_CTRL_REG4, 0x88) != OK) + { + lis2dh_dbg("lis2dh: Failed to write CTRL4 REG for selftest\n"); + ret = -EIO; + goto out; + } + + /* Set the control register (20h) to 50Hz ODR (Output Data Rate) with + * X/Y/Z axis enabled. + */ + + if (lis2dh_write_register(priv, ST_LIS2DH_CTRL_REG1, 0x47) != OK) + { + lis2dh_dbg("lis2dh: Failed to write CTRL1 REG for selftest\n"); + ret = -EIO; + goto out; + } + + /* Dummy reads so that values have stabilized */ + + for (i = 0; i < 20; i++) + { + if (lis2dh_get_raw_readings(priv, &err) == NULL) + { + ret = -EIO; + goto out; + } + } + + for (i = 0; i < SELFTEST_BUF_SIZE; i++) + { + results = lis2dh_get_raw_readings(priv, &err); + if (results == NULL) + { + ret = -EIO; + goto out; + } + + avg_x_no_st += results->x; + avg_y_no_st += results->y; + avg_z_no_st += results->z; + } + + avg_x_no_st = avg_x_no_st / SELFTEST_BUF_SIZE; + avg_y_no_st = avg_y_no_st / SELFTEST_BUF_SIZE; + avg_z_no_st = avg_z_no_st / SELFTEST_BUF_SIZE; + + for (i = SELFTEST_0; i <= SELFTEST_1; i++) + { + avg_x_with_st = 0; + avg_y_with_st = 0; + avg_z_with_st = 0; + + /* Enable self-test 0 or 1 at +/-2g FS with BDU and HR bits enabled. */ + + buf = (i == SELFTEST_0) ? 0x8a : 0x8c; + + if (lis2dh_write_register(priv, ST_LIS2DH_CTRL_REG4, buf) != OK) + { + lis2dh_dbg("lis2dh: Failed to write CTRL4 REG for selftest\n"); + ret = -EIO; + goto out; + } + + /* Dummy reads so that values have stabilized */ + + for (i = 0; i < 10; i++) + { + if (lis2dh_get_raw_readings(priv, &err) == NULL) + { + ret = -EIO; + goto out; + } + } + + for (j = 0; j < SELFTEST_BUF_SIZE; j++) + { + results = lis2dh_get_raw_readings(priv, &err); + if (results == NULL) + { + ret = -EIO; + goto out; + } + + avg_x_with_st += results->x; + avg_y_with_st += results->y; + avg_z_with_st += results->z; + } + + avg_x_with_st = avg_x_with_st / SELFTEST_BUF_SIZE; + avg_y_with_st = avg_y_with_st / SELFTEST_BUF_SIZE; + avg_z_with_st = avg_z_with_st / SELFTEST_BUF_SIZE; + + abs_st_x_value = abs(avg_x_with_st - avg_x_no_st); + abs_st_y_value = abs(avg_y_with_st - avg_y_no_st); + abs_st_z_value = abs(avg_z_with_st - avg_z_no_st); + + dbg ("ST %d, ABSX: %d, ABSY: %d, ABSZ: %d\n", + i, abs_st_x_value, abs_st_y_value, abs_st_z_value); + + if (abs_st_x_value < SELFTEST_ABS_DIFF_MIN_12BIT || + abs_st_x_value > SELFTEST_ABS_DIFF_MAX_12BIT || + abs_st_y_value < SELFTEST_ABS_DIFF_MIN_12BIT || + abs_st_y_value > SELFTEST_ABS_DIFF_MAX_12BIT || + abs_st_z_value < SELFTEST_ABS_DIFF_MIN_12BIT || + abs_st_z_value > SELFTEST_ABS_DIFF_MAX_12BIT) + { + dbg("Selftest %d fail! Limits (%d <= value <= %d). " + "Results: x: %d, y: %d, z: %d ", + i, + SELFTEST_ABS_DIFF_MIN_12BIT, SELFTEST_ABS_DIFF_MAX_12BIT, + abs_st_x_value, abs_st_y_value, abs_st_z_value); + ret = -ERANGE; + goto out; + } + } + + /* Verify INT1 and INT2 lines */ + + if (lis2dh_clear_registers(priv) != OK) + { + ret = -EIO; + goto out; + } + + /* Both INT lines should be low */ + + if (priv->config->read_int1_pin() != 0) + { + dbg("INT1 line is HIGH - expected LOW\n"); + ret = -ENXIO; + goto out; + } + + if (priv->config->read_int2_pin) + { + if (priv->config->read_int2_pin() != 0) + { + dbg("INT2 line is HIGH - expected LOW\n"); + ret = -ENODEV; + goto out; + } + } + + /* 400Hz ODR all axes enabled + FIFO overrun & DATA READY on INT1 + FIFO enabled and INT1 & INT2 latched + FIFO mode, INT1 , THS 0 + OR combination, all events enabled */ + + if ((lis2dh_write_register(priv, ST_LIS2DH_CTRL_REG1, 0x77) != OK) || + (lis2dh_write_register(priv, ST_LIS2DH_CTRL_REG3, 0x12) != OK) || + (lis2dh_write_register(priv, ST_LIS2DH_CTRL_REG5, 0x4a) != OK) || + (lis2dh_write_register(priv, ST_LIS2DH_FIFO_CTRL_REG, 0x40) != OK) || + (lis2dh_write_register(priv, ST_LIS2DH_INT1_CFG_REG, 0x3f) != OK)) + { + dbg("Writing registers for INT line check failed\n"); + ret = -EIO; + goto out; + } + + /* Clear INT1 & INT2 */ + + if ((lis2dh_read_register(priv, ST_LIS2DH_INT1_SRC_REG) == ERROR) || + (lis2dh_read_register(priv, ST_LIS2DH_INT2_SRC_REG) == ERROR)) + { + dbg("Failed to clear INT1 / INT2 registers\n"); + ret = -EIO; + goto out; + } + + usleep(20000); + + /* Now INT1 should have been latched high and INT2 should be still low */ + + if (priv->config->read_int1_pin() != 1) + { + dbg("INT1 line is LOW - expected HIGH\n"); + ret = -ENXIO; + goto out; + } + + if (priv->config->read_int2_pin) + { + if (priv->config->read_int2_pin() != 0) + { + dbg("INT2 line is HIGH - expected LOW\n"); + ret = -ENODEV; + goto out; + } + + /* Enable interupt 1 on INT2 pin */ + + if (lis2dh_write_register(priv, ST_LIS2DH_CTRL_REG6, 0x40) != OK) + { + dbg("Failed to enable interrupt 1 on INT2 pin"); + ret = -EIO; + goto out; + } + + usleep(20000); + + if (priv->config->read_int2_pin() != 1) + { + dbg("INT2 line is LOW - expected HIGH\n"); + ret = -ENODEV; + goto out; + } + } + +out: + (void)lis2dh_clear_registers(priv); + lis2dh_powerdown(priv); + + return ret; +} + +/**************************************************************************** + * Name: lis2dh_raw_to_mg + * + * Description: + * Convert raw acceleration value to mg + * + * Input Parameters: + * raw_hibyte - Hi byte of raw data + * raw_lobyte - Lo byte of raw data + * + * Returned Value: + * Returns acceleration value in mg + ****************************************************************************/ + +static int16_t lis2dh_raw_convert_to_12bit(uint8_t raw_hibyte, + uint8_t raw_lobyte) +{ + int16_t value; + + value = (raw_hibyte << 8) | raw_lobyte; + value = value >> 4; + + value &= 0xfff; + if (value & 0x800) + { + value = ~value; + value &= 0xfff; + value += 1; + value = -value; + } + + return value; +} + +/**************************************************************************** + * Name: lis2dh_data_available + * + * Description: + * Check if new data is available to read + * + * Input Parameters: + * dev - pointer to LIS2DH Private Structure + * + * Returned Value: + * Return true if new data is available. Otherwise returns false + * + ****************************************************************************/ + +static bool lis2dh_data_available(FAR struct lis2dh_dev_s *dev) +{ + uint8_t retval; + + DEBUGASSERT(dev); + + if (lis2dh_access(dev, ST_LIS2DH_STATUS_REG, &retval, + sizeof(retval)) == sizeof(retval)) + { + return ((retval & ST_LIS2DH_SR_ZYXDA) != 0); + } + return false; +} + +/**************************************************************************** + * Name: lis2dh_get_raw_readings + * + * Description: + * Read X, Y, Z - acceleration values from chip + * + * Input Parameters: + * dev - pointer to LIS2DH Private Structure + * + * Returned Value: + * Returns acceleration vectors (High resolution = 12bit values) on + * success, NULL otherwise. + * + ****************************************************************************/ + +static FAR const struct lis2dh_vector_s * + lis2dh_get_raw_readings(FAR struct lis2dh_dev_s *dev, int *err) +{ + uint8_t retval[6]; + uint8_t retries_left = SELFTEST_MAX_READ_ATTEMPTS; + + DEBUGASSERT(dev); + + *err = 0; + + while (--retries_left > 0) + { + usleep(20000); + if (lis2dh_data_available(dev)) + { + if (lis2dh_access(dev, ST_LIS2DH_OUT_X_L_REG, retval, + sizeof(retval)) == sizeof(retval)) + { + dev->vector_data.x = lis2dh_raw_convert_to_12bit(retval[1], retval[0]); + dev->vector_data.y = lis2dh_raw_convert_to_12bit(retval[3], retval[2]); + dev->vector_data.z = lis2dh_raw_convert_to_12bit(retval[5], retval[4]); + return &dev->vector_data; + } + + return NULL; + } + } + + return NULL; +} +#endif /* CONFIG_LIS2DH_DRIVER_SELFTEST */ + +/**************************************************************************** + * Name: lis2dh_clear_interrupts + * + * Description: + * Clear interrupts from LIS2DH chip + * + ****************************************************************************/ + +static int lis2dh_clear_interrupts(FAR struct lis2dh_dev_s *priv, + uint8_t interrupts) +{ + uint8_t buf; + int ret = OK; + + if (interrupts & LIS2DH_INT1) + { + /* Make sure interrupt will get cleared (by reading this register) in + * case of latched configuration. + */ + + if (lis2dh_access(priv, ST_LIS2DH_INT1_SRC_REG, &buf, 1) != 1) + { + lis2dh_dbg("lis2dh: Failed to read INT1_SRC_REG\n"); + ret = -EIO; + } + } + + if (interrupts & LIS2DH_INT2) + { + /* Make sure interrupt will get cleared (by reading this register) in + * case of latched configuration. + */ + + if (lis2dh_access(priv, ST_LIS2DH_INT2_SRC_REG, &buf, 1) != 1) + { + lis2dh_dbg("lis2dh: Failed to read INT2_SRC_REG\n"); + ret = -EIO; + } + } + + return ret; +} + +/**************************************************************************** + * Name: lis2dh_get_reading + * + * Description: + * Read X, Y, Z - acceleration value from chip + * + * Input Parameters: + * dev - pointer to LIS2DH Private Structure + * force_read - Read even if new data is not available (old data) + * + * Returned Value: + * Returns OK if success, negative error code otherwise + * + ****************************************************************************/ + +static int lis2dh_get_reading(FAR struct lis2dh_dev_s *dev, + FAR struct lis2dh_vector_s *res, + bool force_read) +{ + int scale = dev->scale; + uint8_t retval[7]; + int16_t x; + int16_t y; + int16_t z; + + if (lis2dh_access(dev, ST_LIS2DH_STATUS_REG, retval, 7) == 7) + { + /* If result is not yet ready, return NULL */ + + if (!force_read && !(retval[0] & ST_LIS2DH_SR_ZYXDA)) + { + lis2dh_dbg("lis2dh: Results were not ready\n"); + return -EAGAIN; + } + + /* Add something to entropy pool. */ + + add_sensor_randomness((((uint32_t)retval[6] << 25) | + ((uint32_t)retval[6] >> 7)) ^ + ((uint32_t)retval[5] << 20) ^ + ((uint32_t)retval[4] << 15) ^ + ((uint32_t)retval[3] << 10) ^ + ((uint32_t)retval[2] << 5) ^ + ((uint32_t)retval[1] << 0)); + + x = lis2dh_raw_to_mg(retval[2], retval[1], scale); + y = lis2dh_raw_to_mg(retval[4], retval[3], scale); + z = lis2dh_raw_to_mg(retval[6], retval[5], scale); + + if (dev->setup->xy_axis_fixup) + { + res->x = y; + res->y = -x; + } + else + { + res->x = x; + res->y = y; + } + + res->z = z; + return OK; + } + + return -EIO; +} + +/**************************************************************************** + * Name: lis2dh_get_fifo_readings + * + * Description: + * Bulk read from FIFO + * + ****************************************************************************/ + +static unsigned int lis2dh_get_fifo_readings(FAR struct lis2dh_dev_s *priv, + FAR struct lis2dh_result *res, + unsigned int readcount, + FAR int *perr) +{ + int scale = priv->scale; + union + { + uint8_t raw[6]; + struct lis2dh_vector_s sample; + } *buf = (void *)&res->measurements[res->header.meas_count]; + bool xy_axis_fixup = priv->setup->xy_axis_fixup; + size_t buflen = readcount * 6; + int16_t x; + int16_t y; + int16_t z; + unsigned int i; + + if (readcount == 0) + { + return 0; + } + + if (lis2dh_access(priv, ST_LIS2DH_OUT_X_L_REG, (void *)buf, buflen) != buflen) + { + lis2dh_dbg("lis2dh: Failed to read FIFO (%d bytes, %d samples)\n", + buflen, readcount); + *perr = -EIO; + return 0; + } + + /* Add something to entropy pool. */ + + up_rngaddentropy(RND_SRC_SENSOR, (void *)buf, buflen / 4); + + /* Convert raw values to mG */ + + for (i = 0; i < readcount; i++) + { + x = lis2dh_raw_to_mg(buf[i].raw[1], buf[i].raw[0], scale); + y = lis2dh_raw_to_mg(buf[i].raw[3], buf[i].raw[2], scale); + z = lis2dh_raw_to_mg(buf[i].raw[5], buf[i].raw[4], scale); + + if (xy_axis_fixup) + { + buf[i].sample.x = y; + buf[i].sample.y = -x; + } + else + { + buf[i].sample.x = x; + buf[i].sample.y = y; + } + + buf[i].sample.z = z; + } + + return readcount; +} + +/**************************************************************************** + * Name: lis2dh_raw_to_mg + * + * Description: + * Convert raw acceleration value to mg + * + * Input Parameters: + * raw_hibyte - Hi byte of raw data + * raw_lobyte - Lo byte of raw data + * scale - full scale in milliG + * + * Returned Value: + * Returns acceleration value in mg + * + ****************************************************************************/ + +static inline int16_t lis2dh_raw_to_mg(uint8_t raw_hibyte, uint8_t raw_lobyte, + int scale) +{ + int16_t value; + + /* Value is signed integer, range INT16_MIN..INT16_MAX. */ + + value = (raw_hibyte << 8) | raw_lobyte; + + /* Scale to mg, INT16_MIN..INT16_MAX => -scale..scale */ + + return (int32_t)value * scale / INT16_MAX; +} + +/**************************************************************************** + * Name: lis2dh_read_temp + * + * Description: + * + * Input Parameters: + * + * Returned Value: + * + ****************************************************************************/ + +static int lis2dh_read_temp(FAR struct lis2dh_dev_s *dev, FAR int16_t *temper) +{ + int ret; + uint8_t buf[2] = { 0 }; + + ret = lis2dh_access(dev, ST_LIS2DH_OUT_TEMP_L_REG, buf, 2); + if (ret < 0) + { + lis2dh_dbg("Cannot read temperature\n"); + return -EIO; + } + + *temper = buf[0] | ((int16_t)buf[1] << 8); + + return OK; +} + +/**************************************************************************** + * LIS2DH Access with range check + * + * Description: + * Read or write data via I2C + * + * Input Parameters: + * dev LIS2DH Private Structure + * subaddr LIS2DH Sub Address + * buf Pointer to buffer, either for read or write access + * length When >0 it denotes read access, when <0 it denotes write access + * of -length + * + * Returned Value: + * Returns actual length of data on success or negated errno. + * + ****************************************************************************/ + +static int lis2dh_access(FAR struct lis2dh_dev_s *dev, uint8_t subaddr, + FAR uint8_t *buf, int length) +{ + uint16_t flags = 0; + int retval; + int retries; + + DEBUGASSERT(dev != NULL && buf != NULL && length != 0); + + if (length > 0) + { + flags = I2C_M_READ; + } + else + { + flags = I2C_M_NORESTART; + length = -length; + } + + /* Check valid address ranges and set auto address increment flag */ + + if (subaddr == ST_LIS2DH_STATUS_AUX_REG) + { + if (length > 1) + { + length = 1; + } + } + else if (subaddr >= ST_LIS2DH_OUT_TEMP_L_REG && subaddr < 0x10) + { + if (length > (0x10 - subaddr)) + { + length = 0x10 - subaddr; + } + } + + else if (subaddr >= ST_LIS2DH_TEMP_CFG_REG && subaddr <= ST_LIS2DH_ACT_DUR_REG) + { + if (subaddr == ST_LIS2DH_OUT_X_L_REG) + { + /* FIFO bulk read, length maximum 6*32 = 192 bytes. */ + if (length > 6 * 32) + { + length = 6 * 32; + } + } + else + { + if (length > (ST_LIS2DH_ACT_DUR_REG + 1 - subaddr)) + { + length = ST_LIS2DH_ACT_DUR_REG + 1 - subaddr; + } + } + } + else + { + return -EFAULT; + } + + if (length > 1) + { + subaddr |= 0x80; + } + + for (retries = 0; retries < LIS2DH_I2C_RETRIES; retries++) + { + /* Create message and send */ + + struct i2c_msg_s msgv[2] = + { + { + .addr = dev->addr, + .flags = 0, + .buffer = &subaddr, + .length = 1 + }, + { + .addr = dev->addr, + .flags = flags, + .buffer = buf, + .length = length + } + }; + + retval = I2C_TRANSFER(dev->i2c, msgv, 2); + if (retval == OK) + { + return length; + } + else + { + /* Some error. Try to reset I2C bus and keep trying. */ +#ifdef CONFIG_I2C_RESET + int ret = up_i2creset(dev->i2c); + if (ret < 0) + { + lis2dh_dbg("up_i2creset failed: %d\n", ret); + return ret; + } +#endif + continue; + } + } + + lis2dh_dbg("failed, error: %d\n", retval); + return retval; +} + +/**************************************************************************** + * Name: lis2dh_reboot + * + * Description: + * + * Input Parameters: + * + * Returned Value: + * + ****************************************************************************/ + +static int lis2dh_reboot(FAR struct lis2dh_dev_s *dev) +{ + struct timespec start, curr; + int32_t diff_msec; + uint8_t value; + + (void)clock_gettime(CLOCK_MONOTONIC, &start); + + /* Reboot to reset chip. */ + + value = ST_LIS2DH_CR5_BOOT; + if (lis2dh_access(dev, ST_LIS2DH_CTRL_REG5, &value, -1) != 1) + { + return -EIO; + } + + /* Reboot is completed when reboot bit is cleared. */ + + do + { + value = 0; + if (lis2dh_access(dev, ST_LIS2DH_CTRL_REG5, &value, 1) != 1) + { + return -EIO; + } + + if (!(value & ST_LIS2DH_CR5_BOOT)) + { + break; + } + + (void)clock_gettime(CLOCK_MONOTONIC, &curr); + + diff_msec = (curr.tv_sec - start.tv_sec) * 1000; + diff_msec += (curr.tv_nsec - start.tv_nsec) / (1000 * 1000); + + if (diff_msec > 100) + { + return -ETIMEDOUT; + } + + usleep(1); + } + while (true); + + /* Reboot completed, chip is now in power-down state. */ + + return OK; +} + +/**************************************************************************** + * Name: lis2dh_powerdown + * + * Description: + * + * Input Parameters: + * + * Returned Value: + * + ****************************************************************************/ + +static int lis2dh_powerdown(FAR struct lis2dh_dev_s * dev) +{ + uint8_t buf = 0; + int ret = OK; + + if (lis2dh_access(dev, ST_LIS2DH_CTRL_REG1, &buf, -1) != 1) + { + lis2dh_dbg("Failed to clear CTRL_REG1\n"); + ret = -EIO; + } + + return ret; +} + +/**************************************************************************** + * LIS2DH Setup + * + * Description: + * Apply new register setup + * + * Input Parameters: + * dev - pointer to LIS2DH Private Structure + * new_setup - pointer to new setup data to be configured + * + * Returned Value: + * Returns OK on success, ERROR otherwise. + * + ****************************************************************************/ + +static int lis2dh_setup(FAR struct lis2dh_dev_s * dev, + FAR struct lis2dh_setup *new_setup) +{ + uint8_t value; + + dev->setup = new_setup; + + /* Clear old configuration. On first boot after power-loss, reboot bit does + * not get cleared, and lis2dh_reboot() times out. Anyway, chip accepts + * new configuration and functions correctly. */ + + (void)lis2dh_reboot(dev); + + /* TEMP_CFG_REG */ + + value = dev->setup->temp_enable ? (0x3 << 6): 0; + if (lis2dh_access(dev, ST_LIS2DH_TEMP_CFG_REG, &value, -1) != 1) + { + goto error; + } + + /* CTRL_REG2 */ + + value = dev->setup->hpmode | dev->setup->hpcf | dev->setup->fds | + dev->setup->hpclick | dev->setup->hpis2 | dev->setup->hpis1; + if (lis2dh_access(dev, ST_LIS2DH_CTRL_REG2, &value, -1) != 1) + { + goto error; + } + + /* CTRL_REG3 */ + + value = dev->setup->int1_click_enable | dev->setup->int1_aoi_enable | + dev->setup->int2_aoi_enable | dev->setup->int1_drdy_enable | + dev->setup->int2_drdy_enable | dev->setup->int_wtm_enable | + dev->setup->int_overrun_enable; + if (lis2dh_access(dev, ST_LIS2DH_CTRL_REG3, &value, -1) != 1) + { + goto error; + } + + /* CTRL_REG4 */ + + value = dev->setup->bdu | dev->setup->endian | dev->setup->fullscale | + dev->setup->high_resolution_enable | dev->setup->selftest | + dev->setup->spi_mode; + if (lis2dh_access(dev, ST_LIS2DH_CTRL_REG4, &value, -1) != 1) + { + goto error; + } + + /* CTRL_REG5 */ + + value = dev->setup->reboot | dev->setup->fifo_enable | dev->setup->int1_latch | + dev->setup->int1_4d_enable | dev->setup->int2_latch | + dev->setup->int2_4d_enable; + if (lis2dh_access(dev, ST_LIS2DH_CTRL_REG5, &value, -1) != 1) + { + goto error; + } + + /* CTRL_REG6 */ + + value = dev->setup->int2_click_enable | dev->setup->int_enable | + dev->setup->boot_int1_enable | dev->setup->high_low_active; + if (lis2dh_access(dev, ST_LIS2DH_CTRL_REG6, &value, -1) != 1) + { + goto error; + } + + /* REFERENCE */ + + value = dev->setup->reference; + if (lis2dh_access(dev, ST_LIS2DH_REFERENCE_REG, &value, -1) != 1) + { + goto error; + } + + /* FIFO_CTRL_REG */ + + value = dev->setup->fifo_mode | dev->setup->trigger_selection | + dev->setup->fifo_trigger_threshold; + if (lis2dh_access(dev, ST_LIS2DH_FIFO_CTRL_REG, &value, -1) != 1) + { + goto error; + } + + /* INT1_CFG */ + + value = dev->setup->int1_interrupt_mode | dev->setup->int1_enable_6d | dev->setup->int1_int_z_high_enable | + dev->setup->int1_int_z_low_enable | dev->setup->int1_int_y_high_enable | + dev->setup->int1_int_y_low_enable | dev->setup->int1_int_x_high_enable | + dev->setup->int1_int_x_low_enable; + if (lis2dh_access(dev, ST_LIS2DH_INT1_CFG_REG, &value, -1) != 1) + { + goto error; + } + + /* INT1_THS */ + + value = dev->setup->int1_int_threshold; + if (lis2dh_access(dev, ST_LIS2DH_INT1_THS_REG, &value, -1) != 1) + { + goto error; + } + + /* INT1_DURATION */ + + value = dev->setup->int1_int_duration; + if (lis2dh_access(dev, ST_LIS2DH_INT1_DUR_REG, &value, -1) != 1) + { + goto error; + } + + /* INT2_CFG */ + + value = dev->setup->int2_interrupt_mode | dev->setup->int2_enable_6d | dev->setup->int2_int_z_high_enable | + dev->setup->int2_int_z_low_enable | dev->setup->int2_int_y_high_enable | + dev->setup->int2_int_y_low_enable | dev->setup->int2_int_x_high_enable | + dev->setup->int2_int_x_low_enable; + if (lis2dh_access(dev, ST_LIS2DH_INT2_CFG_REG, &value, -1) != 1) + { + goto error; + } + + /* INT2_THS */ + + value = dev->setup->int2_int_threshold; + if (lis2dh_access(dev, ST_LIS2DH_INT2_THS_REG, &value, -1) != 1) + { + goto error; + } + + /* INT2_DURATION */ + + value = dev->setup->int2_int_duration; + if (lis2dh_access(dev, ST_LIS2DH_INT2_DUR_REG, &value, -1) != 1) + { + goto error; + } + + /* CLICK_CFG */ + + value = dev->setup->z_double_click_enable | dev->setup->z_single_click_enable | + dev->setup->y_double_click_enable | dev->setup->y_single_click_enable | + dev->setup->x_double_click_enable | dev->setup->x_single_click_enable; + if (lis2dh_access(dev, ST_LIS2DH_CLICK_CFG_REG, &value, -1) != 1) + { + goto error; + } + + /* CLICK_THS */ + + value = dev->setup->click_threshold; + if (lis2dh_access(dev, ST_LIS2DH_CLICK_THS_REG, &value, -1) != 1) + { + goto error; + } + + /* TIME_LIMIT */ + + value = dev->setup->click_time_limit; + if (lis2dh_access(dev, ST_LIS2DH_TIME_LIMIT_REG, &value, -1) != 1) + { + goto error; + } + + /* TIME_LATENCY */ + + value = dev->setup->click_time_latency; + if (lis2dh_access(dev, ST_LIS2DH_TIME_LATENCY_REG, &value, -1) != 1) + { + goto error; + } + + /* TIME_WINDOW */ + + value = dev->setup->click_time_window; + if (lis2dh_access(dev, ST_LIS2DH_TIME_WINDOW_REG, &value, -1) != 1) + { + goto error; + } + + /* CTRL_REG1 */ + + value = dev->setup->data_rate | dev->setup->low_power_mode_enable | + dev->setup->zen | dev->setup->yen | dev->setup->xen; + if (lis2dh_access(dev, ST_LIS2DH_CTRL_REG1, &value, -1) != 1) + { + goto error; + } + + switch (dev->setup->fullscale & 0x30) + { + default: + case ST_LIS2DH_CR4_FULL_SCALE_2G: + dev->scale = 2000; + break; + + case ST_LIS2DH_CR4_FULL_SCALE_4G: + dev->scale = 4000; + break; + + case ST_LIS2DH_CR4_FULL_SCALE_8G: + dev->scale = 8000; + break; + + case ST_LIS2DH_CR4_FULL_SCALE_16G: + dev->scale = 16000; + break; + } + + if (dev->setup->fifo_enable) + { + dev->fifo_used = true; + + if (lis2dh_fifo_start(dev) < 0) + { + goto error; + } + } + else + { + dev->fifo_used = false; + } + + return OK; + +error: + + /* Setup failed - power down */ + + lis2dh_powerdown(dev); + return -EIO; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: lis2dh_register + * + * Description: + * Register the LIS2DH character device as 'devpath' + * + * Input Parameters: + * devpath - The full path to the driver to register. E.g., "/dev/acc0" + * i2c - An instance of the I2C interface to use to communicate with LIS2DH + * addr - The I2C address of the LIS2DH. The base I2C address of the LIS2DH + * is 0x18. Bit 0 can be controlled via SA0 pad - when connected to + * voltage supply the address is 0x19. + * config - Pointer to LIS2DH configuration + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure. + * + ****************************************************************************/ + +int lis2dh_register(FAR const char *devpath, FAR struct i2c_master_s *i2c, + uint8_t addr, FAR struct lis2dh_config_s *config) +{ + FAR struct lis2dh_dev_s *priv; + int ret; + + DEBUGASSERT(devpath != NULL && i2c != NULL && config != NULL); + + priv = (FAR struct lis2dh_dev_s *)kmm_zalloc(sizeof(struct lis2dh_dev_s)); + if (!priv) + { + lis2dh_dbg("lis2dh: Failed to allocate instance\n"); + return -ENOMEM; + } + + sem_init(&priv->devsem, 0, 1); + + priv->fifo_used = false; +#ifdef LIS2DH_COUNT_INTS + priv->int_pending = 0; +#else + priv->int_pending = false; +#endif + + priv->i2c = i2c; + priv->addr = addr; + priv->config = config; + + ret = register_driver(devpath, &g_lis2dhops, 0666, priv); + if (ret < 0) + { + lis2dh_dbg("lis2dh: Failed to register driver: %d\n", ret); + goto errout_with_priv; + } + + if (priv->config->irq_clear) + { + priv->config->irq_clear(config); + } + priv->config->irq_attach(config, lis2dh_int_handler, priv); + priv->config->irq_enable(config, false); + return OK; + +errout_with_priv: + sem_destroy(&priv->devsem); + kmm_free(priv); + + return ret; +} diff --git a/include/nuttx/sensors/ioctl.h b/include/nuttx/sensors/ioctl.h index 3cef5a8997..18b414773f 100644 --- a/include/nuttx/sensors/ioctl.h +++ b/include/nuttx/sensors/ioctl.h @@ -133,4 +133,14 @@ #define SNIOC_PRESSURE_OUT _SNIOC(0x0034) #define SNIOC_SENSOR_OFF _SNIOC(0x0035) +/* IOCTL commands unique to the LIS2DH */ + +#define SNIOC_WRITESETUP _SNIOC(0x0036) /* Arg: uint8_t value */ +#define SNIOC_WRITE_INT1THRESHOLD _SNIOC(0x0037) /* Arg: uint8_t value */ +#define SNIOC_WRITE_INT2THRESHOLD _SNIOC(0x0038) /* Arg: uint8_t value */ +#define SNIOC_RESET_HPFILTER _SNIOC(0x0039) /* Arg: uint8_t value */ +#define SNIOC_START_SELFTEST _SNIOC(0x003a) /* Arg: uint8_t value */ +#define SNIOC_WHO_AM_I _SNIOC(0x003b) +#define SNIOC_READ_TEMP _SNIOC(0x003c) /* Arg: int16_t value */ + #endif /* __INCLUDE_NUTTX_SENSORS_IOCTL_H */ diff --git a/include/nuttx/sensors/lis2dh.h b/include/nuttx/sensors/lis2dh.h new file mode 100644 index 0000000000..f9fdd839ca --- /dev/null +++ b/include/nuttx/sensors/lis2dh.h @@ -0,0 +1,441 @@ +/**************************************************************************** + * include/nuttx/sensors/lis2dh.h + * LIS2DH accelerometer driver + * + * Copyright (C) 2014-2017 Haltian Ltd. All rights reserved. + * Authors: Timo Voutilainen + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +#ifndef __INCLUDE_NUTTX_SENSORS_LIS2DH_H +#define __INCLUDE_NUTTX_SENSORS_LIS2DH_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +/**************************************************************************** + * Pre-Processor Declarations + ****************************************************************************/ + +#undef EXTERN +#if defined(__cplusplus) +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +#define ST_LIS2DH_WHOAMI_VALUE 0x33 /* Valid WHOAMI register value */ + +/* LIS2DH Internal Registers **********************************************/ + +#define ST_LIS2DH_WHOAMI_REG 0x0f /* WHOAMI register */ + +#define ST_LIS2DH_STATUS_AUX_REG 0x07 /* Temperature status */ +#define ST_LIS2DH_OUT_TEMP_L_REG 0x0c /* Temperature data */ +#define ST_LIS2DH_OUT_TEMP_H_REG 0x0d /* Temperature data */ + +#define ST_LIS2DH_TEMP_CFG_REG 0x1f + +#define ST_LIS2DH_CTRL_REG1 0x20 + +/* CR1 ODR 4 MSBs (XXXX---) */ + +#define ST_LIS2DH_CR1_ODR_PWR_DWN 0x00 +#define ST_LIS2DH_CR1_ODR_1HZ 0x10 /* HR / Normal / Low Power */ +#define ST_LIS2DH_CR1_ODR_10HZ 0x20 /* HR / Normal / Low Power */ +#define ST_LIS2DH_CR1_ODR_25HZ 0x30 /* HR / Normal / Low Power */ +#define ST_LIS2DH_CR1_ODR_50HZ 0x40 /* HR / Normal / Low Power */ +#define ST_LIS2DH_CR1_ODR_100HZ 0x50 /* HR / Normal / Low Power */ +#define ST_LIS2DH_CR1_ODR_200HZ 0x60 /* HR / Normal / Low Power */ +#define ST_LIS2DH_CR1_ODR_400HZ 0x70 /* HR / Normal / Low Power */ +#define ST_LIS2DH_CR1_ODR_1620HZ 0x80 /* Low Power */ +#define ST_LIS2DH_CR1_ODR_1344_5376HZ 0x90 /* HR / Normal: 1344Hz, Low power: 5376Hz*/ +#define ST_LIS2DH_CR1_LOWP_ENABLE 0x08 /* Low power mode enable */ +#define ST_LIS2DH_CR1_ZEN 0x04 /* Z-Axis Enable */ +#define ST_LIS2DH_CR1_YEN 0x02 /* Y-Axis Enable */ +#define ST_LIS2DH_CR1_XEN 0x01 /* X-Axis Enable */ + +#define ST_LIS2DH_CTRL_REG2 0x21 + +/* HPM1 .... HP FILT_MODE (XX------) */ + +#define ST_LIS2DH_CR2_HPFILT_M_NORM 0x00 /* Normal mode (reset reading REFERENCE/DATACAPTURE (26h) register) */ +#define ST_LIS2DH_CR2_HPFILT_M_REFSIG 0x40 /* Reference signal for filtering */ +#define ST_LIS2DH_CR2_HPFILT_M_NORM2 0x80 /* Normal mode */ +#define ST_LIS2DH_CR2_HPFILT_M_AUTOR 0xc0 /* Autoreset on interrupt event */ +#define ST_LIS2DH_CR2_FDS 0x08 + +/* HPIS1 HPFILT ENABLE for INT1 (-------X) */ + +#define ST_LIS2DH_CR2_HPENABLED_INT1 0x01 /* HP filter enabled for INT1 */ + +/* HPIS2 HPFILT ENABLE for INT2 (------X-) */ + +#define ST_LIS2DH_CR2_HPENABLED_INT2 0x02 /* HP filter enabled for INT2 */ + +#define ST_LIS2DH_CTRL_REG3 0x22 + +/* I1_AOI1 ENABLE for INT2 (-X------) */ + +#define ST_LIS2DH_CR3_I1_AOI1_ENABLED 0x40 /* AOI1 interrupt on INT1 pin.*/ +#define ST_LIS2DH_CR3_I1_AOI2_ENABLED 0x20 /* AOI2 interrupt on INT1 pin.*/ + +#define ST_LIS2DH_CTRL_REG4 0x23 + +/* BDU ... Block Data Update (X-------) */ + +#define ST_LIS2DH_CR4_BDU_CONT 0x00 /* Continuous update (Default) */ +#define ST_LIS2DH_CR4_BDU_UPD_ON_READ 0x80 /* Output registers not updated until MSB and LSB have been read */ +#define ST_LIS2DH_CR4_FULL_SCALE_2G 0x0 +#define ST_LIS2DH_CR4_FULL_SCALE_4G 0x10 +#define ST_LIS2DH_CR4_FULL_SCALE_8G 0x20 +#define ST_LIS2DH_CR4_FULL_SCALE_16G 0x30 + +/* HR .. Operation mode selector (----X---) */ + +#define ST_LIS2DH_CR4_HR_ENABLED 0x08 /* See section 2.6.3 in datasheet */ + +#define ST_LIS2DH_CTRL_REG5 0x24 +#define ST_LIS2DH_CR5_BOOT 0x80 +#define ST_LIS2DH_CR5_FIFO_EN 0x40 +#define ST_LIS2DH_CR5_LIR_INT1 0x08 +#define ST_LIS2DH_CR5_D4D_INT1 0x04 +#define ST_LIS2DH_CR5_LIR_INT2 0x02 +#define ST_LIS2DH_CR5_D4D_INT2 0x01 + +#define ST_LIS2DH_CTRL_REG6 0x25 +#define ST_LIS2DH_REFERENCE_REG 0x26 + +#define ST_LIS2DH_STATUS_REG 0x27 /* Status Register */ +#define ST_LIS2DH_SR_ZYXOR 0x80 /* OR'ed X,Y and Z data over-run */ +#define ST_LIS2DH_SR_ZOR 0x40 /* individual data over-run ... */ +#define ST_LIS2DH_SR_YOR 0x20 +#define ST_LIS2DH_SR_XOR 0x10 +#define ST_LIS2DH_SR_ZYXDA 0x08 /* OR'ed X,Y and Z data available */ +#define ST_LIS2DH_SR_ZDA 0x04 /* individual data available ... */ +#define ST_LIS2DH_SR_YDA 0x02 +#define ST_LIS2DH_SR_XDA 0x01 + +#define ST_LIS2DH_OUT_X_L_REG 0x28 +#define ST_LIS2DH_OUT_X_H_REG 0x29 +#define ST_LIS2DH_OUT_Y_L_REG 0x2a +#define ST_LIS2DH_OUT_Y_H_REG 0x2b +#define ST_LIS2DH_OUT_Z_L_REG 0x2c +#define ST_LIS2DH_OUT_Z_H_REG 0x2d + +#define ST_LIS2DH_FIFO_CTRL_REG 0x2e +#define ST_LIS2DH_FIFOCR_THRESHOLD_MASK 0x1f +#define ST_LIS2DH_FIFOCR_THRESHOLD(x) ((x) & ST_LIS2DH_FIFOCR_THRESHOLD_MASK) +#define ST_LIS2DH_FIFOCR_INT1 0x00 +#define ST_LIS2DH_FIFOCR_INT2 0x20 +#define ST_LIS2DH_FIFOCR_MODE_MASK 0xc0 + +#define ST_LIS2DH_FIFO_SRC_REG 0x2f +#define ST_LIS2DH_FIFOSR_NUM_SAMP_MASK 0x1f +#define ST_LIS2DH_FIFOSR_EMPTY 0x20 +#define ST_LIS2DH_FIFOSR_OVRN_FIFO 0x40 +#define ST_LIS2DH_FIFOSR_WTM 0x80 + +#define ST_LIS2DH_INT1_CFG_REG 0x30 +#define ST_LIS2DH_INT_CFG_AOI 0x80 +#define ST_LIS2DH_INT_CFG_6D 0x40 +#define ST_LIS2DH_INT_CFG_ZHIE 0x20 +#define ST_LIS2DH_INT_CFG_ZLIE 0x10 +#define ST_LIS2DH_INT_CFG_YHIE 0x08 +#define ST_LIS2DH_INT_CFG_YLIE 0x04 +#define ST_LIS2DH_INT_CFG_XHIE 0x02 +#define ST_LIS2DH_INT_CFG_XLIE 0x01 + +#define ST_LIS2DH_INT1_SRC_REG 0x31 +#define ST_LIS2DH_INT_SR_XLOW 0x01 +#define ST_LIS2DH_INT_SR_XHIGH 0x02 +#define ST_LIS2DH_INT_SR_YLOW 0x04 +#define ST_LIS2DH_INT_SR_YHIGH 0x08 +#define ST_LIS2DH_INT_SR_ZLOW 0x10 +#define ST_LIS2DH_INT_SR_ZHIGH 0x20 +#define ST_LIS2DH_INT_SR_ACTIVE 0x40 + +#define ST_LIS2DH_INT1_THS_REG 0x32 /* 7-bit value for threshold */ + +#define ST_LIS2DH_INT1_DUR_REG 0x33 /* 7-bit value for duration */ + +#define ST_LIS2DH_INT2_CFG_REG 0x34 + +#define ST_LIS2DH_INT2_SRC_REG 0x35 + +#define ST_LIS2DH_INT2_THS_REG 0x36 /* 7-bit value for threshold */ + +#define ST_LIS2DH_INT2_DUR_REG 0x37 /* 7-bit value for duration */ + +#define ST_LIS2DH_CLICK_CFG_REG 0x38 + +#define ST_LIS2DH_CLICK_SRC_REG 0x39 + +#define ST_LIS2DH_CLICK_THS_REG 0x3a + +#define ST_LIS2DH_TIME_LIMIT_REG 0x3b + +#define ST_LIS2DH_TIME_LATENCY_REG 0x3c + +#define ST_LIS2DH_TIME_WINDOW_REG 0x3d + +#define ST_LIS2DH_ACT_DUR_REG 0x3f + +/************************************************************************************ + * Public Types + ************************************************************************************/ + +enum lis2dh_ouput_data_rate +{ + LIS2DH_ODR_POWER_DOWN = 0x00, + LIS2DH_ODR_1HZ = 0x10, + LIS2DH_ODR_10HZ = 0x20, + LIS2DH_ODR_25HZ = 0x30, + LIS2DH_ODR_50HZ = 0x40, + LIS2DH_ODR_100HZ = 0x50, + LIS2DH_ODR_200HZ = 0x60, + LIS2DH_ODR_400HZ = 0x70, + LIS2DH_ODR_1620HZ = 0x80, + LIS2DH_ODR_5376HZ = 0x90, +}; + +enum lis2dh_high_pass_filter_mode +{ + LIS2DH_REFERENCE_SIGNAL = 0x40, + LIS2DH_NORMAL_MODE = 0x80, + LIS2DH_AUTORESET_ON_INTERRUPT = 0xc0, +}; + +enum lis2dh_scale_range +{ + LIS2DH_RANGE_2G = 0x00, + LIS2DH_RANGE_4G = 0x10, + LIS2DH_RANGE_8G = 0x20, + LIS2DH_RANGE_16G = 0x30, +}; + +enum lis2dh_self_test +{ + LIS2DH_NORMAL = 0x00, + LIS2DH_SELF_TEST0 = 0x02, + LIS2DH_SELF_TEST1 = 0x04, +}; + +enum lis2dh_fifo_mode +{ + LIS2DH_BYPASS_MODE = 0x00, + LIS2DH_FIFO_MODE = 0x40, + LIS2DH_STREAM_MODE = 0x80, + LIS2DH_TRIGGER_MODE = 0xc0, +}; + +enum lis2dh_interrupt_mode +{ + LIS2DH_OR_COMBINATION = 0x00, + LIS2DH_6D_MOVEMENT = 0x40, + LIS2DH_AND_COMBINATION = 0x80, + LIS2DH_6D_POSITION = 0xc0, +}; + +struct lis2dh_vector_s +{ + int16_t x, y, z; +} packed_struct; + +struct lis2dh_res_header +{ + uint8_t meas_count; + bool int1_occurred; + uint8_t int1_source; + bool int2_occurred; + uint8_t int2_source; +} packed_struct; + +struct lis2dh_result +{ + struct lis2dh_res_header header; + struct lis2dh_vector_s measurements[0]; +} packed_struct; + +struct lis2dh_setup +{ + bool temp_enable:1; + bool xy_axis_fixup:1; + + uint8_t data_rate; + uint8_t low_power_mode_enable; + uint8_t zen; + uint8_t yen; + uint8_t xen; + + uint8_t hpmode; + uint8_t hpcf; + uint8_t fds; + uint8_t hpclick; + uint8_t hpis2; + uint8_t hpis1; + + uint8_t int1_click_enable; + uint8_t int1_aoi_enable; + uint8_t int2_aoi_enable; + uint8_t int1_drdy_enable; + uint8_t int2_drdy_enable; + uint8_t int_wtm_enable; + uint8_t int_overrun_enable; + + uint8_t bdu; + uint8_t endian; + uint8_t fullscale; + uint8_t high_resolution_enable; + uint8_t selftest; + uint8_t spi_mode; + + uint8_t reboot; + uint8_t fifo_enable; + uint8_t int1_latch; + uint8_t int1_4d_enable; + uint8_t int2_latch; + uint8_t int2_4d_enable; + + uint8_t int2_click_enable; + uint8_t int_enable; + uint8_t boot_int1_enable; + uint8_t high_low_active; + + uint8_t reference; + + uint8_t fifo_mode; + uint8_t trigger_selection; + uint8_t fifo_trigger_threshold; + + uint8_t int1_interrupt_mode; + uint8_t int1_enable_6d; + uint8_t int1_int_z_high_enable; + uint8_t int1_int_z_low_enable; + uint8_t int1_int_y_high_enable; + uint8_t int1_int_y_low_enable; + uint8_t int1_int_x_high_enable; + uint8_t int1_int_x_low_enable; + uint8_t int1_int_threshold; + uint8_t int1_int_duration; + + uint8_t int2_interrupt_mode; + uint8_t int2_enable_6d; + uint8_t int2_int_z_high_enable; + uint8_t int2_int_z_low_enable; + uint8_t int2_int_y_high_enable; + uint8_t int2_int_y_low_enable; + uint8_t int2_int_x_high_enable; + uint8_t int2_int_x_low_enable; + uint8_t int2_int_threshold; + uint8_t int2_int_duration; + + uint8_t z_double_click_enable; + uint8_t z_single_click_enable; + uint8_t y_double_click_enable; + uint8_t y_single_click_enable; + uint8_t x_double_click_enable; + uint8_t x_single_click_enable; + + uint8_t click_threshold; + uint8_t click_time_limit; + uint8_t click_time_latency; + uint8_t click_time_window; +}; + +struct lis2dh_config_s +{ + /* Device characterization */ + + int irq; /* IRQ number received by interrupt handler. */ + + /* IRQ/GPIO access callbacks. These operations all hidden behind + * callbacks to isolate the lis2dh driver from differences in GPIO + * interrupt handling by varying boards and MCUs. + * + * irq_attach - Attach the lis2dh interrupt handler to the GPIO interrupt + * irq_enable - Enable or disable the GPIO interrupt + * clear_irq - Acknowledge/clear any pending GPIO interrupt + * + */ + + CODE int (*irq_attach)(FAR struct lis2dh_config_s *state, xcpt_t isr, FAR void *arg); + CODE void (*irq_enable)(FAR const struct lis2dh_config_s *state, bool enable); + CODE void (*irq_clear)(FAR const struct lis2dh_config_s *state); + CODE bool (*read_int1_pin)(void); + CODE bool (*read_int2_pin)(void); +}; + +struct lis2dh_raw_data_s +{ + uint16_t out_x; + uint16_t out_y; + uint16_t out_z; +} packed_struct; + +typedef struct lis2dh_raw_data_s lis2dh_raw_data_t; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: lis2dh_register + * + * Description: + * Register the LIS2DH character device as 'devpath' + * + * Input Parameters: + * devpath - The full path to the driver to register. E.g., "/dev/acc0" + * i2c - An instance of the I2C interface to use to communicate with LIS2DH + * addr - The I2C address of the LIS2DH. The base I2C address of the LIS2DH + * is 0x18. Bit 0 can be controlled via SA0 pad - when connected to + * voltage supply the address is 0x19. + * config - Pointer to LIS2DH configuration + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure. + * + ****************************************************************************/ + +int lis2dh_register(FAR const char *devpath, FAR struct i2c_master_s *i2c, + uint8_t addr, FAR struct lis2dh_config_s *config); + +#undef EXTERN +#if defined(__cplusplus) +} +#endif + +#endif /* __INCLUDE_NUTTX_SENSORS_LIS2DH_H */