This commit adds LSM330 SPI and ADXL372 drivers.

Squashed merge of the following:

Author: Gregory Nutt <gnutt@nuttx.org>
    drivers/sensors:  Fix some minor typos in last commit that cause some compile problems.
    drivers/sensors/:  Clean up some coding standard isses in LM330 and ADXL372 drivers.
    drivers/sensors/README.txt:  Clean up some long lines and odd line breaks.
    include/nuttx/sensors:  Completes coding style review of adxl372.h, cluster_driver.h, and lsm330.h.

Author: Bob Feretich <bob.feretich@rafresearch.com>
    Add LSM330 SPI and ADXL372 drivers along with the cluster driver infrastructure.
This commit is contained in:
Bob Feratich 2018-04-07 14:46:54 -06:00 committed by Gregory Nutt
parent 4de1378ceb
commit d893c5b55e
8 changed files with 3650 additions and 6 deletions

View File

@ -178,6 +178,13 @@ config LIS331DL_I2C_FREQUENCY
range 1 100000
depends on LIS331DL
config SENSORS_LSM330SPI
bool "STMicro LSM330 SPI support"
default n
select SPI
---help---
Enable driver support for the STMicro LSM330 on SPI.
config SENSORS_LSM9DS1
bool "STMicro LSM9DS1 support"
default n
@ -303,7 +310,7 @@ config MPL115A_REGDEBUG
Enables register level debug features for the MPL115A
config SENSORS_ADXL345
bool "AnalogDevices ADXL345 Driver"
bool "Analog Devices ADXL345 Driver"
default n
---help---
Enables support for the ADXL345 driver
@ -345,6 +352,13 @@ config ADXL345_REGDEBUG
endif # SENSORS_ADXL345
config SENSORS_ADXL372
bool "Analog Devices ADXL372 Sensor support"
default n
select SPI
---help---
Enable driver support for the Analog Devices ADXL372 Sensor.
config SENSORS_MAX44009
bool "Maxim MAX44009 ALS sensor"
default n

View File

@ -146,6 +146,10 @@ ifeq ($(CONFIG_ADXL345_SPI),y)
CSRCS += adxl345_spi.c
endif
ifeq ($(CONFIG_SENSORS_ADXL372),y)
CSRCS += adxl372.c
endif
ifeq ($(CONFIG_LIS3DSH),y)
CSRCS += lis3dsh.c
endif
@ -170,6 +174,10 @@ ifeq ($(CONFIG_SENSORS_LIS3MDL),y)
CSRCS += lis3mdl.c
endif
ifeq ($(CONFIG_SENSORS_LSM330SPI),y)
CSRCS += lsm330_spi.c
endif
ifeq ($(CONFIG_SENSORS_L3GD20),y)
CSRCS += l3gd20.c
endif

View File

