/**************************************************************************** * drivers/sensors/lsm330.c * Character driver for the ST LSM330 Tri-axis accelerometer and gyroscope. * * Copyright (C) 2017-2018 RAF Research LLC. All rights reserved. * Author: Bob Feretich * * 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 #if defined(CONFIG_SPI) && defined(CONFIG_SENSORS_LSM330SPI) \ && defined(CONFIG_SPI_EXCHANGE) #include #include #include #include #include #include #include /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ #define LSM330_INITIAL_ACLCR_SIZE 7 #define LSM330_INITIAL_GYROCR_SIZE 5 /**************************************************************************** * Private structure definitions ****************************************************************************/ struct sensor_data_s { int16_t x_gyr; /* Measurement result for x axis */ int16_t y_gyr; /* Measurement result for y axis */ int16_t z_gyr; /* Measurement result for z axis */ }; struct lsm330_dev_s { FAR struct lsm330_dev_s *flink; /* Supports a singly linked list of * drivers */ FAR struct spi_dev_s *spi; /* Pointer to the SPI instance */ FAR struct lsm330_config_s *config; /* Pointer to the configuration of the * LSM330 sensor */ sem_t devicesem; /* Manages exclusive access to this * device */ sem_t datasem; /* Manages exclusive access to this * structure */ struct sensor_data_s data; /* The data as measured by the sensor */ uint8_t seek_address; /* Current device address. */ uint8_t readonly; /* 0 = writing to the device in enabled */ }; /**************************************************************************** * Private Function Prototypes ****************************************************************************/ static uint8_t lsm330_read_register(FAR struct lsm330_dev_s *dev, uint8_t reg_addr); static void lsm330_read_acl_registerblk(FAR struct lsm330_dev_s *dev, uint8_t reg_addr, FAR uint8_t *reg_data, uint8_t xfercnt); static void lsm330_read_gyro_registerblk(FAR struct lsm330_dev_s *dev, uint8_t reg_addr, FAR uint8_t *reg_data, uint8_t xfercnt); static void lsm330_write_register(FAR struct lsm330_dev_s *dev, uint8_t reg_addr, uint8_t reg_data); static void lsm330_write_acl_registerblk(FAR struct lsm330_dev_s *dev, uint8_t reg_addr, FAR uint8_t *reg_data, uint8_t xfercnt); static void lsm330_write_gyro_registerblk(FAR struct lsm330_dev_s *dev, uint8_t reg_addr, FAR uint8_t *reg_data, uint8_t xfercnt); static void lsm330acl_reset(FAR struct lsm330_dev_s *dev); static void lsm330gyro_reset(FAR struct lsm330_dev_s *dev); static int lsm330acl_open(FAR struct file *filep); static int lsm330gyro_open(FAR struct file *filep); static int lsm330acl_close(FAR struct file *filep); static int lsm330gyro_close(FAR struct file *filep); static ssize_t lsm330acl_read(FAR struct file *, FAR char *, size_t); static ssize_t lsm330gyro_read(FAR struct file *, FAR char *, size_t); static ssize_t lsm330acl_write(FAR struct file *filep, FAR const char *buffer, size_t buflen); static ssize_t lsm330gyro_write(FAR struct file *filep, FAR const char *buffer, size_t buflen); static off_t lsm330acl_seek(FAR struct file *filep, off_t offset, int whence); static off_t lsm330gyro_seek(FAR struct file *filep, off_t offset, int whence); static int lsm330_ioctl(FAR struct file *filep, int cmd, unsigned long arg); static int lsm330acl_dvr_open(FAR void *instance_handle, int32_t arg); static int lsm330acl_dvr_close(FAR void *instance_handle, int32_t arg); static int lsm330gyro_dvr_open(FAR void *instance_handle, int32_t arg); static int lsm330gyro_dvr_close(FAR void *instance_handle, int32_t arg); static ssize_t lsm330acl_dvr_read(FAR void *instance_handle, FAR char *buffer, size_t buflen); static ssize_t lsm330gyro_dvr_read(FAR void *instance_handle, FAR char *buffer, size_t buflen); static ssize_t lsm330acl_dvr_write(FAR void *instance_handle, FAR const char *buffer, size_t buflen); static ssize_t lsm330gyro_dvr_write(FAR void *instance_handle, FAR const char *buffer, size_t buflen); static off_t lsm330acl_dvr_seek(FAR void *instance_handle, off_t offset, int whence); static off_t lsm330gyro_dvr_seek(FAR void *instance_handle, off_t offset, int whence); static int lsm330_dvr_ioctl(FAR void *instance_handle, int cmd, unsigned long arg); static void lsm330_dvr_exchange(FAR void *instance_handle, FAR const void *txbuffer, FAR void *rxbuffer, size_t nwords); /**************************************************************************** * Private Data ****************************************************************************/ static const struct file_operations g_lsm330a_fops = { lsm330acl_open, lsm330acl_close, lsm330acl_read, lsm330acl_write, lsm330acl_seek, lsm330_ioctl #ifndef CONFIG_DISABLE_POLL , NULL #endif #ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS , NULL #endif }; static const struct file_operations g_lsm330g_fops = { lsm330gyro_open, lsm330gyro_close, lsm330gyro_read, lsm330gyro_write, lsm330gyro_seek, lsm330_ioctl #ifndef CONFIG_DISABLE_POLL , NULL #endif #ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS , NULL #endif }; static const struct lsm330spi_dvr_entry_vector_s g_lsm330acl_dops = { { /* Standard sensor cluster driver entry-vector */ .driver_open = lsm330acl_dvr_open, .driver_close = lsm330acl_dvr_close, .driver_read = lsm330acl_dvr_read, .driver_write = lsm330acl_dvr_write, .driver_seek = lsm330acl_dvr_seek, .driver_ioctl = lsm330_dvr_ioctl, .driver_suspend = 0, .driver_resume = 0, }, /* lsm330 extensions follow */ .driver_spiexc = lsm330_dvr_exchange, }; static const struct lsm330spi_dvr_entry_vector_s g_lsm330gyro_dops = { { /* Standard sensor cluster driver entry-vector */ .driver_open = lsm330gyro_dvr_open, .driver_close = lsm330gyro_dvr_close, .driver_read = lsm330gyro_dvr_read, .driver_write = lsm330gyro_dvr_write, .driver_seek = lsm330gyro_dvr_seek, .driver_ioctl = lsm330_dvr_ioctl, .driver_suspend = 0, .driver_resume = 0, }, /* lsm330 extensions follow */ .driver_spiexc = lsm330_dvr_exchange, }; /* Single linked list to store instances of drivers */ static struct lsm330_dev_s *g_lsm330a_list = NULL; static struct lsm330_dev_s *g_lsm330g_list = NULL; /* Default accelerometer initialization sequence: * * Configure LSM330 to measure mode. * * 1. CR5: ODR=0, power-off * 2. CR6: Bandwidth=800Hz. +/-16g range. 4-wire SPI. * 3. CR7: Make sure auto-increment is turned on. * 4. CR2: Zero state machine 1. * 5. CR3: Zero state machine 2. * 6. CR4: DataReady interrupt disabled. * 7. CR5: Power up. 1600Hz sample rate. X,Y, & Z enabled. */ static struct lsm330_reg_pair_s g_default_lsm330_aclcr_values[] = { /* CR5 ODR[3:0] BDU ZEN YEN XEN */ /* 0000=Off 0 0 0 0=all disabled */ { .addr = LSM330_ACL_CTRL_REG5, .value = 0x00 }, /* CR6 BW[2:1] FSCALE[2:0] - - SIM */ /* 00=800Hz 10 0=16g 0 0 0=4-wire */ { .addr = LSM330_ACL_CTRL_REG6, .value = 0x20 }, /* CR7 BOOT FIFO_EN WTM_EN ADD_INC P1_MTY P1_WTM P1_OVR WTM_EN*/ /* 0 0 0 1 0 0 0 0 */ { .addr = LSM330_ACL_CTRL_REG7, .value = 0x10 }, /* CR2 HYST1 - SM1_PIN - - SM1_EN */ /* 000 0 0 0 0 0 */ { .addr = LSM330_ACL_CTRL_REG2, .value = 0x00 }, /* CR3 HYST2 - SM2_PIN - - SM2_EN */ /* 000 0 0 0 0 0 */ { .addr = LSM330_ACL_CTRL_REG3, .value = 0x00 }, /* CR4 DR_EN IEA IEL INT2_EN INT1_EN VFILT STRT */ /* 1 1 0 0 0 0 0 */ { .addr = LSM330_ACL_CTRL_REG4, .value = 0xc0 }, /* CR5 ODR[3:0] BDU ZEN YEN XEN */ /* 1001=1600Hz 1 1 1 1=all enabled */ { .addr = LSM330_ACL_CTRL_REG5, .value = 0x9f } }; /* Default gyroscope initialization sequence * * Configure LSM330 Gyroscope to measure mode. * * 1. CR1: Power up. 760Hz sample rate. Bandwidth=100Hz. X,Y, & Z enabled. * 2. CR2: Normal measurement mode. High pass filter OFF. * 3. CR3: All interrupts disabled. * 4. CR4: +/-500dps range. Block update. 4-wire SPI. * 5. CR5: Select Low Pass Filter 1 outputs. LPF2 bypassed. */ static struct lsm330_reg_pair_s g_default_lsm330_gyrocr_values[] = { /* CR1 DR[1:0] BW[1:0] PD Zen Xen Yen */ /* 1 1=760Hz 1 1=100Hz 1 1 1 1 */ { .addr = LSM330_GYRO_CTRL_REG1, .value = 0xFF }, /* CR2 EXTRen LVLen HPM[1:0] HPCF[3:0] */ /* 0 0 00=Normal xxxx Default */ { .addr = LSM330_GYRO_CTRL_REG2, .value = 0x00 }, /* CR3 I1_Int1 I1_Boot H_Lactive PP_OD I2_DRDY I2_WTM I2_ORun I2_Empty */ /* 0 0 0 0 0 0 0 0 */ { .addr = LSM330_GYRO_CTRL_REG3, .value = 0x00 }, /* CR4 BDU BLE FS[1:0] 0 0 0 SIM */ /* 1 0 01=500dps 0 0 0 0=4-wire */ { .addr = LSM330_GYRO_CTRL_REG4, .value = 0x90 }, /* CR5 BOOT FIFO_EN - HPen INT1_Sel[1:0] Out_Sel[1:0] */ /* 0 0 0 0 00=LPF1 00=LPF1 */ { .addr = LSM330_GYRO_CTRL_REG5, .value = 0x0a } }; /****************************************************************************** * Private Functions ****************************************************************************/ /**************************************************************************** * Name: lsm330_read_register ****************************************************************************/ static uint8_t lsm330_read_register(FAR struct lsm330_dev_s *dev, uint8_t reg_addr) { uint8_t reg_data; /* Lock the SPI bus so that only one device can access it at the same time */ SPI_LOCK(dev->spi, true); /* Setup SPI frequency and mode */ SPI_SETFREQUENCY(dev->spi, LSM330_SPI_FREQUENCY); SPI_SETMODE(dev->spi, LSM330_SPI_MODE); /* Set CS to low to select the LSM330 */ SPI_SELECT(dev->spi, dev->config->spi_devid, true); /* Transmit the register address from where we want to read. */ SPI_SEND(dev->spi, reg_addr | LSM330_READ); /* Write an idle byte while receiving the requested data */ reg_data = (uint8_t) (SPI_SEND(dev->spi, 0xff)); /* Set CS to high to deselect the LSM330 */ SPI_SELECT(dev->spi, dev->config->spi_devid, false); /* Unlock the SPI bus */ SPI_LOCK(dev->spi, false); return reg_data; } /****************************************************************************** * Name: lsm330_read_acl_registerblk ******************************************************************************/ static void lsm330_read_acl_registerblk(FAR struct lsm330_dev_s *dev, uint8_t reg_addr, FAR uint8_t *reg_data, uint8_t xfercnt) { /* Lock the SPI bus so that only one device can access it at the same time */ SPI_LOCK(dev->spi, true); /* Setup SPI frequency and mode */ SPI_SETFREQUENCY(dev->spi, LSM330_SPI_FREQUENCY); SPI_SETMODE(dev->spi, LSM330_SPI_MODE); /* Set CS to low to select the LSM330 */ SPI_SELECT(dev->spi, dev->config->spi_devid, true); /* Transmit the register address from where we want to start reading */ SPI_SEND(dev->spi, reg_addr | LSM330_READ); /* Write idle bytes while receiving the requested data */ while ( 0 != xfercnt-- ) { *reg_data++ = (uint8_t)SPI_SEND(dev->spi, 0xff); } /* Set CS to high to deselect the LSM330 */ SPI_SELECT(dev->spi, dev->config->spi_devid, false); /* Unlock the SPI bus */ SPI_LOCK(dev->spi, false); } /****************************************************************************** * Name: lsm330_read_gyro_registerblk ******************************************************************************/ static void lsm330_read_gyro_registerblk(FAR struct lsm330_dev_s *dev, uint8_t reg_addr, FAR uint8_t *reg_data, uint8_t xfercnt) { /* Lock the SPI bus so that only one device can access it at the same time */ SPI_LOCK(dev->spi, true); /* Setup SPI frequency and mode */ SPI_SETFREQUENCY(dev->spi, LSM330_SPI_FREQUENCY); SPI_SETMODE(dev->spi, LSM330_SPI_MODE); /* Set CS to low to select the LSM330 */ SPI_SELECT(dev->spi, dev->config->spi_devid, true); /* Transmit the register address from where we want to start reading */ SPI_SEND(dev->spi, reg_addr | LSM330_READ | (xfercnt>1 ? LSM330_GYRO_AUTO : 0)); /* Write idle bytes while receiving the requested data */ while ( 0 != xfercnt-- ) { *reg_data++ = (uint8_t)SPI_SEND(dev->spi, 0xff); } /* Set CS to high to deselect the LSM330 */ SPI_SELECT(dev->spi, dev->config->spi_devid, false); /* Unlock the SPI bus */ SPI_LOCK(dev->spi, false); } /**************************************************************************** * Name: lsm330_write_register ****************************************************************************/ static void lsm330_write_register(FAR struct lsm330_dev_s *dev, uint8_t reg_addr, uint8_t reg_data) { /* Lock the SPI bus so that only one device can access it at the same time */ SPI_LOCK(dev->spi, true); /* Setup SPI frequency and mode */ SPI_SETFREQUENCY(dev->spi, LSM330_SPI_FREQUENCY); SPI_SETMODE(dev->spi, LSM330_SPI_MODE); /* Set CS to low to select the LSM330 */ SPI_SELECT(dev->spi, dev->config->spi_devid, true); /* Transmit the register address to where we want to write */ SPI_SEND(dev->spi, reg_addr | LSM330_WRITE); /* Transmit the content which should be written into the register */ SPI_SEND(dev->spi, reg_data); /* Set CS to high to deselect the LSM330 */ SPI_SELECT(dev->spi, dev->config->spi_devid, false); /* Unlock the SPI bus */ SPI_LOCK(dev->spi, false); } /**************************************************************************** * Name: lsm330_write_acl_registerblk ****************************************************************************/ static void lsm330_write_acl_registerblk(FAR struct lsm330_dev_s *dev, uint8_t reg_addr, FAR uint8_t *reg_data, uint8_t xfercnt) { /* Lock the SPI bus so that only one device can access it at the same time */ SPI_LOCK(dev->spi, true); /* Setup SPI frequency and mode */ SPI_SETFREQUENCY(dev->spi, LSM330_SPI_FREQUENCY); SPI_SETMODE(dev->spi, LSM330_SPI_MODE); /* Set CS to low which selects the LSM330 */ SPI_SELECT(dev->spi, dev->config->spi_devid, true); /* Transmit the register address to where we want to start writing */ SPI_SEND(dev->spi, reg_addr | LSM330_WRITE); /* Transmit the content which should be written in the register block */ while ( 0 != xfercnt-- ) { SPI_SEND(dev->spi, *reg_data++); } /* Set CS to high to deselect the LSM330 */ SPI_SELECT(dev->spi, dev->config->spi_devid, false); /* Unlock the SPI bus */ SPI_LOCK(dev->spi, false); } /**************************************************************************** * Name: lsm330_write_gyro_registerblk ****************************************************************************/ static void lsm330_write_gyro_registerblk(FAR struct lsm330_dev_s *dev, uint8_t reg_addr, FAR uint8_t *reg_data, uint8_t xfercnt) { /* Lock the SPI bus so that only one device can access it at the same time */ SPI_LOCK(dev->spi, true); /* Setup SPI frequency and mode */ SPI_SETFREQUENCY(dev->spi, LSM330_SPI_FREQUENCY); SPI_SETMODE(dev->spi, LSM330_SPI_MODE); /* Set CS to low which selects the LSM330 */ SPI_SELECT(dev->spi, dev->config->spi_devid, true); /* Transmit the register address to where we want to start writing */ SPI_SEND(dev->spi, reg_addr | LSM330_WRITE | (xfercnt>1 ? LSM330_GYRO_AUTO : 0)); /* Transmit the content which should be written in the register block */ while ( 0 != xfercnt-- ) { SPI_SEND(dev->spi, *reg_data++); } /* Set CS to high to deselect the LSM330 */ SPI_SELECT(dev->spi, dev->config->spi_devid, false); /* Unlock the SPI bus */ SPI_LOCK(dev->spi, false); } /**************************************************************************** * Name: lsm330acl_reset ****************************************************************************/ static void lsm330acl_reset(FAR struct lsm330_dev_s *dev) { /* Reset LSM330 Accelerometer. Write only. Begin a boot. * Note that the LSM330 ACL does not set the BOOT bit for read, so we * can't loop on it. */ lsm330_write_register(dev, LSM330_ACL_CTRL_REG7, LSM330_ACR7_BOOT); /* Wait for boot to finish */ up_mdelay(20); /* Set auto-increment. * * CR7 BOOT FIFO_EN WTM_EN ADD_INC P1_MTY P1_WTM P1_OVR WTM_EN * 0 0 0 1 0 0 0 0 */ lsm330_write_register(dev, LSM330_ACL_CTRL_REG7, 0x10); } /**************************************************************************** * Name: lsm330gyro_reset ****************************************************************************/ static void lsm330gyro_reset(FAR struct lsm330_dev_s *dev) { /* Reset LSM330 Gyroscope. Write only. Begin a boot. * Note that the LSM330 ACL does not set the BOOT bit for read, so we * can't loop on it. */ lsm330_write_register(dev, LSM330_GYRO_CTRL_REG5, LSM_GYRO_BOOT_MASK); /* Wait for boot to finish */ up_mdelay(20); } /**************************************************************************** * Name: lsm330acl_dvr_open ****************************************************************************/ static int lsm330acl_dvr_open(FAR void *instance_handle, int32_t arg) { FAR struct lsm330_dev_s *priv = (FAR struct lsm330_dev_s *)instance_handle; FAR struct lsm330_reg_pair_s *initp; uint8_t reg_content; int ret; int sz; int i; sninfo("lsm330acl_open: entered...\n"); DEBUGASSERT(priv != NULL); UNUSED(arg); ret = nxsem_trywait(&priv->devicesem); if (ret < 0) { sninfo("INFO: LSM330 Accelerometer is already open.\n"); set_errno(-EBUSY); return -1; } /* Read the ID Register */ priv->readonly = false; reg_content = lsm330_read_register(priv, LSM330_ACL_IDREG); sninfo("LSM330_ACL_IDREG = 0x%02x\n", reg_content); if (reg_content!=LSM330_ACL_IDREG_VALUE) { /* Made info log level to permit open being used as a device probe. */ snwarn("INFO: " "Device ID (0x%02X) does not match expected LSM330 Acl ID (0x%02).\n", reg_content, LSM330_ACL_IDREG_VALUE); set_errno(ENODEV); priv->readonly = true; } else /* ID matches */ { lsm330acl_reset(priv); /* Perform a sensor reset */ /* Choose the initialization sequence */ if (priv->config->initial_cr_values_size == 0 || priv->config->initial_cr_values == NULL) { initp = g_default_lsm330_aclcr_values; /* Default values */ sz = LSM330_INITIAL_ACLCR_SIZE; sninfo("Using default CRs\n"); } else { initp = priv->config->initial_cr_values; /* User supplied values */ sz = priv->config->initial_cr_values_size; sninfo("Using provided CRs\n"); } /* Apply the initialization sequence */ for (i = 0; i < sz; i++) { lsm330_write_register(priv, initp[i].addr, initp[i].value); } #ifdef CONFIG_DEBUG_SENSORS_INFO /* Read back the content of all control registers for debug purposes */ reg_content = lsm330_read_register(priv, LSM330_ACL_CTRL_REG5); sninfo("LSM330_ACL_CTRL_REG5 = 0x%02x\n", reg_content); reg_content = lsm330_read_register(priv, LSM330_ACL_CTRL_REG7); sninfo("LSM330_ACL_CTRL_REG7 = 0x%02x\n", reg_content); reg_content = lsm330_read_register(priv, LSM330_ACL_CTRL_REG6); sninfo("LSM330_ACL_CTRL_REG6 = 0x%02x\n", reg_content); reg_content = lsm330_read_register(priv, LSM330_ACL_CTRL_REG4); sninfo("LSM330_ACL_CTRL_REG4 = 0x%02x\n", reg_content); #endif } priv->seek_address = (uint8_t) LSM330_ACL_OUT_X_L; return OK; } /**************************************************************************** * Name: lsm330gyro_dvr_open ****************************************************************************/ static int lsm330gyro_dvr_open(FAR void *instance_handle, int32_t arg) { FAR struct lsm330_dev_s *priv = (FAR struct lsm330_dev_s *)instance_handle; FAR struct lsm330_reg_pair_s *initp; uint8_t reg_content; int ret; int sz; int i; sninfo("lsm330gyro_open: entered...\n"); DEBUGASSERT(priv != NULL); UNUSED(arg); ret = nxsem_trywait(&priv->devicesem); if (ret < 0) { sninfo("INFO: LSM330 Gyroscope is already open.\n"); set_errno(-EBUSY); return -1; } /* Read the ID Register */ priv->readonly = false; reg_content = lsm330_read_register(priv, LSM330_GYRO_IDREG); sninfo("LSM330_GYRO_IDREG = 0x%02x\n", reg_content); if (reg_content!=LSM330_GYRO_IDREG_VALUE) { /* Made warning log level to permit open being used as a device probe. */ snwarn("INFO: " "Device ID (0x%02X) does not match expected LSM330 Gyro ID (0x%02).\n", reg_content, LSM330_GYRO_IDREG_VALUE); set_errno(ENODEV); priv->readonly = true; } else /* ID matches */ { lsm330gyro_reset(priv); /* Perform a sensor reset */ /* Choose the initialization sequence */ if (priv->config->initial_cr_values_size == 0 || priv->config->initial_cr_values == NULL) { initp = g_default_lsm330_gyrocr_values; /* Default values */ sz = LSM330_INITIAL_GYROCR_SIZE; } else { initp = priv->config->initial_cr_values; /* User supplied values */ sz = priv->config->initial_cr_values_size; } /* Apply the initialization sequence */ for (i = 0; i < sz; i++) { lsm330_write_register(priv, initp[i].addr, initp[i].value); } #ifdef CONFIG_DEBUG_SENSORS_INFO /* Read back the content of all control registers for debug purposes */ reg_content = lsm330_read_register(priv, LSM330_GYRO_CTRL_REG1); sninfo("LSM330_GYRO_CTRL_REG1 = 0x%02x\n", reg_content); reg_content = lsm330_read_register(priv, LSM330_GYRO_CTRL_REG2); sninfo("LSM330_GYRO_CTRL_REG2 = 0x%02x\n", reg_content); reg_content = lsm330_read_register(priv, LSM330_GYRO_CTRL_REG3); sninfo("LSM330_GYRO_CTRL_REG3 = 0x%02x\n", reg_content); reg_content = lsm330_read_register(priv, LSM330_GYRO_CTRL_REG4); sninfo("LSM330_GYRO_CTRL_REG4 = 0x%02x\n", reg_content); reg_content = lsm330_read_register(priv, LSM330_GYRO_CTRL_REG5); sninfo("LSM330_GYRO_CTRL_REG5 = 0x%02x\n", reg_content); #endif } priv->seek_address = (uint8_t) LSM330_GYRO_OUT_X_L; return OK; } /**************************************************************************** * Name: lsm330acl_dvr_close ******************************************************************************/ static int lsm330acl_dvr_close(FAR void *instance_handle, int32_t arg) { FAR struct lsm330_dev_s *priv = (FAR struct lsm330_dev_s *)instance_handle; DEBUGASSERT(priv != NULL); UNUSED(arg); /* Perform a reset to put the sensor in standby mode. */ lsm330acl_reset(priv); /* Release the sensor */ nxsem_post(&priv->devicesem); return OK; } /**************************************************************************** * Name: lsm330gyro_dvr_close ******************************************************************************/ static int lsm330gyro_dvr_close(FAR void *instance_handle, int32_t arg) { FAR struct lsm330_dev_s *priv = (FAR struct lsm330_dev_s *)instance_handle; sninfo("lsm330gyro_close: entered...\n"); DEBUGASSERT(priv != NULL); UNUSED(arg); /* Perform a reset to put the sensor in standby mode. */ lsm330gyro_reset(priv); /* Release the sensor */ nxsem_post(&priv->devicesem); return OK; } /**************************************************************************** * Name: lsm330acl_dvr_read ****************************************************************************/ static ssize_t lsm330acl_dvr_read(FAR void *instance_handle, FAR char *buffer, size_t buflen) { FAR struct lsm330_dev_s *priv = (FAR struct lsm330_dev_s *)instance_handle; DEBUGASSERT(priv != NULL); lsm330_read_acl_registerblk(priv, priv->seek_address, (uint8_t *)buffer, buflen); return buflen; } /**************************************************************************** * Name: lsm330gyro_dvr_read ****************************************************************************/ static ssize_t lsm330gyro_dvr_read(FAR void *instance_handle, FAR char *buffer, size_t buflen) { FAR struct lsm330_dev_s *priv = (FAR struct lsm330_dev_s *)instance_handle; DEBUGASSERT(priv != NULL); lsm330_read_gyro_registerblk(priv, priv->seek_address, (uint8_t *)buffer, buflen); return buflen; } /**************************************************************************** * Name: lsm330acl_dvr_write ****************************************************************************/ static ssize_t lsm330acl_dvr_write(FAR void *instance_handle, FAR const char *buffer, size_t buflen) { FAR struct lsm330_dev_s *priv = (FAR struct lsm330_dev_s *)instance_handle; DEBUGASSERT(priv != NULL); if (priv->readonly) { set_errno(-EROFS); return -1; } lsm330_write_acl_registerblk(priv, priv->seek_address, (uint8_t *)buffer, buflen); return buflen; } /**************************************************************************** * Name: lsm330gyro_dvr_write ****************************************************************************/ static ssize_t lsm330gyro_dvr_write(FAR void *instance_handle, FAR const char *buffer, size_t buflen) { FAR struct lsm330_dev_s *priv = (FAR struct lsm330_dev_s *)instance_handle; DEBUGASSERT(priv != NULL); if (priv->readonly) { set_errno(-EROFS); return -1; } lsm330_write_gyro_registerblk(priv, priv->seek_address, (FAR uint8_t *)buffer, buflen); return buflen; } /**************************************************************************** * Name: lsm330acl_dvr_seek ****************************************************************************/ static off_t lsm330acl_dvr_seek(FAR void *instance_handle, off_t offset, int whence) { FAR struct lsm330_dev_s *priv = (FAR struct lsm330_dev_s *)instance_handle; off_t reg; DEBUGASSERT(priv != NULL); switch (whence) { case SEEK_CUR: /* incremental seek */ reg = priv->seek_address + offset; if (0 > reg || reg > LSM330_ACL_LAST) { set_errno(-EINVAL); return -1; } priv->seek_address = reg; break; case SEEK_END: /* seek to the 1st X-data register */ priv->seek_address = LSM330_ACL_OUT_X_L; break; case SEEK_SET: /* seek to designated address */ if (0 > offset || offset > LSM330_ACL_LAST) { set_errno(-EINVAL); return -1; } priv->seek_address = offset; break; default: /* Invalid whence */ set_errno(-EINVAL); return -1; } return priv->seek_address; } /**************************************************************************** * Name: lsm330gyro_dvr_seek ****************************************************************************/ static off_t lsm330gyro_dvr_seek(FAR void *instance_handle, off_t offset, int whence) { FAR struct lsm330_dev_s *priv = (FAR struct lsm330_dev_s *)instance_handle; off_t reg; DEBUGASSERT(priv != NULL); switch (whence) { case SEEK_CUR: /* incremental seek */ reg = priv->seek_address + offset; if (0 > reg || reg > LSM330_GYRO_LAST) { set_errno(-EINVAL); return -1; } priv->seek_address = reg; break; case SEEK_END: /* seek to the 1st data register */ priv->seek_address = LSM330_GYRO_OUT_X_L; break; case SEEK_SET: /* seek to designated address */ if (0 > offset || offset > LSM330_GYRO_LAST) { set_errno(-EINVAL); return -1; } priv->seek_address = offset; break; default: /* Invalid whence */ set_errno(-EINVAL); return -1; } return priv->seek_address; } /**************************************************************************** * Name: lsm330_dvr_ioctl ****************************************************************************/ static int lsm330_dvr_ioctl(FAR void *instance_handle, int cmd, unsigned long arg) { int ret = OK; switch (cmd) { /* Command was not recognized */ default: snerr("ERROR: Unrecognized cmd: %d\n", cmd); ret = -ENOTTY; break; } return ret; } /**************************************************************************** * Name: lsm330_dvr_exchange (with SPI DMA capability) * * Description: * Exchange a block of data on SPI using DMA * * Input Parameters: * instance_handle - Pointer to struct lsm330_dev_s. * txbuffer - A pointer to the buffer of data to be sent * rxbuffer - A pointer to a buffer in which to receive data * nwords - the length of data to be exchanged in units of words. * The wordsize is determined by the number of bits-per-word * selected for the SPI interface. If nbits <= 8, the data is * packed into uint8_t's; if nbits >8, the data is packed into * uint16_t's * * Returned Value: * None * ******************************************************************************/ static void lsm330_dvr_exchange(FAR void *instance_handle, FAR const void *txbuffer, FAR void *rxbuffer, size_t nwords) { FAR struct lsm330_dev_s *priv = (FAR struct lsm330_dev_s *)instance_handle; FAR struct spi_dev_s *spi = priv->spi; sninfo("In lsm330_dvr_exchange: Handle=0x%08X\n", instance_handle); /* Lock the SPI bus so that only one device can access it at the same time */ SPI_LOCK(spi, true); SPI_SETFREQUENCY(spi, LSM330_SPI_FREQUENCY); SPI_SETMODE(spi, LSM330_SPI_MODE); sninfo("Calling SPI_EXCHANGE: devid=0x%08X\n", priv->config->spi_devid); /* Set CS to low which selects the LSM330 */ SPI_SELECT(spi, priv->config->spi_devid, true); /* Perform an SPI exchange block operation. */ SPI_EXCHANGE(spi, txbuffer, rxbuffer, nwords); /* Set CS to high to deselect the LSM330 */ SPI_SELECT(spi, priv->config->spi_devid, false); sninfo("Returned from : SPI_EXCHANGE\n"); /* Unlock the SPI bus */ SPI_LOCK(spi, false); } /**************************************************************************** * Name: lsm330acl_open ****************************************************************************/ static int lsm330acl_open(FAR struct file *filep) { FAR struct inode *inode = filep->f_inode; FAR struct lsm330_dev_s *priv = inode->i_private; int ret; ret = lsm330acl_dvr_open((FAR void *)priv, 0); return ret; } /**************************************************************************** * Name: lsm330gyro_open ****************************************************************************/ static int lsm330gyro_open(FAR struct file *filep) { FAR struct inode *inode = filep->f_inode; FAR struct lsm330_dev_s *priv = inode->i_private; int ret; ret = lsm330gyro_dvr_open((FAR void *)priv, 0); return ret; } /**************************************************************************** * Name: lsm330acl_close ****************************************************************************/ static int lsm330acl_close(FAR struct file *filep) { FAR struct inode *inode = filep->f_inode; FAR struct lsm330_dev_s *priv = inode->i_private; int ret; ret = lsm330acl_dvr_close((FAR void *)priv, 0); return ret; } /**************************************************************************** * Name: lsm330gyro_close ****************************************************************************/ static int lsm330gyro_close(FAR struct file *filep) { FAR struct inode *inode = filep->f_inode; FAR struct lsm330_dev_s *priv = inode->i_private; int ret; ret = lsm330gyro_dvr_close((FAR void *)priv, 0); return ret; } /**************************************************************************** * Name: lsm330acl_read ****************************************************************************/ static ssize_t lsm330acl_read(FAR struct file *filep, FAR char *buffer, size_t buflen) { FAR struct inode *inode = filep->f_inode; FAR struct lsm330_dev_s *priv = inode->i_private; return lsm330acl_dvr_read(priv, buffer, buflen); } /**************************************************************************** * Name: lsm330gyro_read ****************************************************************************/ static ssize_t lsm330gyro_read(FAR struct file *filep, FAR char *buffer, size_t buflen) { FAR struct inode *inode = filep->f_inode; FAR struct lsm330_dev_s *priv = inode->i_private; return lsm330gyro_dvr_read(priv, buffer, buflen); } /**************************************************************************** * Name: lsm330acl_write ****************************************************************************/ static ssize_t lsm330acl_write(FAR struct file *filep, FAR const char *buffer, size_t buflen) { FAR struct inode *inode = filep->f_inode; FAR struct lsm330_dev_s *priv = inode->i_private; return lsm330acl_dvr_write(priv, buffer, buflen); } /**************************************************************************** * Name: lsm330gyro_write ****************************************************************************/ static ssize_t lsm330gyro_write(FAR struct file *filep, FAR const char *buffer, size_t buflen) { FAR struct inode *inode = filep->f_inode; FAR struct lsm330_dev_s *priv = inode->i_private; return lsm330gyro_dvr_write(priv, buffer, buflen); } /**************************************************************************** * Name: lsm330acl_seek ****************************************************************************/ static off_t lsm330acl_seek(FAR struct file *filep, off_t offset, int whence) { FAR struct inode *inode = filep->f_inode; FAR struct lsm330_dev_s *priv = inode->i_private; return lsm330acl_dvr_seek(priv, offset, whence); } /**************************************************************************** * Name: lsm330gyro_seek ****************************************************************************/ static off_t lsm330gyro_seek(FAR struct file *filep, off_t offset, int whence) { FAR struct inode *inode = filep->f_inode; FAR struct lsm330_dev_s *priv = inode->i_private; return lsm330gyro_dvr_seek(priv, offset, whence); } /**************************************************************************** * Name: lsm330_ioctl ****************************************************************************/ static int lsm330_ioctl(FAR struct file *filep, int cmd, unsigned long arg) { FAR struct inode *inode = filep->f_inode; FAR struct lsm330_dev_s *priv = inode->i_private; return lsm330_dvr_ioctl(priv, cmd, arg); } /**************************************************************************** * Public Functions ****************************************************************************/ /**************************************************************************** * Name: lsm330_register * * Description: * Register the LSM330 character device as 'devpath' * * Input Parameters: * devpath_acl - The full path to the driver to register. E.g., * "/dev/acl0" * devpath_gyro - The full path to the driver to register. E.g., * "/dev/gyr0" * spi - An instance of the SPI interface to use to communicate * with LSM330 * config_acl - configuration for the LSM330 accelerometer driver. * For details see description above. * config_gyro - configuration for the LSM330 gyroscope driver. * For details see description above. * caller_is_driver - 0 (false) Driver user is a user application using * the fops interface. * 1 (true) "Driver to Driver" interface will be used * in addition to the fops interface. * * Returned Value: * Zero (OK) on success; a negated errno value on failure. * ****************************************************************************/ int lsm330_register(FAR const char *devpath_acl, FAR const char *devpath_gyro, FAR struct spi_dev_s *spi, FAR struct lsm330_config_s *config_acl, FAR struct lsm330_config_s *config_gyro) { FAR struct lsm330_dev_s *priva; FAR struct lsm330_dev_s *priv; int ret; /* Sanity check */ DEBUGASSERT(spi != NULL); DEBUGASSERT(config_acl != NULL); DEBUGASSERT(config_gyro != NULL); config_acl->leaf_handle = NULL; config_gyro->leaf_handle = NULL; config_acl->sc_ops = NULL; config_gyro->sc_ops = NULL; /* Initialize the LSM330 accelerometer device structure. */ priv = (FAR struct lsm330_dev_s *)kmm_malloc(sizeof(struct lsm330_dev_s)); if (priv == NULL) { snerr("ERROR: Failed to allocate accelerometer instance\n"); return -ENOMEM; } priv->spi = spi; priv->config = config_acl; /* Initialize sensor and sensor data access semaphore */ nxsem_init(&priv->devicesem, 0, 1); nxsem_init(&priv->datasem, 0, 1); /* Setup SPI frequency and mode */ SPI_SETFREQUENCY(spi, LSM330_SPI_FREQUENCY); SPI_SETMODE(spi, LSM330_SPI_MODE); /* Register the character driver */ ret = register_driver(devpath_acl, &g_lsm330a_fops, 0666, priv); if (ret < 0) { snerr("ERROR: Failed to register accelerometer driver: %d\n", ret); nxsem_destroy(&priv->datasem); kmm_free(priv); return ret; } /* Since we support multiple LSM330 devices, we will need to add this new * instance to a list of device instances so that it can be found by the * interrupt handler based on the received IRQ number. */ priv->flink = g_lsm330a_list; g_lsm330a_list = priv; priva = priv; config_acl->leaf_handle = (void *) priv; /* Initialize the LSM330 gyroscope device structure. */ priv = (FAR struct lsm330_dev_s *)kmm_malloc(sizeof(struct lsm330_dev_s)); if (priv == NULL) { snerr("ERROR: Failed to allocate gyroscope instance\n"); ret = -ENOMEM; goto err_exit; } priv->spi = spi; priv->config = config_gyro; /* Initialize sensor and sensor data access semaphore */ nxsem_init(&priv->devicesem, 0, 1); nxsem_init(&priv->datasem, 0, 1); /* Register the character driver */ ret = register_driver(devpath_gyro, &g_lsm330g_fops, 0666, priv); if (ret < 0) { snerr("ERROR: Failed to register gyroscope driver: %d\n", ret); nxsem_destroy(&priv->datasem); kmm_free(priv); goto err_exit; } /* Since we support multiple LSM330 devices, we will need to add this new * instance to a list of device instances so that it can be found by the * interrupt handler based on the received IRQ number. */ priv->flink = g_lsm330g_list; g_lsm330g_list = priv; config_gyro->leaf_handle = (void *) priv; config_acl->sc_ops = &g_lsm330acl_dops; config_gyro->sc_ops = &g_lsm330gyro_dops; /* If this is part of a kernel controlled sensor cluster driver, * then return a handle to the caller */ return OK; err_exit: /* Registration the of the gyroscope failed, so we need to destroy the * accelerometer instance. */ nxsem_destroy(&priva->datasem); kmm_free(priva); return ret; } #endif /* CONFIG_SPI && CONFIG_SENSORS_LSM330SPI && CONFIG_SPI_EXCHANGE */