diff --git a/drivers/sensors/Kconfig b/drivers/sensors/Kconfig index d43362eb6cb..9b3e2660b20 100644 --- a/drivers/sensors/Kconfig +++ b/drivers/sensors/Kconfig @@ -32,6 +32,13 @@ config BMP180 ---help--- Enable driver support for the Bosch BMP180 barometer sensor. +config SENSORS_L3GD20 + bool "ST L3GD20 Gyroscope Sensor support" + default n + select SPI + ---help--- + Enable driver support for the ST L3GD20 gyroscope sensor. + config SENSOR_KXTJ9 bool "Kionix KXTJ9 Accelerometer support" default n diff --git a/drivers/sensors/Make.defs b/drivers/sensors/Make.defs index f99916d0578..c4b14ace8f6 100644 --- a/drivers/sensors/Make.defs +++ b/drivers/sensors/Make.defs @@ -130,6 +130,10 @@ ifeq ($(CONFIG_LIS3MDL),y) CSRCS += lis3mdl.c endif +ifeq ($(CONFIG_SENSORS_L3GD20),y) + CSRCS += l3gd20.c +endif + endif # CONFIG_SPI # Quadrature encoder upper half diff --git a/drivers/sensors/l3gd20.c b/drivers/sensors/l3gd20.c new file mode 100644 index 00000000000..5c37380515a --- /dev/null +++ b/drivers/sensors/l3gd20.c @@ -0,0 +1,652 @@ +/**************************************************************************** + * drivers/sensors/l3gd20.c + * Character driver for the ST L3GD20 3-Axis gyroscope. + * + * Authors: Mateusz Szafoni + * based on drivers/sensors/lis3dsh.c + * + * 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 + +#if defined(CONFIG_SPI) && defined(CONFIG_SENSORS_L3GD20) + +#if !defined(CONFIG_SCHED_HPWORK) +# error Hi-priority work queue support is required (CONFIG_SCHED_HPWORK) +#endif + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct l3gd20_dev_s +{ + FAR struct l3gd20_dev_s *flink; /* Supports a singly linked list of + * drivers */ + FAR struct spi_dev_s *spi; /* Pointer to the SPI instance */ + FAR struct l3gd20_config_s *config; /* Pointer to the configuration of the + * L3GD20 sensor */ + sem_t datasem; /* Manages exclusive access to this + * structure */ + struct l3gd20_sensor_data_s data; /* The data as measured by the sensor */ + struct work_s work; /* The work queue is responsible for + * retrieving the data from the sensor + * after the arrival of new data was + * signalled in an interrupt */ +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static void l3gd20_read_register(FAR struct l3gd20_dev_s *dev, + uint8_t const reg_addr, uint8_t * reg_data); +static void l3gd20_write_register(FAR struct l3gd20_dev_s *dev, + uint8_t const reg_addr, + uint8_t const reg_data); +static void l3gd20_reset(FAR struct l3gd20_dev_s *dev); +static void l3gd20_read_measurement_data(FAR struct l3gd20_dev_s *dev); +static void l3gd20_read_gyroscope_data(FAR struct l3gd20_dev_s *dev, + uint16_t * x_gyr, uint16_t * y_gyr, + uint16_t * z_gyr); +static void l3gd20_read_temperature(FAR struct l3gd20_dev_s *dev, + uint8_t * temperature); +static int l3gd20_interrupt_handler(int irq, FAR void *context); +static void l3gd20_worker(FAR void *arg); + +static int l3gd20_open(FAR struct file *filep); +static int l3gd20_close(FAR struct file *filep); +static ssize_t l3gd20_read(FAR struct file *, FAR char *, size_t); +static ssize_t l3gd20_write(FAR struct file *filep, FAR const char *buffer, + size_t buflen); +static int l3gd20_ioctl(FAR struct file *filep, int cmd, unsigned long arg); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct file_operations g_l3gd20_fops = + { + l3gd20_open, + l3gd20_close, + l3gd20_read, + l3gd20_write, + NULL, + l3gd20_ioctl +#ifndef CONFIG_DISABLE_POLL + , NULL +#endif +#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS + , NULL +#endif + }; + +/* Single linked list to store instances of drivers */ + +static struct l3gd20_dev_s *g_l3gd20_list = NULL; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: l3gd20_read_register + ****************************************************************************/ + +static void l3gd20_read_register(FAR struct l3gd20_dev_s *dev, + uint8_t const 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); + + /* Set CS to low which selects the L3GD20 */ + + SPI_SELECT(dev->spi, dev->config->spi_devid, true); + + /* Transmit the register address from where we want to read - the MSB needs + * to be set to indicate the read indication. + */ + + SPI_SEND(dev->spi, reg_addr | 0x80); + + /* Write an idle byte while receiving the required data */ + + *reg_data = (uint8_t) (SPI_SEND(dev->spi, 0)); + + /* Set CS to high which deselects the L3GD20 */ + + SPI_SELECT(dev->spi, dev->config->spi_devid, false); + + /* Unlock the SPI bus */ + + SPI_LOCK(dev->spi, false); +} + +/**************************************************************************** + * Name: l3gd20_write_register + ****************************************************************************/ + +static void l3gd20_write_register(FAR struct l3gd20_dev_s *dev, + uint8_t const reg_addr, + uint8_t const reg_data) +{ + /* Lock the SPI bus so that only one device can access it at the same time */ + + SPI_LOCK(dev->spi, true); + + /* Set CS to low which selects the L3GD20 */ + + 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); + + /* Transmit the content which should be written in the register */ + + SPI_SEND(dev->spi, reg_data); + + /* Set CS to high which deselects the L3GD20 */ + + SPI_SELECT(dev->spi, dev->config->spi_devid, false); + + /* Unlock the SPI bus */ + + SPI_LOCK(dev->spi, false); +} + +/**************************************************************************** + * Name: l3gd20_reset + ****************************************************************************/ +static void l3gd20_reset(FAR struct l3gd20_dev_s *dev) +{ + /* Reboot memory content */ + + l3gd20_write_register(dev, L3GD20_CTRL_REG_5, L3GD20_CTRL_REG_5_BOOT_bm); + + up_mdelay(100); +} + +/**************************************************************************** + * Name: l3gd20_read_measurement_data + ****************************************************************************/ + +static void l3gd20_read_measurement_data(FAR struct l3gd20_dev_s *dev) +{ + int ret; + uint16_t x_gyr = 0, y_gyr = 0, z_gyr = 0; + uint8_t temperature = 0; + + /* Read Gyroscope */ + + l3gd20_read_gyroscope_data(dev, &x_gyr, &y_gyr, &z_gyr); + + /* Read Temperature */ + + l3gd20_read_temperature(dev, &temperature); + + /* Aquire the semaphore before the data is copied */ + + ret = sem_wait(&dev->datasem); + if (ret < 0) + { + snerr("ERROR: Could not aquire dev->datasem: %d\n", ret); + return; + } + + /* Copy retrieve data to internal data structure */ + + dev->data.x_gyr = (int16_t) (x_gyr); + dev->data.y_gyr = (int16_t) (y_gyr); + dev->data.z_gyr = (int16_t) (z_gyr); + + /* Give back the semaphore */ + + sem_post(&dev->datasem); +} + + +/**************************************************************************** + * Name: l3gd20_read_gyroscope_data + ****************************************************************************/ + +static void l3gd20_read_gyroscope_data(FAR struct l3gd20_dev_s *dev, + uint16_t * x_gyr, uint16_t * y_gyr, + uint16_t * z_gyr) +{ + /* Lock the SPI bus so that only one device can access it at the same time */ + + SPI_LOCK(dev->spi, true); + + /* Set CS to low which selects the L3GD20 */ + + SPI_SELECT(dev->spi, dev->config->spi_devid, true); + + /* Transmit the register address from where we want to start reading + * 0x80 -> MSB is set -> Read Indication + * 0x40 -> MSB-1 (MS-Bit) is set -> auto increment of address when reading + * multiple bytes. + */ + + SPI_SEND(dev->spi, (L3GD20_OUT_X_L_REG | 0x80 | 0x40)); /* RX */ + + *x_gyr = ((uint16_t) (SPI_SEND(dev->spi, 0)) << 0); /* LSB */ + *x_gyr |= ((uint16_t) (SPI_SEND(dev->spi, 0)) << 8); /* MSB */ + + *y_gyr = ((uint16_t) (SPI_SEND(dev->spi, 0)) << 0); /* LSB */ + *y_gyr |= ((uint16_t) (SPI_SEND(dev->spi, 0)) << 8); /* MSB */ + + *z_gyr = ((uint16_t) (SPI_SEND(dev->spi, 0)) << 0); /* LSB */ + *z_gyr |= ((uint16_t) (SPI_SEND(dev->spi, 0)) << 8); /* MSB */ + + /* Set CS to high which deselects the L3GD20 */ + + SPI_SELECT(dev->spi, dev->config->spi_devid, false); + + /* Unlock the SPI bus */ + + SPI_LOCK(dev->spi, false); +} + + +/**************************************************************************** + * Name: l3gd20_read_temperature + ****************************************************************************/ +static void l3gd20_read_temperature(FAR struct l3gd20_dev_s* dev, + uint8_t* temperature) +{ + /* Lock the SPI bus so that only one device can access it at the same time */ + + SPI_LOCK(dev->spi, true); + + /* Set CS to low which selects the L3GD20 */ + + SPI_SELECT(dev->spi, dev->config->spi_devid, true); + + /* Transmit the register address from where we want to start reading + * 0x80 MSB is set -> Read Indication + */ + + SPI_SEND(dev->spi, (L3GD20_OUT_TEMP_REG | 0x80)); + + /* RX */ + + *temperature = (SPI_SEND(dev->spi, 0)); + + /* Set CS to high which deselects the L3GD20 */ + + SPI_SELECT(dev->spi, dev->config->spi_devid, false); + + /* Unlock the SPI bus */ + + SPI_LOCK(dev->spi, false); +} + +/**************************************************************************** + * Name: l3gd20_interrupt_handler + ****************************************************************************/ + +static int l3gd20_interrupt_handler(int irq, FAR void* context) +{ + /* This function should be called upon a rising edge on the L3GD20 new data + * interrupt pin since it signals that new data has been measured. + */ + + FAR struct l3gd20_dev_s *priv = 0; + int ret; + + /* Find out which L3GD20 device caused the interrupt */ + + for (priv = g_l3gd20_list; + priv && priv->config->irq != irq; + priv = priv->flink) + { + DEBUGASSERT(priv != NULL); + } + + /* Task the worker with retrieving the latest sensor data. We should not do + * this in a interrupt since it might take too long. Also we cannot lock the + * SPI bus from within an interrupt. + */ + + DEBUGASSERT(priv->work.worker == NULL); + ret = work_queue(HPWORK, &priv->work, l3gd20_worker, priv, 0); + if (ret < 0) + { + snerr("ERROR: Failed to queue work: %d\n", ret); + return ret; + } + + return OK; +} + +/**************************************************************************** + * Name: l3gd20_worker + ****************************************************************************/ + +static void l3gd20_worker(FAR void *arg) +{ + FAR struct l3gd20_dev_s *priv = (FAR struct l3gd20_dev_s *)(arg); + DEBUGASSERT(priv != NULL); + + /* Read out the latest sensor data */ + + l3gd20_read_measurement_data(priv); +} + +/**************************************************************************** + * Name: l3gd20_open + ****************************************************************************/ + +static int l3gd20_open(FAR struct file *filep) +{ + FAR struct inode *inode = filep->f_inode; + FAR struct l3gd20_dev_s *priv = inode->i_private; + +#ifdef CONFIG_DEBUG_SENSORS_INFO + uint8_t reg_content; + uint8_t reg_addr; +#endif + + DEBUGASSERT(priv != NULL); + + /* Perform a reset */ + + l3gd20_reset(priv); + + /* Enable DRDY signal on INT 2 */ + + l3gd20_write_register(priv, + L3GD20_CTRL_REG_3, + L3GD20_CTRL_REG_3_I2_DRDY_bm); + + /* Enable the maximum full scale mode. + * Enable block data update for gyro sensor data. + * This should prevent race conditions when reading sensor data. + */ + + l3gd20_write_register(priv, + L3GD20_CTRL_REG_4, + L3GD20_CTRL_REG_4_BDU_bm | + L3GD20_CTRL_REG_4_FS_1_bm | + L3GD20_CTRL_REG_4_FS_0_bm); + + /* Enable X,Y,Z axis + * DR=00 -> Output data rate = 95 Hz, Cut-off = 12.5 + */ + + l3gd20_write_register(priv, + L3GD20_CTRL_REG_1, + L3GD20_CTRL_REG_1_POWERDOWN_bm | + L3GD20_CTRL_REG_1_X_EN_bm | + L3GD20_CTRL_REG_1_Y_EN_bm | + L3GD20_CTRL_REG_1_Z_EN_bm); + + /* Read measurement data to ensure DRDY is low */ + + l3gd20_read_measurement_data(priv); + + /* Read back the content of all control registers for debug purposes */ + +#ifdef CONFIG_DEBUG_SENSORS_INFO + reg_content = 0; + + l3gd20_read_register(priv, L3GD20_WHO_AM_I, ®_content); + sninfo("WHO_AM_I_REG = %04x\n", reg_content); + + for (reg_addr = L3GD20_CTRL_REG_1; + reg_addr <= L3GD20_CTRL_REG_5; + reg_addr++) + { + l3gd20_read_register(priv, reg_addr, ®_content); + sninfo("R#%04x = %04x\n", reg_addr, reg_content); + } + + l3gd20_read_register(priv, L3GD20_STATUS_REG, ®_content); + sninfo("STATUS_REG = %04x\n", reg_content); +#endif + + return OK; +} + + +/**************************************************************************** + * Name: l3gd20_close + ****************************************************************************/ + +static int l3gd20_close(FAR struct file *filep) +{ + FAR struct inode *inode = filep->f_inode; + FAR struct l3gd20_dev_s *priv = inode->i_private; + + DEBUGASSERT(priv != NULL); + + /* Perform a reset */ + + l3gd20_reset(priv); + + return OK; +} + +/**************************************************************************** + * Name: l3gd20_read + ****************************************************************************/ + +static ssize_t l3gd20_read(FAR struct file *filep, FAR char *buffer, + size_t buflen) +{ + FAR struct inode *inode = filep->f_inode; + FAR struct l3gd20_dev_s *priv = inode->i_private; + FAR struct l3gd20_sensor_data_s *data; + int ret; + + DEBUGASSERT(priv != NULL); + + /* Check if enough memory was provided for the read call */ + + if (buflen < sizeof(FAR struct l3gd20_sensor_data_s)) + { + snerr("ERROR: Not enough memory for reading out a sensor data sample\n"); + return -ENOSYS; + } + + /* Aquire the semaphore before the data is copied */ + + ret = sem_wait(&priv->datasem); + if (ret < 0) + { + int errcode = errno; + snerr("ERROR: Could not aquire priv->datasem: %d\n", errcode); + return -errcode; + } + + /* Copy the sensor data into the buffer */ + + data = (FAR struct l3gd20_sensor_data_s *)buffer; + memset(data, 0, sizeof(FAR struct l3gd20_sensor_data_s)); + + data->x_gyr = priv->data.x_gyr; + data->y_gyr = priv->data.y_gyr; + data->z_gyr = priv->data.z_gyr; + data->temperature = priv->data.temperature; + + /* Give back the semaphore */ + + sem_post(&priv->datasem); + + return sizeof(FAR struct l3gd20_sensor_data_s); +} + + +/**************************************************************************** + * Name: l3gd20_write + ****************************************************************************/ + +static ssize_t l3gd20_write(FAR struct file *filep, FAR const char *buffer, + size_t buflen) +{ + return -ENOSYS; +} + +/**************************************************************************** + * Name: l3gd20_ioctl + ****************************************************************************/ + +static int l3gd20_ioctl(FAR struct file *filep, int cmd, unsigned long arg) +{ + int ret = OK; + + switch (cmd) + { + /* @TODO */ + + /* Command was not recognized */ + + default: + snerr("ERROR: Unrecognized cmd: %d\n", cmd); + ret = -ENOTTY; + break; + } + + return ret; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: l3gd20_register + * + * Description: + * Register the L3DF20 character device as 'devpath'. + * + * Input Parameters: + * devpath - The full path to the driver to register, e.g., "/dev/gyr0". + * spi - An SPI driver instance. + * config - configuration for the L3GD20 driver. + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure. + * + ****************************************************************************/ + +int l3gd20_register(FAR const char *devpath, FAR struct spi_dev_s *spi, + FAR struct l3gd20_config_s *config) +{ + FAR struct l3gd20_dev_s *priv; + int ret = OK; + + /* Sanity check */ + + DEBUGASSERT(spi != NULL); + DEBUGASSERT(config != NULL); + + /* Initialize the L3GD20 device structure */ + + priv = (FAR struct l3gd20_dev_s *)kmm_malloc(sizeof(struct l3gd20_dev_s)); + if (priv == NULL) + { + snerr("ERROR: Failed to allocate instance\n"); + ret = -ENOMEM; + goto errout; + } + + priv->spi = spi; + priv->config = config; + priv->work.worker = NULL; + + priv->data.x_gyr = 0; + priv->data.y_gyr = 0; + priv->data.z_gyr = 0; + priv->data.temperature = 0; + + /* Initialize sensor data access semaphore */ + + sem_init(&priv->datasem, 0, 1); + + /* Setup SPI frequency and mode */ + + SPI_SETFREQUENCY(spi, L3GD20_SPI_FREQUENCY); + SPI_SETMODE(spi, L3GD20_SPI_MODE); + + /* Attach the interrupt handler */ + + ret = priv->config->attach(priv->config, &l3gd20_interrupt_handler); + if (ret < 0) + { + snerr("ERROR: Failed to attach interrupt\n"); + goto errout; + } + + /* Register the character driver */ + + ret = register_driver(devpath, &g_l3gd20_fops, 0666, priv); + if (ret < 0) + { + snerr("ERROR: Failed to register driver: %d\n", ret); + kmm_free(priv); + sem_destroy(&priv->datasem); + goto errout; + } + + /* Since we support multiple L3GD20 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_l3gd20_list; + g_l3gd20_list = priv; + + errout: + return ret; +} + +#endif /* CONFIG_SPI && CONFIG_SENSORS_L3GD20 */ diff --git a/include/nuttx/sensors/l3gd20.h b/include/nuttx/sensors/l3gd20.h new file mode 100644 index 00000000000..dbd657c8144 --- /dev/null +++ b/include/nuttx/sensors/l3gd20.h @@ -0,0 +1,296 @@ +/**************************************************************************** + * drivers/sensors/l3gd20.c + * + * Authors: Mateusz Szafoni + * + * 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_L3GD20_H +#define __INCLUDE_NUTTX_SENSORS_L3GD20_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include + +#if defined(CONFIG_SPI) && defined(CONFIG_SENSORS_L3GD20) + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* SPI BUS PARAMETERS ********************************************************/ + +#define L3GD20_SPI_FREQUENCY (4000000) /* 4 MHz */ +#define L3GD20_SPI_MODE (SPIDEV_MODE3) /* Device uses SPI Mode 3: CPOL=1, CPHA=1 * + +/* Register Addresses *******************************************************/ +/* Gyroscope registers */ + +#define L3GD20_WHO_AM_I 0x0F /* Accelerometer and gyroscope device identification */ +#define L3GD20_CTRL_REG_1 0x20 /* Gyroscope control register 1 */ +#define L3GD20_CTRL_REG_2 0x21 /* Gyroscope control register 2 */ +#define L3GD20_CTRL_REG_3 0x22 /* Gyroscope control register 3 */ +#define L3GD20_CTRL_REG_4 0x23 /* Gyroscope control register 4 */ +#define L3GD20_CTRL_REG_5 0x24 /* Gyroscope control register 5 */ +#define L3GD20_REF_REG 0x25 /* Gyroscope reference value for interrupt generation */ +#define L3GD20_OUT_TEMP_REG 0x26 /* Temperature data */ +#define L3GD20_STATUS_REG 0x27 /* Status register */ +#define L3GD20_OUT_X_L_REG 0x28 /* Gyroscope pitch (X) low byte */ +#define L3GD20_OUT_X_H_REG 0x29 /* Gyroscope pitch (X) high byte */ +#define L3GD20_OUT_Y_L_REG 0x2A /* Gyroscope roll (Y) low byte */ +#define L3GD20_OUT_Y_H_REG 0x2B /* Gyroscope roll (Y) high byte */ +#define L3GD20_OUT_Z_L_REG 0x2C /* Gyroscope yaw (Z) low byte */ +#define L3GD20_OUT_Z_H_REG 0x2D /* Gyroscope yaw (Z) high byte */ +#define L3GD20_FIFO_CTRL_REG 0x2E /* FIFO control register */ +#define L3GD20_FIFO_SRC_REG 0x2f /* FIFO status control register */ +#define L3GD20_INT_GEN_CFG_REG 0x30 /* Gyroscope interrupt configuration */ +#define L3GD20_INT_GEN_SRC_REG 0x31 /* Gyroscope interrupt source */ +#define L3GD20_INT_GEN_THS_X_H_REG 0x32 /* Gyroscope pitch (X) interrupt threshold high byte */ +#define L3GD20_INT_GEN_THS_X_L_REG 0x33 /* Gyroscope pitch (X) interrupt threshold low byte */ +#define L3GD20_INT_GEN_THS_Y_H_REG 0x34 /* Gyroscope roll (Y) interrupt threshold high byte */ +#define L3GD20_INT_GEN_THS_Y_L_REG 0x35 /* Gyroscope roll (Y) interrupt threshold low byte */ +#define L3GD20_INT_GEN_THS_Z_H_REG 0x36 /* Gyroscope yaw (Z) interrupt threshold high byte */ +#define L3GD20_INT_GEN_THS_Z_L_REG 0x37 /* Gyroscope yaw (Z) interrupt threshold low byte */ +#define L3GD20_INT_GEN_DUR_REG 0x38 /* Gyroscope interrupt duration */ + + +/* Register Bit Definitions *************************************************/ + +/* Device identification register */ + +#define L3GD20_WHO_AM_I_VALUE 0xD4 + +/* Gyroscope control register 1 */ + +#define L3GD20_CTRL_REG_1_X_EN_bm (1 << 0) +#define L3GD20_CTRL_REG_1_Y_EN_bm (1 << 1) +#define L3GD20_CTRL_REG_1_Z_EN_bm (1 << 2) +#define L3GD20_CTRL_REG_1_POWERDOWN_bm (1 << 3) +#define L3GD20_CTRL_REG_1_BW_0_bm (1 << 4) +#define L3GD20_CTRL_REG_1_BW_1_bm (1 << 5) +#define L3GD20_CTRL_REG_1_DR_0_bm (1 << 6) +#define L3GD20_CTRL_REG_1_DR_1_bm (1 << 7) + +/* Gyroscope control register 2 */ + +#define L3GD20_CTRL_REG_2_HPCF_0_bm (1 << 0) +#define L3GD20_CTRL_REG_2_HPCF_1_bm (1 << 1) +#define L3GD20_CTRL_REG_2_HPCF_2_bm (1 << 2) +#define L3GD20_CTRL_REG_2_HPCF_3_bm (1 << 3) +#define L3GD20_CTRL_REG_2_HPM_0_bm (1 << 4) +#define L3GD20_CTRL_REG_2_HPM_1_bm (1 << 5) +#define L3GD20_CTRL_REG_2_RES6_ (1 << 6) +#define L3GD20_CTRL_REG_2_RES7_ (1 << 7) + +/* Gyroscope control register 3 */ + +#define L3GD20_CTRL_REG_3_I2_EMPTY_bm (1 << 0) +#define L3GD20_CTRL_REG_3_I2_ORUN_bm (1 << 1) +#define L3GD20_CTRL_REG_3_I2_WTM_bm (1 << 2) +#define L3GD20_CTRL_REG_3_I2_DRDY_bm (1 << 3) +#define L3GD20_CTRL_REG_3_PP_OD_bm (1 << 4) +#define L3GD20_CTRL_REG_3_H_LACTIVE_bm (1 << 5) +#define L3GD20_CTRL_REG_3_I1_BOOT_bm (1 << 6) +#define L3GD20_CTRL_REG_3_I1_INT1_bm (1 << 7) + + +/* Gyroscope control register 4 */ + +#define L3GD20_CTRL_REG_4_SIM_bm (1 << 0) +#define L3GD20_CTRL_REG_4_RES1_ (1 << 1) +#define L3GD20_CTRL_REG_4_RES2_ (1 << 2) +#define L3GD20_CTRL_REG_4_RES3_ (1 << 3) +#define L3GD20_CTRL_REG_4_FS_0_bm (1 << 4) +#define L3GD20_CTRL_REG_4_FS_1_bm (1 << 5) +#define L3GD20_CTRL_REG_4_BLE_bm (1 << 6) +#define L3GD20_CTRL_REG_4_BDU_bm (1 << 7) + +/* Gyroscope control register 5 */ + +#define L3GD20_CTRL_REG_5_OUT_SEL_0_bm (1 << 0) +#define L3GD20_CTRL_REG_5_OUT_SEL_1_bm (1 << 1) +#define L3GD20_CTRL_REG_5_INT1_SEL_0_bm (1 << 2) +#define L3GD20_CTRL_REG_5_INT1_SEL_1_bm (1 << 3) +#define L3GD20_CTRL_REG_5_HP_EN_bm (1 << 4) +#define L3GD20_CTRL_REG_5_RES5_ (1 << 5) +#define L3GD20_CTRL_REG_5_FIFO_EN_bm (1 << 6) +#define L3GD20_CTRL_REG_5_BOOT_bm (1 << 7) + +/* Status register */ + +#define L3GD20_STATUS_REG_X_DA_bm (1 << 0) +#define L3GD20_STATUS_REG_Y_DA_bm (1 << 1) +#define L3GD20_STATUS_REG_Z_DA_bm (1 << 2) +#define L3GD20_STATUS_REG_ZYX_DA_bm (1 << 3) +#define L3GD20_STATUS_REG_X_OR_bm (1 << 4) +#define L3GD20_STATUS_REG_Y_OR_bm (1 << 5) +#define L3GD20_STATUS_REG_Z_OR_bm (1 << 6) +#define L3GD20_STATUS_REG_ZYX_OR_bm (1 << 7) + +/* FIFO control register */ + +#define L3GD20_FIFO_CTRL_WTM_0_bm (1 << 0) +#define L3GD20_FIFO_CTRL_WTM_1_bm (1 << 1) +#define L3GD20_FIFO_CTRL_WTM_2_bm (1 << 2) +#define L3GD20_FIFO_CTRL_WTM_3_bm (1 << 3) +#define L3GD20_FIFO_CTRL_WTM_4_bm (1 << 4) +#define L3GD20_FIFO_CTRL_FM_0_bm (1 << 5) +#define L3GD20_FIFO_CTRL_FM_1_bm (1 << 6) +#define L3GD20_FIFO_CTRL_FM_2_bm (1 << 7) +#define L3GD20_FIFO_CTRL_FMODE_BYPASS (0) +#define L3GD20_FIFO_CTRL_FMODE_FIFO (L3GD20_FIFO_CTRL_FM0) +#define L3GD20_FIFO_CTRL_FMODE_CONT (L3GD20_FIFO_CTRL_FM1) +#define L3GD20_FIFO_CTRL_FMODE_CONT_FIFO (L3GD20_FIFO_CTRL_FM1 | L3GD20_FIFO_CTRL_FM0) +#define L3GD20_FIFO_CTRL_FMODE_BYPASS_CONT (L3GD20_FIFO_CTRL_FM2 | L3GD20_FIFO_CTRL_FM1) + +/* FIFO status control register */ + +#define L3GD20_FIFO_SRC_FSS_0_bm (1 << 0) +#define L3GD20_FIFO_SRC_FSS_1_bm (1 << 1) +#define L3GD20_FIFO_SRC_FSS_2_bm (1 << 2) +#define L3GD20_FIFO_SRC_FSS_3_bm (1 << 3) +#define L3GD20_FIFO_SRC_FSS_4_bm (1 << 4) +#define L3GD20_FIFO_SRC_EMPTY_bm (1 << 5) +#define L3GD20_FIFO_SRC_OVRUN_bm (1 << 6) +#define L3GD20_FIFO_SRC_WTM_bm (1 << 7) + +/* Gyroscope interrupt configuration */ + +#define L3GD20_INT_GEN_CFG_X_L_IE_bm (1 << 0) +#define L3GD20_INT_GEN_CFG_X_H_IE_bm (1 << 1) +#define L3GD20_INT_GEN_CFG_Y_L_IE_bm (1 << 2) +#define L3GD20_INT_GEN_CFG_Y_H_IE_bm (1 << 3) +#define L3GD20_INT_GEN_CFG_Z_L_IE_bm (1 << 4) +#define L3GD20_INT_GEN_CFG_Z_H_IE_bm (1 << 5) +#define L3GD20_INT_GEN_CFG_LIR_bm (1 << 6) +#define L3GD20_INT_GEN_CFG_AOI_bm (1 << 7) + + +/* Gyroscope interrupt source */ + +#define L3GD20_INT_GEN_SRC_X_L_bm (1 << 0) +#define L3GD20_INT_GEN_SRC_X_H_bm (1 << 1) +#define L3GD20_INT_GEN_SRC_Y_L_bm (1 << 2) +#define L3GD20_INT_GEN_SRC_Y_H_bm (1 << 3) +#define L3GD20_INT_GEN_SRC_Z_L_bm (1 << 4) +#define L3GD20_INT_GEN_SRC_Z_H_bm (1 << 5) +#define L3GD20_INT_GEN_SRC_I_A_bm (1 << 6) +#define L3GD20_INT_GEN_SRC_RES7_ (1 << 7) + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* A reference to a structure of this type must be passed to the L3GD20 + * driver. This structure provides information about the configuration + * of the sensor and provides some board-specific hooks. + * + * Memory for this structure is provided by the caller. It is not copied + * by the driver and is presumed to persist while the driver is active. + */ + +struct l3gd20_config_s +{ + /* Since multiple L3GD20 can be connected to the same SPI bus we need + * to use multiple spi device ids which are employed by NuttX to select/ + * deselect the desired L3GD20 chip via their chip select inputs. + */ + + int spi_devid; + + /* The IRQ number must be provided for each L3GD20 device so that + * their interrupts can be distinguished. + */ + + int irq; + + /* Attach the L3GD20 interrupt handler to the GPIO interrupt of the + * concrete L3GD20 instance. + */ + + int (*attach)(FAR struct l3gd20_config_s *, xcpt_t); +}; + +/* Data returned by reading from the L3GD20 is returned in this format. */ + +struct l3gd20_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 */ + int8_t temperature; /* Measurement result for temperature sensor */ +}; + + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +#ifdef __cplusplus +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + + +/**************************************************************************** + * Name: l3gd20_register + * + * Description: + * Register the L3DF20 character device as 'devpath'. + * + * Input Parameters: + * devpath - The full path to the driver to register, e.g., "/dev/gyr0". + * i2c - An SPI driver instance. + * config - configuration for the L3GD20 driver. For details see + * description above. + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure. + * + ****************************************************************************/ + +int l3gd20_register(FAR const char *devpath, FAR struct spi_dev_s *spi, + FAR struct l3gd20_config_s *config); + +#undef EXTERN +#ifdef __cplusplus +} +#endif + +#endif /* CONFIG_SPI && CONFIG_SENSORS_L3GD20 */ +#endif /* __INCLUDE_NUTTX_SENSORS_L3GD20_H */