@ -1,4 +1,4 @@
ADXL345
ADXL345 (Alan Carvalho de Assis)
=======
The ADXL345 accelerometer can operate in I2C or SPI mode. To operate in I2C
@ -7,10 +7,458 @@ mode just connect the CS pin to Vddi/o.
In order to operate in SPI mode CS need to use connected to microcontroller,
it cannot leave unconnected.
In SPI mode it works with clock polarity (CPOL) = 1 and clock phase (CPHA) = 1.
In SPI mode it works with clock polarity (CPOL) = 1 and clock phase (CPHA)
= 1.
MPL115A
ADXL372 (Bob Feretich)
=======
This driver has support only for MPL115A1 (SPI), but support to MPL115A2 (I2C) can
be added easily.
The ADXL372 is a 200g tri-axis accelerometer that is capable of detecting
and recording shock impact impact events. Recording trigger
characteristics are programed into the sensor via multiple threshold and
duration registers. The ADXL372 is a SPI only device that can transfer
data at 10 MHz. The data transfer performance of this part permits the
sensor to be sampled "on demand" rather than periodically sampled by a
worker task.
See the description of the "Common Sensor Register Interface" below for more
details. It also implements the "Sensor Cluster Driver Interface".
LSM330_SPI (Bob Feretich)
==========
The LSM330 consists of a multi-range tri-axis accelerometer and a
multi-range tri-axis gyroscope. The tri-axis accelerometer features two
state machines that can be firmware programmed for event detection. The
tri-axis gyroscope features threshold and duration registers for event
detection.
This driver supports the LSM330 in SPI mode. In this mode, the LSM330
that can transfer data at 10 MHz. The data transfer performance of
this part permits the sensor to be sampled "on demand" rather than
periodically sampled by a worker task. See the description of the "Common
Sensor Register Interface" below for more details. It also implements the
"Sensor Cluster Driver Interface".
MPL115A (Alan Carvalho de Assis)
=======
This driver has support only for MPL115A1 (SPI), but support to MPL115A2
(I2C) can be added easily.
Common Sensor Register Interface (Bob Feretich)
================================
Background and problem statement:
The capabilities and performance of modern sensors have grown tremendously.
Most sensors are now capable of some degree of autonomous behavior and
several permit the user to load firmware into them and perform as
nanocontrollers. Other sensors have very sophisticated built-in digital
filters that can be programmed with hundreds of parameters.
Currently most sensor drivers in the Nuttx drivers/sensors
directory implement file_ops open(), close(), and read() functions.
The open() function initializes the sensor and places it in a mode where
it can transfer live data in a default configuration. The close() function
places the sensor in a low power shutdown mode. The read() function
returns the most recent data sample from the sensor's most used data
output registers. The write() function is rarely implemented and when it
is there is no consistency in its use. The lseek() and poll() functions
seem to be completely ignored. This results in the sensors being operated
in only their most primitive modes using a fixed "default configuration".
To work around this problem sensor drivers have implemented ioctl()
functions to perform configuration, program the sensor, and manage
autonomous activity. Ioctls provide a method where user programs can
tunnel through a high level driver to access and control device specific
features. The problem with using ioctls is that before the ioctl interface
can be used, the sensor driver must be opened; and the open() function
causes the driver to start performing these primitive actions, so before
ioctls can manage the drivers as desired, ioctls must first be used to
undo the generic actions caused by the open() function. Another major
issue is that there is no consistency from sensor to sensor on ioctl
definitions, not even for the most common sensor actions like writing a
sensor control register or reading a sensor status register.
Purpose:
The purpose of the "Common Sensor Register Interface" is to implement a
consistent and more useful definition of file_ops interface and to make the
file_ops open() function more flexible in establishing the initial
operational state of the sensor. Compatibility for user applications that
implement the current open(), close(), read() interface will be
maintained; and the much greater capabilities of modern sensors will
become accessible through this interface.
Scope:
Applicable to I2C and SPI attached sensors, and some serial port attached
sensors.
The file_ops interface definition:
open(): This function performs the below actions...
1) Reads the sensors ID register. If the sensor responds with an
unexpected value, then...
a) The driver's write() function is disabled.
b) The open function initializes the driver instance, so
that read() and lseek() operations may be performed to enable
problem diagnoses, but the sensor hardware is not initialized.
(No write operations are performed to the sensor.)
c) The errno global variable is set to positive ENODEV
("No such device").
d) The open() function returns successfully with a file_handle.
Note that the calling routine should clear errno before
calling open(). (The file_ops rules prevent drivers from
setting errno to zero.)
2) The other file_ops functions are enabled.
3) The driver's "current reg address" state variable is set to the
sensor's first sensor data output register. (This will make
calls to read() return live sensor data and maintain compatibility
with existing user programs.)
4) If the driver supports a default worker task and an interrupt
handler is specified by in the sensor configuration structure, then
the default worker task is bound to the default worker task.
5) The sensor configuration structure (that was provided to the driver
registration function) is examined to determine whether a custom
sensor configuration is specified. (The custom configuration is
basically an array of (device_reg_address, value) pairs that are
written to the sensor via "single register write" operations.
If a custom sensor configuration was specified, then the that
configuration is written to the sensor, otherwise the "default
sensor configuration" is written to the sensor.
(A side effect of writing this data may result in interrupts
occurring and data being transferred to/from the worker task.)
6) The open() function returns successfully with a file_handle.
close(): This function stops sensor activity and places it in a low
power mode. The file_ops interface functions are disabled for this
instance of the sensor driver. (Except for open())
read(): The action of this function is dependent on whether a "default
worker task" is running and the value of the driver's "current reg
address" state variable.
If a "default worker task" is running,
AND the driver's "current reg address" is equal to the value of
the first sensor data output register,
AND the number of bytes to be read is less than or equal to the
number of bytes in a "default worker task" sample,
Then data is copied from the "default worker task's" sample memory to
the caller's provided buffer.
Otherwise, this function transfers data from sensor registers to the
data buffer provided by the caller. The first byte read is from the
sensor register address specified by the sensor's "current reg
address". The addresses of subsequent bytes to be read are context
sensitive. If more than bus transfer is needed to complete the read,
then a "multi-byte" (sometimes called "burst mode") data transfer
will be used to fill the buffer.
See the sensor's datasheet to determine the auto-increment
behavior of a "multi-byte" data transfers.
Note: That most sensors collect only a few bytes of data per sample.
Small data transfers occurring over a high speed bus (like SPI and some
high speed i2c and serial interfaces) are much more efficient when
collected directly from the sensor hardware than by using a worker task
as an intermediary.
write(): This function transfers data from the data buffer provided by
the caller to sensor registers. The first byte written is to the
sensor register address specified by the sensor's "current reg
address". The addresses of subsequent bytes to be read are context
sensitive. If more than bus transfer is needed to complete the write,
then a "multi-byte" (sometimes called "burst mode") data
transfer will be used to transfer data from the buffer.
See the sensor's datasheet to determine the auto-increment
behavior of a "multi-byte" data transfers.
Note: If write() function was disabled, then no writes will be performed
and the function will return 0 (characters transferred) and errno
is set to -EROFS ("read-only file system").
lseek(): This function sets the value of the sensor's "current reg address"
(seek_address). The open() function initializes the "current reg address"
to the first sensor data output register, so unless the user needs
to change the sensor configuration, lseek() does not need to be
called. Neither read() nor write() change the sensor's "current reg
address".
The definition of lseek is...
off_t lseek(int fd, off_t offset, int whence);
For whence == SEEK_SET, the sensor's "current reg address" will be set
to offset.
For whence == SEEK_CUR, offset will be added to the sensor's "current
reg address".
For whence == SEEK_END, offset is ignored and the sensor's "current
reg address" is set to the first sensor data output register.
lseek() will return an error if the resulting "current reg address"
is invalid for the sensor.
ioctl(): Ioctls() may still be used and this interface make no attempt to
regulate them. But, it is expected that far fewer ioctls will be needed.
The above interface can be used to fully configure a sensor to the needs
of an application, including the ability to load firmware into sensor
state machines
Sensor Cluster Driver Interface:(Bob Feretich)
===============================
Background and problem statement:
Most microcontrollers can support SPI bus transfers at 8 MHz or greater.
Most SPI attached sensors can support a 10 MHz SPI bus. Most tri-axis
accelerometers, tri-axis gyroscopes, or tri-axis magnetometers use only 6
bytes per sample. Many sensors use less than 6 bytes per sample. On an 8
MHz SPI bus it takes about 8 microseconds to transfer a 6 byte sample.
(This time includes a command byte, 6 data bytes, and chip select select
setup and hold.) So, for the below discussion keep in mind that the sensor
sample collection work we want to perform should ideally take 8 microseconds
per sample.
The drivers in the drivers/sensors directory support only the user space
file_ops interface (accessing drivers through the POSIX open/read/close
functions using a file descriptor). Also these drivers typically start
their own worker task to perform sensor data collection, even when their
sensors only transfer a few bytes of data per sample and those transfers
are being made over a high performance bus.
Using the current implementation...
1) A sensor "data ready" or timer interrupt occurs.
2) Context is saved and and the driver's interrupt handler is scheduled
to run.
3) The Nuttx scheduler dispatches the driver's interrupt handler task.
4) The driver's interrupt handler task posts to a semaphore that the
driver's worker task is waiting on.
5) Nuttx restores the context for the driver's worker task and starts it
running.
6) The driver's worker task starts the i/o to collect the sample.) (This is
where the 8 microseconds of real work gets performed.) And waits on a
SPI data transfer complete semaphore.
7) The Nuttx saves the context of the driver's worker task, and the
scheduler dispatches some other task to run while we are waiting.
Note that this is a good thing. This task is probably performing some
other real work. We want this to happen during the data transfer.
8) The completion of the data transfer causes an interrupt. Nuttx saves the
current context and restores the driver's worker task's context.
9) The driver's worker task goes to sleep waiting on the semaphore for the
next sensor "data ready" or timer interrupt.
10) The Nuttx saves the context of the driver's worker task, and the
scheduler dispatches some other task to run while we are waiting.
Independently with the above...
a) The sensor application program performs a file_ops read() to collect a
sample.
b) The Nuttx high level driver receives control, performs a thin layer of
housekeeping and calls the sensor driver's read function.
c) The sensor driver's read function copies the most recent sample from the
worker task's data area to the application's buffer and returns.
d) The Nuttx high level driver receives control, performs a thin layer of
housekeeping and returns.
e) The application processes the sample.
Using a 216 MHz STM32F7 with no other activity occurring, we have timed the
above the elapsed time for the above to be on average 45 microseconds.
Most sensor applications process data from multiple sensors. (An 9-DoF IMU
is typically represented as three sensors (accelerometer, gyroscope, and
magnetometer). In this case there are three copies of 1-10 occurring in
parallel.
In applications where live data is being used, the context switch
thrashing and cache pollution of this approach cripples system
performance. In applications where sensor FIFO data is being used and
therefore a large amount of data is collected per iteration, the non "zero
copy" nature of the data collection becomes a performance issue.
Purpose:
The "Sensor Cluster Driver Interface" provides a standard mechanism for
an application to collect data from multiple sensor drivers in a much more
efficient manner. It significantly reduces the number of running tasks and
the context thrashing and cache pollution caused by them. It also permits
"zero copy" collection of sensor data.
The Sensor Cluster Driver Interface uses a single "worker task" to be shared
by an arbitrary number of drivers. This shared worker task is a kernel
task that is registered like a driver, supports a driver interface to
application programs, and collects data from multiple sensors (a cluster of
sensors), we refer to it a "Sensor Cluster Driver".
Its goal is to change the sequence of events detailed above to...
1) A sensor "data ready" or timer interrupt occurs.
2) Context is saved and and the cluster driver's interrupt handler is
scheduled to run.
3) The Nuttx scheduler dispatches the cluster driver's interrupt handler
task.
4) The cluster driver's interrupt handler task posts to a semaphore that
the cluster driver's worker task is waiting on.
5) Nuttx restores the context for the driver's worker task and starts it
running.
6) The cluster driver's worker task starts the i/o to collect the sample.
There are two choices here. Programed I/O (PIO) or DMA. If PIO is
fastest for a small sample size, but it will lock up the processor for
the full duration of the transfer; it can only transfer from one
sensor at a time; and the worker task should manually yield control
occasionally to permit other tasks to run. DMA has higher start and
completion overhead, but it is much faster for long transfers, can
perform simultaneous transfers from sensors on different buses, and
automatically releases the processor while the transfer is occurring.
For this reason our drivers allows the worker task to choose between
PIO (driver_read()) and DMA (driver_exchange()), a common extension to
the sensor_cluster_operations_s structure. So either way after one or
more transfers we yield control and move to the next step. Note that
the data is being transferred directly into the buffer provided by the
application program; so no copy needs to be performed.
7) The Nuttx saves the context of the cluster driver's worker task, and the
scheduler dispatches some other task to run while we are waiting.
Again note that this is a good thing. This task is probably performing
some other real work. We want this to happen during the data transfer.
8) The completion of the last of the previous data transfers causes an
interrupt. Nuttx saves the current context and restores the cluster
driver's worker task's context. If there is more sensor data to
collect, then goto Step 6. Otherwise it posts to a semaphore that
will wake the application.
9) The driver's worker task goes to sleep waiting on the semaphore for the
next sensor "data ready" or timer interrupt.
10) The Nuttx saves the context of the driver's worker task, and the
scheduler dispatches some other task to run while we are waiting.
Independently with the above...
a) The sensor application program performs a file_ops read() to collect a
sample.
b) The Nuttx high level driver receives control, performs a thin layer of
housekeeping and calls the sensor driver's read function.
c) The sensor driver's read function copies the most recent sample from the
worker task's data area to the application's buffer and returns.
d) The Nuttx high level driver receives control, performs a thin layer of
housekeeping and returns.
e) The application processes the sample.
So when collecting data from three sensors, this mechanism saved...
* the handling of 2 sensor "data ready" or timer interrupts (Steps 1 - 4).
* 2 occurrences of waking and scheduling of a worker task (Step 5).
* 2 context switches to other tasks (Step 9 & 10)
* if the three sensors were on separate buses, then 2 occurrences of
Steps 6 - 8 could have also been saved.
* An extra copy operation of the collected sensor data.
* The cache pollution caused by 2 competing worker tasks.
Definitions:
Leaf Driver - a kernel driver that implements the "Sensor Cluster Driver
Interface" so that it can be called by Cluster drivers.
Cluster Driver - a kernel driver that uses the "Sensor Cluster Driver
Interface" to call leaf drivers.
Entry-Point Vector - an array of function addresses to which a leaf driver
will permit calls by a Cluster Driver.
Leaf Driver Instance Handle - a pointer to an opaque Leaf Driver structure
that identifies an instance of the leaf driver. Leaf Drivers store this
handle in its configuration structure during registration.
Sensor Cluster Interface description:
* The definition of an entry-point vector. This is similar to the
entry-point vector that is provided to the file-ops high level driver.
This entry-point vector must include the sensor_cluster_operations_s
structure as its first member.
* The the definition of an driver entry-point vector member in the leaf
driver's configuration structure. The leaf driver registration function
must store the address of its entry-point vector in this field.
* The the definition of an instance handle member in the leaf drivers
configuration structure. The leaf driver registration function must store
a handle (opaque pointer) to the instance of the leaf driver being
registered in this field. Note that this should be the same handle that
the leaf driver supplies to Nuttx to register itself. The cluster driver
will include this handle as a parameter in calls made to the leaf driver.
struct sensor_cluster_operations_s
{
CODE int (*driver_open)(FAR void *instance_handle, int32_t arg);
CODE int (*driver_close)(FAR void *instance_handle, int32_t arg);
CODE ssize_t (*driver_read)(FAR void *instance_handle, FAR char *buffer,
size_t buflen);
CODE ssize_t (*driver_write)(FAR void *instance_handle,
FAR const char *buffer, size_t buflen);
CODE off_t (*driver_seek)(FAR void *instance_handle, off_t offset,
int whence);
CODE int (*driver_ioctl)(FAR void *instance_handle, int cmd,
unsigned long arg);
CODE int (*driver_suspend)(FAR void *instance_handle, int32_t arg);
CODE int (*driver_resume)(FAR void *instance_handle, int32_t arg);
};
Note that the sensor_cluster_operations_s strongly resembles the Nuttx fs.h
file_operations structures. This permits the current file_operations
functions to become thin wrappers around these functions.
driver_open(): Same as the fs.h open() except that arg can be specify
permitting more flexibility in sensor configuration and initial operation.
when arg = 0 the function of driver_open() must be identical to open().
driver_close(): Same as the fs.h close() except that arg can be specify
permitting more flexibility in selecting a sensor low power state.
when arg = 0 the function of driver_close() must be identical to close().
driver_read(): Same as the fs.h read().
driver_write(): Same as the fs.h write(). Optional. Set to NULL if not
supported.
driver_seek(): Same as the fs.h seek(). Optional. Set to NULL if not
supported.
driver_ioctl(): Same as the fs.h ioctl(). Optional. Set to NULL if not
supported.
driver_suspend() and driver_resume(): Optional. Set to NULL if not
supported. It is common for sensor applications to conserve power and
send their microcontroller into a low power sleep state. It seems
appropriate to reserve these spots for future use. These driver entry
points exist in Linux and Windows. Since microcontrollers and sensors
get more capable every year, there should soon be a requirement for
these entry points. Discussion on how to standardize their use and
implementation should
be taken up independently from this driver document.
Note that all drivers are encouraged to extend their entry-point vectors
beyond this common segment. For example it may be beneficial for the
worker task to select between programed i/o and DMA data transfer
routines. Unregulated extensions to the Entry-Point Vector should be
encouraged to maximize the benefits of a sensor's features.
Operation:
Board logic (configs directory) will register the cluster driver. The
cluster driver will register the leaf drivers that it will call.
This means that the cluster driver has access to the leaf driver's
configuration structures and can pass the Leaf Driver Instance Handle to
the leaf driver as a parameter in calls made via the Entry-Point Vector.
Either board logic or an application program may open() the cluster
driver. The cluster driver open() calls the open() function of the leaf
drivers. The cluster driver open() or read() function can launch the
shared worker task that collects the data.
The cluster driver close() function calls the close functions of the leaf
drivers.

905
drivers/sensors/adxl372.c Normal file
View File

@ -0,0 +1,905 @@
/****************************************************************************
* drivers/sensors/adxl372.c
* Character driver for the ST ADXL372 Tri-axis accelerometer and gyroscope.
*
* Copyright (C) 2017-2018 RAF Research LLC. All rights reserved.
* Author: Bob Feretich <bob.feretich@rafresearch.com>
*
* 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 <nuttx/config.h>
#if defined(CONFIG_SPI) && defined(CONFIG_SENSORS_ADXL372) \
&& defined(CONFIG_SPI_EXCHANGE)
#include <errno.h>
#include <debug.h>
#include <string.h>
#include <semaphore.h>
#include <nuttx/kmalloc.h>
#include <nuttx/fs/fs.h>
#include <nuttx/sensors/adxl372.h>
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#define ADXL372_INITIAL_CR_SIZE 7
/****************************************************************************
* 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 adxl372_dev_s
{
FAR struct adxl372_dev_s *flink; /* Supports a singly linked list of
* drivers */
FAR struct spi_dev_s *spi; /* Pointer to the SPI instance */
FAR struct adxl372_config_s *config; /* Pointer to the configuration of the
* ADXL372 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 adxl372_read_register(FAR struct adxl372_dev_s *dev,
uint8_t reg_addr);
static void adxl372_read_registerblk(FAR struct adxl372_dev_s *dev,
uint8_t reg_addr,
FAR uint8_t *reg_data,
uint8_t xfercnt);
static void adxl372_write_register(FAR struct adxl372_dev_s *dev,
uint8_t reg_addr,
uint8_t reg_data);
static void adxl372_write_registerblk(FAR struct adxl372_dev_s *dev,
uint8_t reg_addr,
FAR uint8_t *reg_data,
uint8_t xfercnt);
static void adxl372_reset(FAR struct adxl372_dev_s *dev);
static int adxl372_open(FAR struct file *filep);
static int adxl372_close(FAR struct file *filep);
static ssize_t adxl372_read(FAR struct file *, FAR char *, size_t);
static ssize_t adxl372_write(FAR struct file *filep,
FAR const char *buffer, size_t buflen);
static off_t adxl372_seek(FAR struct file *filep, off_t offset,
int whence);
static int adxl372_ioctl(FAR struct file *filep, int cmd,
unsigned long arg);
static int adxl372_dvr_open(FAR void *instance_handle, int32_t arg);
static int adxl372_dvr_close(FAR void *instance_handle, int32_t arg);
static ssize_t adxl372_dvr_read(FAR void *instance_handle,
FAR char *buffer, size_t buflen);
static ssize_t adxl372_dvr_write(FAR void *instance_handle,
FAR const char *buffer, size_t buflen);
static off_t adxl372_dvr_seek(FAR void *instance_handle, off_t offset,
int whence);
static int adxl372_dvr_ioctl(FAR void *instance_handle, int cmd,
unsigned long arg);
static void adxl372_dvr_exchange(FAR void *instance_handle,
FAR const void *txbuffer,
FAR void *rxbuffer, size_t nwords);
/****************************************************************************
* Private Data
****************************************************************************/
static const struct file_operations g_adxl372_fops =
{
adxl372_open,
adxl372_close,
adxl372_read,
adxl372_write,
adxl372_seek,
adxl372_ioctl
#ifndef CONFIG_DISABLE_POLL
, NULL
#endif
#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
, NULL
#endif
};
static const struct adxl372_dvr_entry_vector_s g_adxl372_dops =
{
/* Standard sensor cluster driver entry-vector */
{
.driver_open = adxl372_dvr_open,
.driver_close = adxl372_dvr_close,
.driver_read = adxl372_dvr_read,
.driver_write = adxl372_dvr_write,
.driver_seek = adxl372_dvr_seek,
.driver_ioctl = adxl372_dvr_ioctl,
.driver_suspend = 0,
.driver_resume = 0,
},
/* adxl372 extensions follow */
.driver_spiexc = adxl372_dvr_exchange,
};
/****************************************************************************
* Private data storage
****************************************************************************/
/* Single linked list to store instances of drivers */
static struct adxl372_dev_s *g_adxl372_list = NULL;
/* Default accelerometer initialization sequence */
/* Configure ADXL372 to read live data (not using FIFO).
* 1. Set to standby mode. The below can't be set while running.
* 2. Configure the FIFO to be bypassed.
* 3. Configure interrupts as disabled, because ADXL372 irpts are used.
* 4. Configure the Output Data Rate (ODR) as 1600 Hz.
* 5. Configure normal mode (vs low noise) and 800Hz bandwidth.
* 6. Set to operational mode; 370ms filter settle; LPF=enb; HPF=dis;
*/
static struct adxl372_reg_pair_s g_initial_adxl372_cr_values[] =
{
/* Set to standby mode */
{
.addr = ADXL372_POWER_CTL,
.value = 0
},
{
.addr = ADXL372_FIFO_CTL,
.value = ADXL372_FIFO_BYPASSED
},
/* Interrupts disabled. */
{
.addr = ADXL372_INT1_MAP,
.value = 0
},
{
.addr = ADXL372_TIMING,
.value = ADXL372_TIMING_ODR1600
},
{
.addr = ADXL372_MEASURE,
.value = ADXL372_MEAS_BW800
},
{
.addr = ADXL372_POWER_CTL,
.value = ADXL372_POWER_HPF_DISABLE | ADXL372_POWER_MODE_MEASURE
}
};
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: adxl372_read_register
****************************************************************************/
static uint8_t adxl372_read_register(FAR struct adxl372_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);
SPI_SETFREQUENCY(dev->spi, ADXL372_SPI_FREQUENCY);
SPI_SETMODE(dev->spi, ADXL372_SPI_MODE);
/* Set CS to low to select the ADXL372 */
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 | ADXL372_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 ADXL372 */
SPI_SELECT(dev->spi, dev->config->spi_devid, false);
/* Unlock the SPI bus */
SPI_LOCK(dev->spi, false);
return reg_data;
}
/******************************************************************************
* Name: adxl372_read_registerblk
******************************************************************************/
static void adxl372_read_registerblk(FAR struct adxl372_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);
SPI_SETFREQUENCY(dev->spi, ADXL372_SPI_FREQUENCY);
SPI_SETMODE(dev->spi, ADXL372_SPI_MODE);
/* Set CS to low to select the ADXL372 */
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 | ADXL372_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 ADXL372 */
SPI_SELECT(dev->spi, dev->config->spi_devid, false);
/* Unlock the SPI bus */
SPI_LOCK(dev->spi, false);
}
/****************************************************************************
* Name: adxl372_write_register
****************************************************************************/
static void adxl372_write_register(FAR struct adxl372_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);
SPI_SETFREQUENCY(dev->spi, ADXL372_SPI_FREQUENCY);
SPI_SETMODE(dev->spi, ADXL372_SPI_MODE);
/* Set CS to low to select the ADXL372 */
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 | ADXL372_WRITE);
/* Transmit the content which should be written into the register */
SPI_SEND(dev->spi, reg_data);
/* Set CS to high to deselect the ADXL372 */
SPI_SELECT(dev->spi, dev->config->spi_devid, false);
/* Unlock the SPI bus */
SPI_LOCK(dev->spi, false);
}
/****************************************************************************
* Name: adxl372_write_registerblk
****************************************************************************/
static void adxl372_write_registerblk(FAR struct adxl372_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);
SPI_SETFREQUENCY(dev->spi, ADXL372_SPI_FREQUENCY);
SPI_SETMODE(dev->spi, ADXL372_SPI_MODE);
/* Set CS to low which selects the ADXL372 */
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 | ADXL372_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 ADXL372 */
SPI_SELECT(dev->spi, dev->config->spi_devid, false);
/* Unlock the SPI bus */
SPI_LOCK(dev->spi, false);
}
/****************************************************************************
* Name: adxl372_reset
*
* Description:
* ADXL Accelerometer Reset
* 1. Make sure that a reset is not in progress.
* 2. Write ADXL372_RESET_VALUE (0x52) to ADXL372_RESET register.
* 3. Wait for the reset to finish.
*
****************************************************************************/
static void adxl372_reset(FAR struct adxl372_dev_s *dev)
{
uint wdcnt = 10;
/* Wait for boot to finish (15 ms error timeout) */
up_mdelay(5);
while (wdcnt > 0 && (0 != adxl372_read_register(dev, ADXL372_RESET)))
{
up_mdelay(1);
}
/* Reset ADXL372 Accelerometer. Write only. Begin a boot. */
adxl372_write_register(dev, ADXL372_RESET, ADXL372_RESET_VALUE);
/* Wait for boot to finish (15 ms error timeout) */
up_mdelay(5);
wdcnt = 10;
while (wdcnt>0 && (0 != adxl372_read_register(dev, ADXL372_RESET)))
{
up_mdelay(1);
}
}
/****************************************************************************
* Name: adxl372_read_id
*
* Description:
*
* Read the ADXL372 Accelerometer's ID Registers.
* There are 4 ID Registers...
*
* Manufacturer should be ADXL372_DEVID_AD_VALUE (0xAD).
* Family should be ADXL372_DEVID_MST_VALUE (0x1D).
* Part ID should be ADXL372_PARTID_VALUE (0xFA, Octal 372)
* Revision is returned, but not expected to be checked.
* All of the above are returned as an uint32_t. Should be 0xAD1DFAxx.
*
****************************************************************************/
static uint32_t adxl372_read_id(FAR struct adxl372_dev_s *dev)
{
union
{
uint32_t adxl_devid32;
uint8_t adxl_devid[4];
} un;
un.adxl_devid[3] = adxl372_read_register(dev, ADXL372_DEVID_AD);
un.adxl_devid[2] = adxl372_read_register(dev, ADXL372_DEVID_MST);
un.adxl_devid[1] = adxl372_read_register(dev, ADXL372_PARTID);
un.adxl_devid[0] = adxl372_read_register(dev, ADXL372_REVID);
return un.adxl_devid32;
}
/****************************************************************************
* Name: adxl372_dvr_open
****************************************************************************/
static int adxl372_dvr_open(FAR void *instance_handle, int32_t arg)
{
FAR struct adxl372_dev_s *priv = (FAR struct adxl372_dev_s *)instance_handle;
FAR struct adxl372_reg_pair_s *initp;
uint32_t pnpid;
int sz;
int ret;
int i;
#ifdef CONFIG_DEBUG_SENSORS_INFO
uint8_t reg_content;
#endif
sninfo("adxl372_open: entered...\n");
DEBUGASSERT(priv != NULL);
UNUSED(arg);
ret = nxsem_trywait(&priv->devicesem);
if (ret < 0)
{
sninfo("INFO: ADXL372 Accelerometer is already opened.\n");
return -EBUSY;
}
/* Read the ID registers */
pnpid = adxl372_read_id(priv);
priv->readonly = false;
sninfo("ADXL372_ID = 0x%08x\n", pnpid);
if ((pnpid & 0xffffff00) != (ADXL372_DEVID_AD_VALUE << 24 |
ADXL372_DEVID_MST_VALUE << 16 |
ADXL372_PARTID_VALUE << 8))
{
snwarn("ERROR: Invalid ADXL372_ID = 0x%08x\n", pnpid);
priv->readonly = true;
set_errno(ENODEV);
}
else /* ID matches */
{
adxl372_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_initial_adxl372_cr_values; /* Default values */
sz = ADXL372_INITIAL_CR_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++)
{
adxl372_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 = adxl372_read_register(priv, ADXL372_FIFO_CTL);
sninfo("ADXL372_FIFO_CTL = 0x%02x\n", reg_content);
reg_content = adxl372_read_register(priv, ADXL372_INT1_MAP);
sninfo("ADXL372_INT1_MAP = 0x%02x\n", reg_content);
reg_content = adxl372_read_register(priv, ADXL372_TIMING);
sninfo("ADXL372_TIMING = 0x%02x\n", reg_content);
reg_content = adxl372_read_register(priv, ADXL372_MEASURE);
sninfo("ADXL372_MEASURE = 0x%02x\n", reg_content);
reg_content = adxl372_read_register(priv, ADXL372_POWER_CTL);
sninfo("ADXL372_POWER_CTL = 0x%02x\n", reg_content);
#endif
}
priv->seek_address = (uint8_t) ADXL372_XDATA_H;
return OK;
}
/****************************************************************************
* Name: adxl372_dvr_close
****************************************************************************/
static int adxl372_dvr_close(FAR void *instance_handle, int32_t arg)
{
FAR struct adxl372_dev_s *priv = (FAR struct adxl372_dev_s *)instance_handle;
DEBUGASSERT(priv != NULL);
UNUSED(arg);
/* Perform a reset to place the sensor in standby mode.*/
adxl372_reset(priv);
/* Release the sensor */
nxsem_post(&priv->devicesem);
return OK;
}
/****************************************************************************
* Name: adxl372_dvr_read
****************************************************************************/
static ssize_t adxl372_dvr_read(FAR void *instance_handle, FAR char *buffer,
size_t buflen)
{
FAR struct adxl372_dev_s *priv = ((FAR struct adxl372_dev_s *)instance_handle);
union
{
int16_t d16;
char d8[2];
} un;
FAR char *p1;
FAR char *p2;
int i;
DEBUGASSERT(priv != NULL);
adxl372_read_registerblk(priv, priv->seek_address, (uint8_t *)buffer,
buflen);
/* Permute accelerometer data out fields */
if (priv->seek_address == ADXL372_XDATA_H && buflen >= 6)
{
p1 = p2 = buffer;
for (i=0; i<3; i++)
{
un.d8[1] = *p1++;
un.d8[0] = *p1++;
un.d16 = un.d16 >> 4;
*p2++ = un.d8[0];
*p2++ = un.d8[1];
}
}
return buflen;
}
/****************************************************************************
* Name: adxl372_dvr_write
****************************************************************************/
static ssize_t adxl372_dvr_write(FAR void *instance_handle,
FAR const char *buffer, size_t buflen)
{
FAR struct adxl372_dev_s *priv = (FAR struct adxl372_dev_s *)instance_handle;
DEBUGASSERT(priv != NULL);
if (priv->readonly)
{
set_errno(EROFS);
return -1;
}
adxl372_write_registerblk(priv, priv->seek_address, (uint8_t *)buffer,
buflen);
return buflen;
}
/****************************************************************************
* Name: adxl372_dvr_seek
****************************************************************************/
static off_t adxl372_dvr_seek(FAR void *instance_handle, off_t offset,
int whence)
{
off_t reg;
FAR struct adxl372_dev_s *priv = (FAR struct adxl372_dev_s *)instance_handle;
DEBUGASSERT(priv != NULL);
switch (whence)
{
case SEEK_CUR: /* Incremental seek */
reg = priv->seek_address + offset;
if (0 > reg || reg > ADXL372_LAST)
{
set_errno(-EINVAL);
return -1;
}
priv->seek_address = reg;
break;
case SEEK_END: /* Seek to the 1st X-data register */
priv->seek_address = ADXL372_XDATA_H;
break;
case SEEK_SET: /* Seek to designated address */
if (0 > offset || offset > ADXL372_LAST)
{
set_errno(-EINVAL);
return -1;
}
priv->seek_address = offset;
break;
default: /* invalid whence */
set_errno(-EINVAL);
return -1;
}
return priv->seek_address;
}
/****************************************************************************
* Name: adxl372_dvr_ioctl
****************************************************************************/
static int adxl372_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: adxl372_dvr_exchange (with SPI DMA capability)
*
* Description:
* Exchange a block of data on SPI using DMA
*
* Input Parameters:
* instance_handle - Pointer to struct adxl372_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 adxl372_dvr_exchange(FAR void *instance_handle,
FAR const void *txbuffer,
FAR void *rxbuffer, size_t nwords)
{
FAR struct adxl372_dev_s *priv = (FAR struct adxl372_dev_s *)instance_handle;
FAR struct spi_dev_s *spi = priv->spi;
/* Lock the SPI bus so that only one device can access it at the same time */
SPI_LOCK(spi, true);
SPI_SETFREQUENCY(spi, ADXL372_SPI_FREQUENCY);
SPI_SETMODE(spi, ADXL372_SPI_MODE);
/* Set CS to low which selects the ADXL372 */
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 ADXL372 */
SPI_SELECT(spi, priv->config->spi_devid, false);
/* Unlock the SPI bus */
SPI_LOCK(spi, false);
}
/****************************************************************************
* Name: adxl372_open
****************************************************************************/
static int adxl372_open(FAR struct file *filep)
{
FAR struct inode *inode = filep->f_inode;
FAR struct adxl372_dev_s *priv = inode->i_private;
int ret;
ret = adxl372_dvr_open((FAR void *)priv, 0);
return ret;
}
/****************************************************************************
* Name: adxl372_close
****************************************************************************/
static int adxl372_close(FAR struct file *filep)
{
FAR struct inode *inode = filep->f_inode;
FAR struct adxl372_dev_s *priv = inode->i_private;
int ret;
ret = adxl372_dvr_close((FAR void *)priv, 0);
return ret;
}
/****************************************************************************
* Name: adxl372_read
****************************************************************************/
static ssize_t adxl372_read(FAR struct file *filep, FAR char *buffer,
size_t buflen)
{
FAR struct inode *inode = filep->f_inode;
FAR struct adxl372_dev_s *priv = inode->i_private;
return adxl372_dvr_read(priv, buffer, buflen);
}
/****************************************************************************
* Name: adxl372_write
****************************************************************************/
static ssize_t adxl372_write(FAR struct file *filep, FAR const char *buffer,
size_t buflen)
{
FAR struct inode *inode = filep->f_inode;
FAR struct adxl372_dev_s *priv = inode->i_private;
return adxl372_dvr_write(priv, buffer, buflen);
}
/****************************************************************************
* Name: adxl372_seek
****************************************************************************/
static off_t adxl372_seek(FAR struct file *filep, off_t offset, int whence)
{
FAR struct inode *inode = filep->f_inode;
FAR struct adxl372_dev_s *priv = inode->i_private;
return adxl372_dvr_seek(priv, offset, whence);
}
/****************************************************************************
* Name: adxl372_ioctl
****************************************************************************/
static int adxl372_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
{
FAR struct inode *inode = filep->f_inode;
FAR struct adxl372_dev_s *priv = inode->i_private;
return adxl372_dvr_ioctl(priv, cmd, arg);
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: adxl372_register
*
* Description:
* Register the ADXL372 character device as 'devpath'
*
* Input Parameters:
* devpath - The full path to the driver to register. E.g., "/dev/acl0"
* spi - An instance of the SPI interface to use to communicate with
* ADXL372
* config - Configuration for the ADXL372 accelerometer driver. For
* details see description above.
*
* Returned Value:
* Zero (OK) on success; a negated errno value on failure.
*
****************************************************************************/
int adxl372_register(FAR const char *devpath,
FAR struct spi_dev_s *spi,
FAR struct adxl372_config_s *config)
{
FAR struct adxl372_dev_s *priv;
int ret;
/* Sanity check */
DEBUGASSERT(spi != NULL);
DEBUGASSERT(config != NULL);
/* Initialize the ADXL372 accelerometer device structure. */
priv = (FAR struct adxl372_dev_s *)kmm_malloc(sizeof(struct adxl372_dev_s));
if (priv == NULL)
{
snerr("ERROR: Failed to allocate accelerometer instance\n");
return -ENOMEM;
}
priv->spi = spi;
priv->config = config;
config->leaf_handle = NULL;
config->sc_ops = NULL;
/* 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, &g_adxl372_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 ADXL372 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_adxl372_list;
g_adxl372_list = priv;
config->leaf_handle = (void *) priv;
config->sc_ops = &g_adxl372_dops;
return OK;
}
#endif /* CONFIG_SPI && CONFIG_SENSORS_ADXL372 && CONFIG_SPI_EXCHANGE */

1434
drivers/sensors/lsm330_spi.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,288 @@
/******************************************************************************
* include/nuttx/sensors/adxl372.h
*
* Copyright (C) 2017-2018 RAF Research LLC. All rights reserved.
* Author: Bob Feretich <bob.feretich@rafresearch.com>
*
* 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_ADXL372_H
#define __INCLUDE_NUTTX_SENSORS_ADXL372_H
/******************************************************************************
* Driver usage notes:
*
* This driver is a "kernel sensor leaf driver" that may be used directly from
* user applications via the file_operations interface or have selected entry
* points called directly from a "kernel sensor cluster driver".
*
* To use this driver via the file_operations interface, the board
* initialization function should call this driver's registration function.
* The driver will register itself with Nuttx under the /dev path that is
* provided by the config structure. Then user applications may access the
* driver via the "file descriptor handle" returned by the file_operations
* open() function.
*
* By default the open() function configures the sensor for:
*
* Output Data Rate (ODR) = 1600 Hz.
* Bandwidth (BW) = 800 Hz.
* Normal mode sampling (as opposed to low power mode sampling).
* The Low Pass Filter is enabled and the High Pass Filter is disabled.
* A filter settling time of 370ms is selected.
*
* If the user desires a different configuration settings, the the user may
* either provide a pointer to an array of "struct adxl372_reg_pair_s" that
* will be applied to to the sensor upon open(); or dynamically use
* the lseek() and write() file_operations functions to set the
* sensor configuration as desired.
*
* When using the sensor from the file_operations interface, the sensor is
* accessed in Programmed I/O (PIO) mode. (i.e. When the read() function is
* executed, the sensor is read on that thread.) PIO reads and writes block
* the calling thread until data is available. Since the sensor is on an SPI
* bus running at near 10 MHz, the read or write operations should only take
* a few microseconds (about a microsecond per byte of data), so for
* individual sensor reads and writes, the overhead of using interrupts or
* DMA is not worthwhile.
*
* This driver supports the Common Sensor Register Interface.
* See drivers/sensors/README.txt for details.
*
* This driver supports the Sensor Cluster Driver Interface.
* See drivers/sensors/README.txt for details.
*
* It also extends the interface by permitting cluster driver calls to
* a function that is intended to perform high performance DMA SPI exchange
* operations. See the usage note on the exchange operation below.
*
****************************************************************************/
/*******************************************************************************
* Included Files
*******************************************************************************
*/
#include <nuttx/irq.h>
#include <nuttx/config.h>
#include <nuttx/fs/ioctl.h>
#include <nuttx/spi/spi.h>
#include <nuttx/sensors/cluster_driver.h>
#if defined(CONFIG_SPI) && defined(CONFIG_SENSORS_ADXL372) \
&& defined(CONFIG_SPI_EXCHANGE)
/*******************************************************************************
* Pre-processor Definitions
*******************************************************************************
*/
/* ADXL372 common definitions */
#define ADXL372_READ 0x01
#define ADXL372_WRITE 0x00
#define ADXL372_ADDR_MASK 0x0fe
/* ADXL372 Accelerometer Register definitions */
#define ADXL372_DEVID_AD (0x00 << 1)
# define ADXL372_DEVID_AD_VALUE 0xad
#define ADXL372_DEVID_MST (0x01 << 1)
# define ADXL372_DEVID_MST_VALUE 0x1D
#define ADXL372_PARTID (0x02 << 1)
# define ADXL372_PARTID_VALUE 0xfa
#define ADXL372_REVID (0x03 << 1)
#define ADXL372_STATUS (0x04 << 1)
#define ADXL372_STATUS2 (0x05 << 1)
#define ADXL372_FIFO_ENTRIES2 (0x06 << 1)
#define ADXL372_FIFO_ENTRIES (0x07 << 1)
#define ADXL372_XDATA_H (0x08 << 1)
#define ADXL372_XDATA_L (0x09 << 1)
#define ADXL372_YDATA_H (0x0a << 1)
#define ADXL372_YDATA_L (0x0b << 1)
#define ADXL372_ZDATA_H (0x0c << 1)
#define ADXL372_ZDATA_L (0x0d << 1)
#define ADXL372_THRESH_ACT_X_H (0x23 << 1)
#define ADXL372_FIFO_CTL (0x3a << 1)
# define ADXL372_FIFO_BYPASSED 0x00
# define ADXL372_FIFO_STREAMED 0x02
#define ADXL372_INT1_MAP (0x3b << 1)
# define ADXL372_INT1_MAP_DR 0x01
# define ADXL372_INT1_MAP_FRDY 0x02
# define ADXL372_INT1_MAP_FFULL 0x04
#define ADXL372_TIMING (0x3d << 1)
# define ADXL372_TIMING_ODR400 (0x0 << 5) /* 400 Hz ODR */
# define ADXL372_TIMING_ODR800 (0x1 << 5) /* 800 Hz ODR */
# define ADXL372_TIMING_ODR1600 (0x2 << 5) /* 1600 Hz ODR */
# define ADXL372_TIMING_ODR3200 (0x3 << 5) /* 3200 Hz ODR */
# define ADXL372_TIMING_ODR6400 (0x4 << 5) /* 6400 Hz ODR */
#define ADXL372_MEASURE (0x3e << 1)
# define ADXL372_MEAS_BW200 0x0 /* 200 Hz Bandwidth */
# define ADXL372_MEAS_BW400 0x1 /* 400 Hz Bandwidth */
# define ADXL372_MEAS_BW800 0x2 /* 800 Hz Bandwidth */
# define ADXL372_MEAS_BW1600 0x3 /* 1600 Hz Bandwidth */
# define ADXL372_MEAS_BW3200 0x4 /* 3200 Hz Bandwidth */
#define ADXL372_POWER_CTL (0x3f << 1)
# define ADXL372_POWER_LPF_DISABLE (1 << 3)
# define ADXL372_POWER_HPF_DISABLE (1 << 2)
# define ADXL372_POWER_MODE_STANDBY 0x0
# define ADXL372_POWER_MODE_WAKEUP 0x1
# define ADXL372_POWER_MODE_INSTON 0x2
# define ADXL372_POWER_MODE_MEASURE 0x3
#define ADXL372_RESET (0x41 << 1)
# define ADXL372_RESET_VALUE 0x52
#define ADXL372_FIFO_DATA (0x42 << 1)
#define ADXL372_LAST (0x42 << 1)
#define ADXL372_SCRATCH ADXL372_THRESH_ACT_X_H
/* SPI Bus Parameters */
#define ADXL372_SPI_FREQUENCY (10000000) /* 10 MHz */
#define ADXL372_SPI_MODE (SPIDEV_MODE0) /* SPI Mode 0: CPOL=0,CPHA=0 */
/****************************************************************************
* Public Types
****************************************************************************/
/* A reference to a structure of this type must be passed to the ADXL372
* driver. This structure provides information about the configuration
* of the sensor and provides some board-specific hooks.
*
* This sensor driver presents two interfaces, the POSIX character driver
* interface (fops) that is intended for use from a user application, and
* a set of direct call entry points that are intended to be used by
* a sensor cluster driver that is running as a kernel task (a driver to
* driver interface). Application tasks should not attempt to call sensor
* cluster driver entry points.
*
* 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 adxl372_reg_pair_s /* Utility struct for the below... */
{
uint8_t addr; /* SPI register address */
uint8_t value; /* Value to be stored in the above reg on open() */
};
struct adxl372_dvr_entry_vector_s
{
struct sensor_cluster_operations_s c;
/* Extend the sensor cluster driver interface with a SPI DMA exchange transfer.
* The standard driver_read and driver_write perform PIO transfers.
* The will loop waiting on the SPI hardware and are only appropriate for
* short data transfers.
* Note that the first byte in the tx buffer must be a command/address
* byte. The exchange function does not provide one. Also note that
* the first byte stored in the rxbuffer is a garbage byte, which
* is natural for a SPI exchange transfer. Plan your buffer accordingly.
*/
CODE void (*driver_spiexc)(FAR void *instance_handle,
FAR const void *txbuffer,
FAR void *rxbuffer, size_t nwords);
};
struct adxl372_config_s
{
/* Since multiple ADXL372 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 ADXL372 chip via their chip select inputs.
*/
int spi_devid;
/* Initial control register configuration values. */
uint16_t initial_cr_values_size; /* size of the below array.
* 0 = use default values. */
/* The initial value store operations will occur in the order they
* appear in the array.
*/
struct adxl372_reg_pair_s *initial_cr_values;
/* The below fields are intended for the sensor cluster driver interface
* and may be ignored when the sensor cluster driver is not being used.
* The leaf driver's registration function fills in the below fields.
*/
/* Leaf sensor handle for sensor cluster kernel driver */
FAR struct spi_dev_s *leaf_handle;
/* Pointer to the leaf driver's sensor_cluster_operations_s structure */
FAR const struct adxl372_dvr_entry_vector_s *sc_ops;
};
/****************************************************************************
* Public Function Prototypes
****************************************************************************/
#ifdef __cplusplus
#define EXTERN extern "C"
extern "C"
{
#else
#define EXTERN extern
#endif
/****************************************************************************
* Name: adxl372_register
*
* Description:
* Register the ADXL372 character device as 'devpath'
*
* Input Parameters:
* devpath - The full path to the driver to register. E.g., "/dev/acl0"
* spi - An instance of the SPI interface to use to communicate with
* ADXL372
* config - configuration for the ADXL372 accelerometer driver. For
* details see description above.
*
* Returned Value:
* Zero (OK) on success; a negated errno value on failure.
*
****************************************************************************/
int adxl372_register(FAR const char *devpath,
FAR struct spi_dev_s *spi,
FAR struct adxl372_config_s *config);
#undef EXTERN
#ifdef __cplusplus
}
#endif
#endif /* CONFIG_SPI && CONFIG_SENSORS_ADXL372 && CONFIG_SPI_EXCHANGE */
#endif /* __INCLUDE_NUTTX_SENSORS_ADXL372_H */

View File

@ -0,0 +1,192 @@
/****************************************************************************
* include/nuttx/sensors/cluster_driver.h
*
* Copyright (C) 2017 RAF Research LLC. All rights reserved.
* Author: Bob Feretich <bob.feretich@rafresearch.com>
*
* 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_CLUSTER_DRIVER_H
#define __INCLUDE_NUTTX_SENSORS_CLUSTER_DRIVER_H
/* Definitions for the cluster driver interface:
*
* The sensor cluster driver interface is created to permit high performance
* collection and processing of sensor data. Normally when high performance
* sensor data collection is required, data is needed from multiple sensors
* and the collection/processing task needs to run as a "higher than user
* priority" priority level. This mechanism is commonly supported by writing
* the collection/processing task as a kernel driver worker thread that can
* access multiple sensors.
*
* A clean way to implement this is to implement this collection/processing
* mechanism is as a driver that has efficient kernel-to-kernel access to
* other sensor leaf-drivers that support the cluster driver interface.
* (This documentation describes the "cluster driver interface" from the
* perspective of a "cluster driver interface" enabled leaf driver.)
*
* The file_operations interface typically deals with the caller's parameters
* and buffers being in user space, while the driver's code, variables, and
* buffers are in kernel space. There is also a layer of kernel code between
* the caller and driver that deals with security and assists helps with the
* driver's access of user space memory. So the file_operations interface
* is not appropriate for driver-to-driver communication.
*
* Since the driver registration function is not part of the file_operations
* interface and is permitted to be called from a kernel task, this function
* is reused. But rather than being called by the board initialization
* function, the cluster drivers registration function is called from the
* board initialization function; and the cluster drivers registration function
* calls the leaf driver's registration function.
*
* To be "cluster driver interface" enabled the leaf driver's registration
* function must communicate the leaf driver's instance back to the cluster
* driver's registration function. This is done by storing the leaf
* driver's instance handle into the caller provided config structure.
* This handle is provided as a parameter in cluster driver interface calls.
*
* dvr_open(): Reserve this sensor.
*
* dvr_close(): Places the sensor into low power standby mode, frees driver
* resources associated with the sensor, and release the reservation
* . of the sensor.
*
* To perform I/O to a sensor, the cluster driver needs...
* > A pointer to the spi instance (struct spi_dev_s *). The cluster driver
* has this pointer, because it provides it to the leaf driver as an input
* parameter to the leaf driver's registration function.
* > The chip select gpio pin identifier for the sensor device. The cluster
* driver knows this identifier,because it provides it to the leaf driver
* as an input parameter to the leaf driver's registration function.
* > A pointer to the leaf driver instance. The leaf driver communicates this
* pointer to the cluster driver by storing it into the config struct
* leaf_handle (struct spi_dev_s *) field that is also passed as an in/out
* parameter in the leaf driver's registration function.
*
* With the above information, the sensor cluster driver may efficiently access
* multiple sensors and aggregate their data.
*
****************************************************************************/
/****************************************************************************
* Public Types
****************************************************************************/
/* Cluster driver operations interface */
struct sensor_cluster_operations_s
{
CODE int (*driver_open)(FAR void *instance_handle, int32_t arg);
CODE int (*driver_close)(FAR void *instance_handle, int32_t arg);
CODE ssize_t (*driver_read)(FAR void *instance_handle, FAR char *buffer,
size_t buflen);
CODE ssize_t (*driver_write)(FAR void *instance_handle,
FAR const char *buffer, size_t buflen);
CODE off_t (*driver_seek)(FAR void *instance_handle, off_t offset,
int whence);
CODE int (*driver_ioctl)(FAR void *instance_handle, int cmd,
unsigned long arg);
CODE int (*driver_suspend)(FAR void *instance_handle, int32_t arg);
CODE int (*driver_resume)(FAR void *instance_handle, int32_t arg);
};
/**************************************************************************
* Public Function Prototypes
**************************************************************************/
#ifdef __cplusplus
#define EXTERN extern "C"
extern "C"
{
#else
#define EXTERN extern
#endif
/*****************************************************************************
* Name: xxxxxx_register <-- for a leaf driver.
*
* Description:
* Example of a driver register function for a "sensor leaf driver" that
* can be controlled by a "sensor cluster driver". The specific format of
* the registration function will vary based on the characteristics of the
* driver. The below is an example, which describes common aspects of the
* function and and its parameters.
*
* Normally, sensor driver register functions are called by the board
* initialization code. In the case of a "sensor cluster driver", the board
* initialization code calls the register function of the "sensor cluster
* driver"; and the "sensor cluster driver" calls the register function of
* each of the sensor (leaf) drivers that it will be controlling.
*
* Input Parameters:
* devpath - The full path to the leaf driver to register. E.g.,
* "/dev/acl0"
* spi - An instance of the SPI interface to use to communicate with the
* leaf driver. Or, it could be the I2C driver instance if the
* sensor is on an I2C bus.
* config - configuration struct for the sensor leaf driver. This struct is
* defined in the leaf driver's xxxxxx.h file.
*
* For a SPI sensor, this structure must contain:
*
* int spi_devid; The spi device id, which is used by NuttX to
* select/deselect the device. This is usually some
* type of reference to the chip_select gpio pin.
* FAR void *leaf_handle; The handle to the leaf driver instance.
* This is an opaque handle that provided by the
* leaf driver's register function.
* It is passed as a parameter to the leaf driver's
* driver_open() and driver_close() functions.
*
* For an I2C sensor, this struct must contain:
*
* int i2c_devid; The i2c device id, which is used by NuttX to
* address the sensor device on an I2C bus..
* FAR void *leaf_handle; The handle to the leaf driver instance.
* This is an opaque handle that provided by the
* leaf driver's register function.
* It is passed as a parameter to the leaf driver's
* driver_open() and driver_close() functions.
*
* Returned Value:
* Zero (OK) on success; a negated errno value on failure.
*
* int xxxxxx_register(FAR const char *devpath,
* FAR struct spi_dev_s *spi,
* FAR struct xxxxxx_config_s *config);
*
****************************************************************************/
#undef EXTERN
#ifdef __cplusplus
}
#endif
#endif /* __INCLUDE_NUTTX_SENSORS_CLUSTER_DRIVER_H */

View File

@ -0,0 +1,355 @@
/*****************************************************************************
* include/nuttx/sensors/lsm330.h
*
* Copyright (C) 2017-2018 RAF Research LLC. All rights reserved.
* Author: Bob Feretich <bob.feretich@rafresearch.com>
*
* 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_LSM330_H
#define __INCLUDE_NUTTX_SENSORS_LSM330_H
/*****************************************************************************
* Driver usage notes:
*
* This driver is a "kernel sensor leaf driver" that may be used directly
* from user applications via the file_operations interface or have selected
* entry points called directly from a "kernel sensor cluster driver".
*
* To use this driver via the file_operations interface, the board
* initialization function should call this driver's registration function.
* The driver will register itself with Nuttx under the /dev path that is
* provided by the config structure. Then user applications may access the
* driver via the "file descriptor handle" returned by the file_operations
* open() function.
*
* By default the accelerometer's open() function configures the sensor
* for:
*
* Output Data Rate (ODR) = 1600 Hz.
* Bandwidth (BW) = 800 Hz.
* Range = 16g.
*
* By default the gyroscope's open() function configures the sensor for:
*
* Output Data Rate (ODR) = 760 Hz.
* Bandwidth (BW) = 100 Hz.
* Range = 500 dps.
* Low Pass Filter #1 selected.
*
* If the user desires a different configuration settings, the the user may
* either provide a pointer to an array of "struct lsm330_reg_pair_s" that
* will be applied to to the sensor upon open(); or dynamically use the
* lseek() and write() file_operations functions to set the sensor
* configuration as desired.
*
* When using the sensor from the file_operations interface, the sensor is
* accessed in Programmed I/O (PIO) mode. (i.e. When the read() function is
* executed, the sensor is read on that thread.) PIO reads and writes block
* the calling thread until data is available. Since the sensor is on an SPI
* bus running at near 10 MHz, the read or write operations should only take
* a few microseconds (about a microsecond per byte of data), so for
* individual sensor reads and writes, the overhead of using interrupts or
* DMA is not worthwhile.
*
* Use the Cluster Driver Interface to perform DMA block transfers.
*
* This driver supports the Common Sensor Register Interface.
* See drivers/sensors/README.txt for details.
*
* This driver supports the Sensor Cluster Driver Interface.
* See drivers/sensors/README.txt for details.
*
* It also extends the interface by permitting cluster driver calls to
* a function that is intended to perform high performance DMA SPI exchange
* operations. See the usage note on the exchange operation below.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/irq.h>
#include <nuttx/config.h>
#include <nuttx/fs/ioctl.h>
#include <nuttx/spi/spi.h>
#include <nuttx/sensors/cluster_driver.h>
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/* LSM330 common definitions */
#define LSM330_READ 0x80
#define LSM330_WRITE 0x00
#define LSM330_GYRO_AUTO 0x40
/****************************************************************************
* LSM330 Accelerometer Register definitions
****************************************************************************/
#define LSM330_ACL_IDREG 0x0f
#define LSM330_ACL_IDREG_VALUE 0x40
#define LSM330_ACL_CTRL_REG2 0x21
#define LSM330_ACR2_HYST1_MASK 0xe0 /* Hysteresis for SM1 bit mask */
#define LSM330_ACR2_HYST1_SHIFT 5 /* Hysteresis for SM1 shift amount */
#define LSM330_ACR2_SM1_PIN 0x08 /* 1= SM1 int on INT2_A; 0= on INT1_A */
#define LSM330_ACR2_SM1_EN 0x01 /* 1= SM1 enabled; 0= disabled */
#define LSM330_ACL_CTRL_REG3 0x22
#define LSM330_ACR3_HYST2_MASK 0xe0 /* Hysteresis for SM2 bit mask */
#define LSM330_ACR3_HYST2_SHIFT 5 /* Hysteresis for SM2 shift amount */
#define LSM330_ACR3_SM2_PIN 0x08 /* 1= SM2 int on INT1_A; 0= on INT1_A */
#define LSM330_ACR3_SM2_EN 0x01 /* 1= SM2 enabled; 0= disabled */
#define LSM330_ACL_CTRL_REG4 0x23
#define LSM330_ACR4_DREN 0x80 /* 1= Data Ready enabled on INT1_A; 0= disabled */
#define LSM330_ACR4_IEA 0x40 /* 1= INT1 polarity active high; 0= low */
#define LSM330_ACR4_IEL 0x20 /* 1= INT1 pulsed; 0= latched */
#define LSM330_ACR4_INT2_EN 0x10 /* 1= INT2_A enabled; 0= disabled */
#define LSM330_ACR4_INT1_EN 0x08 /* 1= INT1_A enabled; 0= disabled */
#define LSM330_ACR4_VFILT 0x40 /* 1= Vector filter enabled; 0= disabled */
#define LSM330_ACR4_STRT 0x01 /* 1= Soft reset. Cleared by HW when done */
#define LSM330_ACL_CTRL_REG5 0x20
#define LSM330_ACR5_ODR_MASK 0xf0 /* ODR bit mask */
#define LSM330_ACR5_ODR_OFF 0x00 /* ODR = powered down */
#define LSM330_ACR5_ODR_3 0x10 /* ODR = 3.125 Hz */
#define LSM330_ACR5_ODR_6 0x20 /* ODR = 6.25 Hz */
#define LSM330_ACR5_ODR_12 0x30 /* ODR = 12.5 Hz */
#define LSM330_ACR5_ODR_25 0x40 /* ODR = 25 Hz */
#define LSM330_ACR5_ODR_50 0x50 /* ODR = 50 Hz */
#define LSM330_ACR5_ODR_100 0x60 /* ODR = 100 Hz */
#define LSM330_ACR5_ODR_400 0x70 /* ODR = 400 Hz */
#define LSM330_ACR5_ODR_800 0x80 /* ODR = 800 Hz */
#define LSM330_ACR5_ODR_1600 0x90 /* ODR = 1600 Hz */
#define LSM330_ACR5_BDU 0x08 /* 1= MSB not updated until LSB is read */
#define LSM330_ACR5_ZEN 0x04 /* 1= Z-axis sensor enabled */
#define LSM330_ACR5_YEN 0x02 /* 1= Y-axis sensor enabled */
#define LSM330_ACR5_XEN 0x01 /* 1= X-axis sensor enabled */
#define LSM330_ACL_CTRL_REG6 0x24
#define LSM330_ACR6_BW_MASK 0xc0 /* Bandwidth bit mask */
#define LSM330_ACR6_BW_800 0x00 /* Bandwidth = 800 Hz */
#define LSM330_ACR6_BW_200 0x40 /* Bandwidth = 200 Hz */
#define LSM330_ACR6_BW_400 0x80 /* Bandwidth = 400 Hz */
#define LSM330_ACR6_BW_50 0xc0 /* Bandwidth = 50 Hz */
#define LSM330_ACR6_FS_MASK 0x38 /* Full Scale bit mask */
#define LSM330_ACR6_FS_2 0x00 /* FScale = 2g */
#define LSM330_ACR6_FS_4 0x08 /* FScale = 4g */
#define LSM330_ACR6_FS_6 0x10 /* FScale = 6g */
#define LSM330_ACR6_FS_8 0x18 /* FScale = 8g */
#define LSM330_ACR6_FS_16 0x20 /* FScale = 16g */
#define LSM330_ACR6_SIM 0x01 /* 1= 3-wire SPI; 0= 4-wire SPI */
#define LSM330_ACL_CTRL_REG7 0x25
#define LSM330_ACR7_BOOT 0x80 /* Force reboot, cleared by HW when done */
#define LSM330_ACR7_FIFO_EN 0x40 /* 1= FIFO enabled; 0= disabled */
#define LSM330_ACR7_WTM_EN 0x20 /* 1= FIFO watermark enabled; 0= disabled */
#define LSM330_ACR7_ADD_INC 0x10 /* 1= auto post-increment ACL addresses */
#define LSM330_ACR7_P1_EMPTY 0x08 /* 1= enable FIFO empty on INT1_A */
#define LSM330_ACR7_P1_WTM 0x04 /* 1= enable FIFO watermark on INT1_A */
#define LSM330_ACR7_P1_OVERRUN 0x02 /* 1= enable FIFO overrun on INT1_A */
#define LSM330_ACR7_P2_BOOT 0x01 /* 1= enable BOOT interrupt on INT2_A */
#define LSM330_ACL_STATUS 0x27
#define LSM330_ASR_ZYXOR 0x80 /* 1= At least one of the axes has overrun */
#define LSM330_ASR_ZOR 0x40 /* 1= Z-axis has overrun and lost data */
#define LSM330_ASR_YOR 0x20 /* 1= Y-axis has overrun and lost data */
#define LSM330_ASR_XOR 0x10 /* 1= X-axis has overrun and lost data */
#define LSM330_ASR_ZYXDA 0x08 /* 1= All of the axes have new data */
#define LSM330_ASR_ZDA 0x04 /* 1= New Z-axis data is available */
#define LSM330_ASR_YDA 0x02 /* 1= New Y-axis data is available */
#define LSM330_ASR_XDA 0x01 /* 1= New X-axis data is available */
#define LSM330_ACL_OUT_X_L 0x28
#define LSM330_ACL_OUT_X_H 0x29
#define LSM330_ACL_OUT_Y_L 0x2a
#define LSM330_ACL_OUT_Y_H 0x2b
#define LSM330_ACL_OUT_Z_L 0x2c
#define LSM330_ACL_OUT_Z_H 0x2d
#define LSM330_ACL_THRS1_1 0x57
#define LSM330_ACL_LAST 0x7f
#define LSM330_ACL_SCRATCH LSM330_ACL_THRS1_1
/* LSM330 Gyroscope Register definitions */
#define LSM330_GYRO_IDREG 0x0f
# define LSM330_GYRO_IDREG_VALUE 0xd4
#define LSM330_GYRO_CTRL_REG1 0x20
#define LSM330_GYRO_CTRL_REG2 0x21
#define LSM330_GYRO_CTRL_REG3 0x22
#define LSM330_GYRO_CTRL_REG4 0x23
#define LSM330_GYRO_CTRL_REG5 0x24
# define LSM_GYRO_BOOT_MASK 0x80
#define LSM330_GYRO_OUT_TEMP 0x26
#define LSM330_GYRO_STATUS_REG 0x27
#define LSM330_GYRO_OUT_X_L 0x28
#define LSM330_GYRO_OUT_X_H 0x29
#define LSM330_GYRO_OUT_Y_L 0x2a
#define LSM330_GYRO_OUT_Y_H 0x2b
#define LSM330_GYRO_OUT_Z_L 0x2c
#define LSM330_GYRO_OUT_Z_H 0x2d
#define LSM330_GYRO_INT1_THS_ZL 0x37
#define LSM330_GYRO_LAST 0x3f
#define LSM330_GYRO_SCRATCH LSM330_GYRO_INT1_THS_ZL
/* SPI Bus Parameters */
#define LSM330_SPI_FREQUENCY (10000000) /* 10 MHz */
#define LSM330_SPI_MODE (SPIDEV_MODE3) /* SPI Mode 3: CPOL=1,CPHA=1 */
/****************************************************************************
* Public Types
****************************************************************************/
/* A reference to a structure of this type must be passed to the LSM330
* driver. This structure provides information about the configuration
* of the sensor and provides some board-specific hooks.
*
* This sensor driver presents two interfaces, the POSIX character driver
* interface (fops) that is intended for use from a user application, and
* a set of direct call entry points that are intended to be used by
* a sensor cluster driver that is running as a kernel task (a driver to
* driver interface). Application tasks should not attempt to call sensor
* cluster driver entry points.
*
* 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 lsm330_reg_pair_s /* Utility struct for the below... */
{
uint8_t addr; /* SPI register address */
uint8_t value; /* Value to be stored in the above reg on open() */
};
struct lsm330spi_dvr_entry_vector_s
{
struct sensor_cluster_operations_s c;
/* Extend the sensor cluster driver interface with a SPI DMA exchange
* transfer. The standard driver_read and driver_write perform PIO
* transfers. The will loop waiting on the SPI hardware and are only
* appropriate for short data transfers.
*
* Note that the first byte in the tx buffer must be a command/address
* byte. The exchange function does not provide one. Also note that
* the first byte stored in the rxbuffer is a garbage byte, which
* is natural for a SPI exchange transfer. Plan your buffer accordingly.
*/
CODE void (*driver_spiexc)(FAR void *instance_handle,
FAR const void *txbuffer,
FAR void *rxbuffer, size_t nwords);
};
struct lsm330_config_s
{
/* Since multiple LSM330 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 LSM330 chip via their chip select inputs.
*/
int spi_devid;
/* Initial control register configuration values. */
uint16_t initial_cr_values_size; /* size of the below array.
* 0 = use default values. */
/* The initial value store operations will occur in the order they appear
* in the array.
*/
struct lsm330_reg_pair_s *initial_cr_values;
/* The below fields are intended for the sensor cluster driver interface
* and may be ignored when the sensor cluster driver is not being used.
* The leaf driver's registration function fills in the below fields.
*/
/* Leaf sensor handle (opaque) for sensor cluster kernel driver */
FAR void *leaf_handle;
/* Pointer to the leaf driver's sensor_cluster_operations_s structure */
FAR const struct lsm330spi_dvr_entry_vector_s *sc_ops;
};
/****************************************************************************
* Public Function Prototypes
****************************************************************************/
#ifdef __cplusplus
#define EXTERN extern "C"
extern "C"
{
#else
#define EXTERN extern
#endif
/*******************************************************************************
* 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.
*
* 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);
#undef EXTERN
#ifdef __cplusplus
}
#endif
#endif /* __INCLUDE_NUTTX_SENSORS_LSM330_H */